Skip to content

Commit b1546ac

Browse files
committed
Add kernel driver for the power board
1 parent 86cd053 commit b1546ac

File tree

3 files changed

+344
-0
lines changed

3 files changed

+344
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
*.ko
2+
*.o
3+
*.cmd
4+
*.mod
5+
*.mod.c
6+
/modules.order
7+
/Module.symvers
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
obj-m += power_board.o
2+
3+
all:
4+
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
5+
6+
clean:
7+
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Lines changed: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
#include<linux/module.h>
2+
#include<linux/init.h>
3+
#include<linux/usb.h>
4+
#include<linux/hwmon.h>
5+
#include<linux/errno.h>
6+
7+
#define FOSDEM_VENDOR_ID 0xF05D
8+
#define FOSDEM_PRODUCT_KRAB 0x4011
9+
10+
static struct usb_device_id power_board_table [] = {
11+
{ USB_DEVICE(FOSDEM_VENDOR_ID, FOSDEM_PRODUCT_KRAB) }, /* Krab Controller */
12+
{ } /* Terminating entry */
13+
};
14+
15+
MODULE_DEVICE_TABLE(usb, power_board_table);
16+
17+
#define POWER_BOARD_MINOR_BASE 0
18+
19+
#define VENDOR_REQUEST_TEMPERATURE 0x10
20+
21+
struct power_board {
22+
struct usb_device * udev;
23+
__u8 bulk_in_addr;
24+
__u8 bulk_out_addr;
25+
26+
char *read_buffer;
27+
size_t read_buffer_len;
28+
char *write_buffer;
29+
size_t write_buffer_len;
30+
31+
int protocol;
32+
int present;
33+
struct kref kref;
34+
};
35+
#define to_pb(d) container_of(d, struct power_board, kref)
36+
37+
static struct usb_driver power_board_driver;
38+
39+
static int vendor_command(struct usb_device *dev, unsigned char request, unsigned char value, unsigned char index, void *buf, int size)
40+
{
41+
return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
42+
request,
43+
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
44+
value,
45+
index, buf, size,
46+
USB_CTRL_GET_TIMEOUT);
47+
}
48+
49+
static void power_board_delete(struct kref *kref)
50+
{
51+
printk("PB DELETE!\n");
52+
struct power_board *dev = to_pb(kref);
53+
usb_put_dev(dev->udev);
54+
kfree(dev);
55+
}
56+
57+
static int power_board_open(struct inode *inode, struct file *file)
58+
{
59+
struct power_board *dev;
60+
struct usb_interface *interface;
61+
int result;
62+
63+
interface = usb_find_interface(&power_board_driver, iminor(inode));
64+
if (!interface)
65+
return -ENODEV;
66+
67+
dev = usb_get_intfdata(interface);
68+
if (!dev)
69+
return -ENODEV;
70+
71+
kref_get(&dev->kref);
72+
file->private_data = dev;
73+
74+
return result;
75+
}
76+
77+
static int power_board_release(struct inode *inode, struct file *file)
78+
{
79+
struct power_board *dev;
80+
dev = file->private_data;
81+
if (dev == NULL)
82+
return -ENODEV;
83+
84+
kref_put(&dev->kref, power_board_delete);
85+
return 0;
86+
}
87+
88+
static ssize_t power_board_read(struct file *file, char *buffer, size_t count,
89+
loff_t *ppos)
90+
{
91+
struct power_board *dev = file->private_data;
92+
int result;
93+
94+
if(!dev->present)
95+
return -ENODEV;
96+
97+
//result = simple_read_from_buffer(buffer, count, ppos,
98+
result = 0;
99+
return result;
100+
}
101+
102+
static ssize_t power_board_write(struct file *file, const char *user_buffer,
103+
size_t count, loff_t *ppos)
104+
{
105+
struct power_board *dev = file->private_data;
106+
int result;
107+
108+
char *buf = NULL;
109+
size_t writesize = min_t(size_t, count, PAGE_SIZE - 512);
110+
111+
// Zero byte write is a noop
112+
if (count == 0)
113+
return 0;
114+
115+
//result = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_addr), dev->bu);
116+
117+
return 0;
118+
}
119+
120+
static const struct file_operations power_board_fops = {
121+
.owner = THIS_MODULE,
122+
.read = power_board_read,
123+
.write = power_board_write,
124+
.open = power_board_open,
125+
.release = power_board_release,
126+
};
127+
128+
/*********************************
129+
* Board API
130+
********************************/
131+
132+
long power_board_get_temperature(struct power_board *pb) {
133+
int retval;
134+
int temp;
135+
uint8_t *buffer = kmalloc(2, GFP_KERNEL);
136+
if (!buffer)
137+
return 0;
138+
139+
retval = vendor_command(pb->udev, VENDOR_REQUEST_TEMPERATURE, 0, 0, buffer, 2);
140+
if (retval)
141+
dev_info(&pb->udev->dev, "retval = %d\n", retval);
142+
143+
temp = buffer[0] << 8 | buffer[1];
144+
long celcius = ((temp >> (16-12)) * 1000) >> (12-8);
145+
kfree(buffer);
146+
return celcius;
147+
}
148+
149+
/*********************************
150+
* HWMON Integration
151+
********************************/
152+
153+
static int power_board_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
154+
u32 attr, int channel, long *val)
155+
{
156+
struct power_board *data = dev_get_drvdata(dev);
157+
158+
switch(type) {
159+
case hwmon_temp:
160+
switch(attr) {
161+
case hwmon_temp_input:
162+
*val = power_board_get_temperature(data);
163+
return 0;
164+
}
165+
}
166+
return -EINVAL;
167+
}
168+
169+
static int power_board_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
170+
u32 attr, int channel, long val)
171+
{
172+
return 0;
173+
}
174+
175+
static umode_t power_board_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
176+
u32 attr, int channel)
177+
{
178+
const struct power_board *config_data = data;
179+
180+
switch (type) {
181+
case hwmon_temp:
182+
switch (attr) {
183+
case hwmon_temp_input:
184+
return 0444;
185+
}
186+
}
187+
return 0;
188+
}
189+
190+
static const struct hwmon_channel_info * const power_board_hwmon_info[] = {
191+
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
192+
NULL
193+
};
194+
195+
static const struct hwmon_ops power_board_hwmon_ops = {
196+
.is_visible = power_board_hwmon_is_visible,
197+
.read = power_board_hwmon_read,
198+
.write = power_board_hwmon_write,
199+
};
200+
201+
static const struct hwmon_chip_info power_board_chip_info = {
202+
.ops = &power_board_hwmon_ops,
203+
.info = power_board_hwmon_info,
204+
};
205+
206+
/*********************************
207+
* SYSFS Integration
208+
********************************/
209+
210+
static ssize_t temperature_show(struct device *dev, struct device_attribute *attr, char *buf)
211+
{
212+
struct usb_interface *intf = to_usb_interface(dev);
213+
struct power_board *pb = usb_get_intfdata(intf);
214+
215+
long celcius = power_board_get_temperature(pb);
216+
return sprintf(buf, "%ld", celcius);
217+
}
218+
static DEVICE_ATTR_RO(temperature);
219+
220+
static struct attribute *power_board_attrs[] = {
221+
&dev_attr_temperature.attr,
222+
NULL,
223+
};
224+
ATTRIBUTE_GROUPS(power_board);
225+
226+
static struct usb_class_driver power_board_class = {
227+
.name = "powerboard%d",
228+
.fops = &power_board_fops,
229+
.minor_base = POWER_BOARD_MINOR_BASE,
230+
};
231+
232+
static int power_board_probe(struct usb_interface *interface,
233+
const struct usb_device_id *id)
234+
{
235+
int result;
236+
int protocol;
237+
struct power_board *dev;
238+
struct device *parent;
239+
struct usb_host_interface *iface_desc;
240+
struct usb_endpoint_descriptor *endpoint_in;
241+
struct usb_endpoint_descriptor *endpoint_out;
242+
int in_end_size;
243+
int out_end_size;
244+
245+
parent = &interface->dev;
246+
iface_desc = interface->cur_altsetting;
247+
248+
if (iface_desc->desc.bInterfaceClass != 0xFF)
249+
return -ENODEV;
250+
251+
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
252+
if(!dev)
253+
return -ENOMEM;
254+
255+
kref_init(&dev->kref);
256+
257+
dev->udev = usb_get_dev(interface_to_usbdev(interface));
258+
259+
// Get the bulk-in and bulk-out endpoint
260+
result = usb_find_common_endpoints(iface_desc, &endpoint_in, &endpoint_out, NULL, NULL);
261+
if (result) {
262+
dev_err(&interface->dev, "Unable to find the bulk endpoints\n");
263+
goto error;
264+
}
265+
dev->bulk_in_addr = endpoint_in->bEndpointAddress;
266+
dev->bulk_out_addr = endpoint_out->bEndpointAddress;
267+
dev->protocol = protocol;
268+
dev->present = 1;
269+
270+
dev->read_buffer_len = usb_endpoint_maxp(endpoint_in) * 10;
271+
dev->write_buffer_len = usb_endpoint_maxp(endpoint_in) * 10;
272+
dev->read_buffer = kmalloc(dev->read_buffer_len, GFP_KERNEL);
273+
if (!dev->read_buffer) {
274+
result = -ENOMEM;
275+
goto error;
276+
}
277+
dev->write_buffer = kmalloc(dev->write_buffer_len, GFP_KERNEL);
278+
if (!dev->write_buffer) {
279+
result = -ENOMEM;
280+
goto error;
281+
}
282+
283+
usb_set_intfdata(interface, dev);
284+
285+
result = usb_register_dev(interface, &power_board_class);
286+
if (result) {
287+
dev_err(&interface->dev, "Unable to allocate minor number.\n");
288+
usb_set_intfdata(interface, NULL);
289+
goto error;
290+
}
291+
292+
// Register with the HWMON subsystem
293+
devm_hwmon_device_register_with_info(parent, "PowerBoard", dev, &power_board_chip_info, NULL);
294+
295+
dev_info(&interface->dev, "Loaded FOSDEM power board driver\n");
296+
return 0;
297+
298+
error:
299+
kref_put(&dev->kref, power_board_delete);
300+
return result;
301+
302+
}
303+
304+
static void power_board_disconnect(struct usb_interface *interface)
305+
{
306+
struct power_board *dev;
307+
308+
dev = usb_get_intfdata(interface);
309+
dev->present = 0;
310+
usb_deregister_dev(interface, &power_board_class);
311+
312+
usb_set_intfdata(interface, NULL);
313+
usb_put_dev(dev->udev);
314+
kref_put(&dev->kref, power_board_delete);
315+
}
316+
317+
static struct usb_driver power_board_driver = {
318+
.name = "power_board",
319+
.probe = power_board_probe,
320+
.disconnect = power_board_disconnect,
321+
.id_table = power_board_table,
322+
.dev_groups = power_board_groups,
323+
};
324+
325+
module_usb_driver(power_board_driver);
326+
327+
MODULE_LICENSE("GPL v2");
328+
MODULE_AUTHOR("Martijn Braam");
329+
MODULE_DESCRIPTION("Kernel driver for the FOSDEM power board");
330+
MODULE_VERSION("0.1");

0 commit comments

Comments
 (0)