Commit | Line | Data |
---|---|---|
eedaf1ae JW |
1 | /* |
2 | * lirc_igorplugusb - USB remote support for LIRC | |
3 | * | |
4 | * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware. | |
5 | * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm | |
6 | * | |
7 | * The device can only record bursts of up to 36 pulses/spaces. | |
8 | * Works fine with RC5. Longer commands lead to device buffer overrun. | |
9 | * (Maybe a better firmware or a microcontroller with more ram can help?) | |
10 | * | |
11 | * Version 0.1 [beta status] | |
12 | * | |
13 | * Copyright (C) 2004 Jan M. Hochstein | |
14 | * <hochstein@algo.informatik.tu-darmstadt.de> | |
15 | * | |
16 | * This driver was derived from: | |
17 | * Paul Miller <pmiller9@users.sourceforge.net> | |
18 | * "lirc_atiusb" module | |
19 | * Vladimir Dergachev <volodya@minspring.com>'s 2002 | |
20 | * "USB ATI Remote support" (input device) | |
21 | * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002 | |
22 | * "USB StreamZap remote driver" (LIRC) | |
23 | * Artur Lipowski <alipowski@kki.net.pl>'s 2002 | |
24 | * "lirc_dev" and "lirc_gpio" LIRC modules | |
25 | */ | |
26 | ||
27 | /* | |
28 | * This program is free software; you can redistribute it and/or modify | |
29 | * it under the terms of the GNU General Public License as published by | |
30 | * the Free Software Foundation; either version 2 of the License, or | |
31 | * (at your option) any later version. | |
32 | * | |
33 | * This program is distributed in the hope that it will be useful, | |
34 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
35 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
36 | * GNU General Public License for more details. | |
37 | * | |
38 | * You should have received a copy of the GNU General Public License | |
39 | * along with this program; if not, write to the Free Software | |
40 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
41 | */ | |
42 | ||
43 | #include <linux/module.h> | |
44 | #include <linux/kernel.h> | |
45 | #include <linux/kmod.h> | |
46 | #include <linux/sched.h> | |
47 | #include <linux/errno.h> | |
48 | #include <linux/fs.h> | |
49 | #include <linux/usb.h> | |
50 | #include <linux/time.h> | |
51 | ||
52 | #include <media/lirc.h> | |
53 | #include <media/lirc_dev.h> | |
54 | ||
55 | ||
56 | /* module identification */ | |
d653c0af | 57 | #define DRIVER_VERSION "0.2" |
eedaf1ae JW |
58 | #define DRIVER_AUTHOR \ |
59 | "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>" | |
917167e9 | 60 | #define DRIVER_DESC "Igorplug USB remote driver for LIRC" |
eedaf1ae JW |
61 | #define DRIVER_NAME "lirc_igorplugusb" |
62 | ||
63 | /* debugging support */ | |
64 | #ifdef CONFIG_USB_DEBUG | |
65 | static int debug = 1; | |
66 | #else | |
67 | static int debug; | |
68 | #endif | |
69 | ||
70 | #define dprintk(fmt, args...) \ | |
71 | do { \ | |
72 | if (debug) \ | |
73 | printk(KERN_DEBUG fmt, ## args); \ | |
74 | } while (0) | |
75 | ||
76 | /* One mode2 pulse/space has 4 bytes. */ | |
77 | #define CODE_LENGTH sizeof(int) | |
78 | ||
79 | /* Igor's firmware cannot record bursts longer than 36. */ | |
80 | #define DEVICE_BUFLEN 36 | |
81 | ||
82 | /* | |
83 | * Header at the beginning of the device's buffer: | |
84 | * unsigned char data_length | |
85 | * unsigned char data_start (!=0 means ring-buffer overrun) | |
86 | * unsigned char counter (incremented by each burst) | |
87 | */ | |
88 | #define DEVICE_HEADERLEN 3 | |
89 | ||
90 | /* This is for the gap */ | |
91 | #define ADDITIONAL_LIRC_BYTES 2 | |
92 | ||
93 | /* times to poll per second */ | |
94 | #define SAMPLE_RATE 100 | |
95 | static int sample_rate = SAMPLE_RATE; | |
96 | ||
97 | ||
98 | /**** Igor's USB Request Codes */ | |
99 | ||
100 | #define SET_INFRABUFFER_EMPTY 1 | |
101 | /** | |
102 | * Params: none | |
103 | * Answer: empty | |
104 | */ | |
105 | ||
106 | #define GET_INFRACODE 2 | |
107 | /** | |
108 | * Params: | |
109 | * wValue: offset to begin reading infra buffer | |
110 | * | |
111 | * Answer: infra data | |
112 | */ | |
113 | ||
114 | #define SET_DATAPORT_DIRECTION 3 | |
115 | /** | |
116 | * Params: | |
117 | * wValue: (byte) 1 bit for each data port pin (0=in, 1=out) | |
118 | * | |
119 | * Answer: empty | |
120 | */ | |
121 | ||
122 | #define GET_DATAPORT_DIRECTION 4 | |
123 | /** | |
124 | * Params: none | |
125 | * | |
126 | * Answer: (byte) 1 bit for each data port pin (0=in, 1=out) | |
127 | */ | |
128 | ||
129 | #define SET_OUT_DATAPORT 5 | |
130 | /** | |
131 | * Params: | |
132 | * wValue: byte to write to output data port | |
133 | * | |
134 | * Answer: empty | |
135 | */ | |
136 | ||
137 | #define GET_OUT_DATAPORT 6 | |
138 | /** | |
139 | * Params: none | |
140 | * | |
141 | * Answer: least significant 3 bits read from output data port | |
142 | */ | |
143 | ||
144 | #define GET_IN_DATAPORT 7 | |
145 | /** | |
146 | * Params: none | |
147 | * | |
148 | * Answer: least significant 3 bits read from input data port | |
149 | */ | |
150 | ||
151 | #define READ_EEPROM 8 | |
152 | /** | |
153 | * Params: | |
154 | * wValue: offset to begin reading EEPROM | |
155 | * | |
156 | * Answer: EEPROM bytes | |
157 | */ | |
158 | ||
159 | #define WRITE_EEPROM 9 | |
160 | /** | |
161 | * Params: | |
162 | * wValue: offset to EEPROM byte | |
163 | * wIndex: byte to write | |
164 | * | |
165 | * Answer: empty | |
166 | */ | |
167 | ||
168 | #define SEND_RS232 10 | |
169 | /** | |
170 | * Params: | |
171 | * wValue: byte to send | |
172 | * | |
173 | * Answer: empty | |
174 | */ | |
175 | ||
176 | #define RECV_RS232 11 | |
177 | /** | |
178 | * Params: none | |
179 | * | |
180 | * Answer: byte received | |
181 | */ | |
182 | ||
183 | #define SET_RS232_BAUD 12 | |
184 | /** | |
185 | * Params: | |
186 | * wValue: byte to write to UART bit rate register (UBRR) | |
187 | * | |
188 | * Answer: empty | |
189 | */ | |
190 | ||
191 | #define GET_RS232_BAUD 13 | |
192 | /** | |
193 | * Params: none | |
194 | * | |
195 | * Answer: byte read from UART bit rate register (UBRR) | |
196 | */ | |
197 | ||
198 | ||
199 | /* data structure for each usb remote */ | |
200 | struct igorplug { | |
201 | ||
202 | /* usb */ | |
203 | struct usb_device *usbdev; | |
eedaf1ae JW |
204 | int devnum; |
205 | ||
206 | unsigned char *buf_in; | |
207 | unsigned int len_in; | |
208 | int in_space; | |
209 | struct timeval last_time; | |
210 | ||
211 | dma_addr_t dma_in; | |
212 | ||
213 | /* lirc */ | |
214 | struct lirc_driver *d; | |
215 | ||
216 | /* handle sending (init strings) */ | |
217 | int send_flags; | |
eedaf1ae JW |
218 | }; |
219 | ||
220 | static int unregister_from_lirc(struct igorplug *ir) | |
221 | { | |
917167e9 | 222 | struct lirc_driver *d; |
eedaf1ae JW |
223 | int devnum; |
224 | ||
917167e9 JW |
225 | if (!ir) { |
226 | printk(KERN_ERR "%s: called with NULL device struct!\n", | |
227 | __func__); | |
eedaf1ae | 228 | return -EINVAL; |
917167e9 | 229 | } |
eedaf1ae JW |
230 | |
231 | devnum = ir->devnum; | |
917167e9 | 232 | d = ir->d; |
eedaf1ae | 233 | |
917167e9 JW |
234 | if (!d) { |
235 | printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", | |
236 | __func__); | |
237 | return -EINVAL; | |
238 | } | |
eedaf1ae | 239 | |
917167e9 JW |
240 | dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum); |
241 | lirc_unregister_driver(d->minor); | |
eedaf1ae JW |
242 | |
243 | kfree(d); | |
244 | ir->d = NULL; | |
245 | kfree(ir); | |
917167e9 JW |
246 | |
247 | return devnum; | |
eedaf1ae JW |
248 | } |
249 | ||
250 | static int set_use_inc(void *data) | |
251 | { | |
252 | struct igorplug *ir = data; | |
253 | ||
254 | if (!ir) { | |
255 | printk(DRIVER_NAME "[?]: set_use_inc called with no context\n"); | |
256 | return -EIO; | |
257 | } | |
917167e9 | 258 | |
eedaf1ae JW |
259 | dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum); |
260 | ||
261 | if (!ir->usbdev) | |
262 | return -ENODEV; | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
267 | static void set_use_dec(void *data) | |
268 | { | |
269 | struct igorplug *ir = data; | |
270 | ||
271 | if (!ir) { | |
272 | printk(DRIVER_NAME "[?]: set_use_dec called with no context\n"); | |
273 | return; | |
274 | } | |
917167e9 | 275 | |
eedaf1ae JW |
276 | dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum); |
277 | } | |
278 | ||
d653c0af JW |
279 | static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf, |
280 | int i, int max) | |
281 | { | |
282 | int code; | |
283 | ||
284 | /* MODE2: pulse/space (PULSE_BIT) in 1us units */ | |
285 | while (i < max) { | |
286 | /* 1 Igor-tick = 85.333333 us */ | |
287 | code = (unsigned int)ir->buf_in[i] * 85 + | |
288 | (unsigned int)ir->buf_in[i] / 3; | |
289 | ir->last_time.tv_usec += code; | |
290 | if (ir->in_space) | |
291 | code |= PULSE_BIT; | |
292 | lirc_buffer_write(buf, (unsigned char *)&code); | |
293 | /* 1 chunk = CODE_LENGTH bytes */ | |
294 | ir->in_space ^= 1; | |
295 | ++i; | |
296 | } | |
297 | } | |
eedaf1ae JW |
298 | |
299 | /** | |
300 | * Called in user context. | |
301 | * return 0 if data was added to the buffer and | |
302 | * -ENODATA if none was available. This should add some number of bits | |
303 | * evenly divisible by code_length to the buffer | |
304 | */ | |
917167e9 | 305 | static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) |
eedaf1ae JW |
306 | { |
307 | int ret; | |
308 | struct igorplug *ir = (struct igorplug *)data; | |
309 | ||
917167e9 | 310 | if (!ir || !ir->usbdev) /* Has the device been removed? */ |
eedaf1ae JW |
311 | return -ENODEV; |
312 | ||
313 | memset(ir->buf_in, 0, ir->len_in); | |
314 | ||
917167e9 JW |
315 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), |
316 | GET_INFRACODE, USB_TYPE_VENDOR | USB_DIR_IN, | |
317 | 0/* offset */, /*unused*/0, | |
318 | ir->buf_in, ir->len_in, | |
319 | /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); | |
eedaf1ae | 320 | if (ret > 0) { |
eedaf1ae JW |
321 | int code, timediff; |
322 | struct timeval now; | |
323 | ||
d653c0af JW |
324 | /* ACK packet has 1 byte --> ignore */ |
325 | if (ret < DEVICE_HEADERLEN) | |
eedaf1ae JW |
326 | return -ENODATA; |
327 | ||
328 | dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n", | |
329 | ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]); | |
330 | ||
eedaf1ae JW |
331 | do_gettimeofday(&now); |
332 | timediff = now.tv_sec - ir->last_time.tv_sec; | |
333 | if (timediff + 1 > PULSE_MASK / 1000000) | |
334 | timediff = PULSE_MASK; | |
335 | else { | |
336 | timediff *= 1000000; | |
337 | timediff += now.tv_usec - ir->last_time.tv_usec; | |
338 | } | |
339 | ir->last_time.tv_sec = now.tv_sec; | |
340 | ir->last_time.tv_usec = now.tv_usec; | |
341 | ||
342 | /* create leading gap */ | |
343 | code = timediff; | |
344 | lirc_buffer_write(buf, (unsigned char *)&code); | |
345 | ir->in_space = 1; /* next comes a pulse */ | |
346 | ||
d653c0af JW |
347 | if (ir->buf_in[2] == 0) |
348 | send_fragment(ir, buf, DEVICE_HEADERLEN, ret); | |
349 | else { | |
350 | printk(KERN_WARNING DRIVER_NAME | |
351 | "[%d]: Device buffer overrun.\n", ir->devnum); | |
352 | /* HHHNNNNNNNNNNNOOOOOOOO H = header | |
353 | <---[2]---> N = newer | |
354 | <---------ret--------> O = older */ | |
355 | ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */ | |
356 | /* keep even-ness to not desync pulse/pause */ | |
357 | send_fragment(ir, buf, DEVICE_HEADERLEN + | |
358 | ir->buf_in[2] - (ir->buf_in[2] & 1), ret); | |
359 | send_fragment(ir, buf, DEVICE_HEADERLEN, | |
360 | DEVICE_HEADERLEN + ir->buf_in[2]); | |
eedaf1ae JW |
361 | } |
362 | ||
363 | ret = usb_control_msg( | |
364 | ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), | |
365 | SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, | |
366 | /*unused*/0, /*unused*/0, | |
367 | /*dummy*/ir->buf_in, /*dummy*/ir->len_in, | |
368 | /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); | |
369 | if (ret < 0) | |
370 | printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: " | |
371 | "error %d\n", ir->devnum, ret); | |
372 | return 0; | |
373 | } else if (ret < 0) | |
374 | printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n", | |
375 | ir->devnum, ret); | |
376 | ||
377 | return -ENODATA; | |
378 | } | |
379 | ||
380 | ||
381 | ||
917167e9 JW |
382 | static int igorplugusb_remote_probe(struct usb_interface *intf, |
383 | const struct usb_device_id *id) | |
eedaf1ae JW |
384 | { |
385 | struct usb_device *dev = NULL; | |
386 | struct usb_host_interface *idesc = NULL; | |
917167e9 | 387 | struct usb_endpoint_descriptor *ep; |
eedaf1ae JW |
388 | struct igorplug *ir = NULL; |
389 | struct lirc_driver *driver = NULL; | |
390 | int devnum, pipe, maxp; | |
391 | int minor = 0; | |
392 | char buf[63], name[128] = ""; | |
393 | int mem_failure = 0; | |
394 | int ret; | |
395 | ||
396 | dprintk(DRIVER_NAME ": usb probe called.\n"); | |
397 | ||
398 | dev = interface_to_usbdev(intf); | |
399 | ||
400 | idesc = intf->cur_altsetting; | |
401 | ||
402 | if (idesc->desc.bNumEndpoints != 1) | |
403 | return -ENODEV; | |
917167e9 JW |
404 | |
405 | ep = &idesc->endpoint->desc; | |
406 | if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) | |
eedaf1ae | 407 | != USB_DIR_IN) |
917167e9 | 408 | || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) |
eedaf1ae JW |
409 | != USB_ENDPOINT_XFER_CONTROL) |
410 | return -ENODEV; | |
917167e9 JW |
411 | |
412 | pipe = usb_rcvctrlpipe(dev, ep->bEndpointAddress); | |
eedaf1ae JW |
413 | devnum = dev->devnum; |
414 | maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); | |
415 | ||
2b361ab6 | 416 | dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n", |
eedaf1ae JW |
417 | devnum, CODE_LENGTH, maxp); |
418 | ||
eedaf1ae JW |
419 | mem_failure = 0; |
420 | ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL); | |
421 | if (!ir) { | |
422 | mem_failure = 1; | |
423 | goto mem_failure_switch; | |
424 | } | |
425 | driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); | |
426 | if (!driver) { | |
427 | mem_failure = 2; | |
428 | goto mem_failure_switch; | |
429 | } | |
430 | ||
917167e9 JW |
431 | ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, |
432 | GFP_ATOMIC, &ir->dma_in); | |
eedaf1ae JW |
433 | if (!ir->buf_in) { |
434 | mem_failure = 3; | |
435 | goto mem_failure_switch; | |
436 | } | |
437 | ||
438 | strcpy(driver->name, DRIVER_NAME " "); | |
439 | driver->minor = -1; | |
440 | driver->code_length = CODE_LENGTH * 8; /* in bits */ | |
441 | driver->features = LIRC_CAN_REC_MODE2; | |
442 | driver->data = ir; | |
443 | driver->chunk_size = CODE_LENGTH; | |
444 | driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES; | |
445 | driver->set_use_inc = &set_use_inc; | |
446 | driver->set_use_dec = &set_use_dec; | |
447 | driver->sample_rate = sample_rate; /* per second */ | |
917167e9 | 448 | driver->add_to_buf = &igorplugusb_remote_poll; |
eedaf1ae JW |
449 | driver->dev = &intf->dev; |
450 | driver->owner = THIS_MODULE; | |
451 | ||
eedaf1ae JW |
452 | minor = lirc_register_driver(driver); |
453 | if (minor < 0) | |
454 | mem_failure = 9; | |
455 | ||
456 | mem_failure_switch: | |
457 | ||
458 | switch (mem_failure) { | |
459 | case 9: | |
d653c0af | 460 | usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, |
eedaf1ae JW |
461 | ir->buf_in, ir->dma_in); |
462 | case 3: | |
463 | kfree(driver); | |
464 | case 2: | |
465 | kfree(ir); | |
466 | case 1: | |
467 | printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", | |
468 | devnum, mem_failure); | |
469 | return -ENOMEM; | |
470 | } | |
471 | ||
472 | driver->minor = minor; | |
473 | ir->d = driver; | |
474 | ir->devnum = devnum; | |
475 | ir->usbdev = dev; | |
d653c0af | 476 | ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN; |
eedaf1ae JW |
477 | ir->in_space = 1; /* First mode2 event is a space. */ |
478 | do_gettimeofday(&ir->last_time); | |
479 | ||
480 | if (dev->descriptor.iManufacturer | |
481 | && usb_string(dev, dev->descriptor.iManufacturer, | |
482 | buf, sizeof(buf)) > 0) | |
483 | strlcpy(name, buf, sizeof(name)); | |
484 | if (dev->descriptor.iProduct | |
485 | && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0) | |
486 | snprintf(name + strlen(name), sizeof(name) - strlen(name), | |
487 | " %s", buf); | |
488 | printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name, | |
489 | dev->bus->busnum, devnum); | |
490 | ||
491 | /* clear device buffer */ | |
492 | ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), | |
493 | SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, | |
494 | /*unused*/0, /*unused*/0, | |
495 | /*dummy*/ir->buf_in, /*dummy*/ir->len_in, | |
496 | /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); | |
497 | if (ret < 0) | |
498 | printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", | |
499 | devnum, ret); | |
500 | ||
501 | usb_set_intfdata(intf, ir); | |
502 | return 0; | |
503 | } | |
504 | ||
505 | ||
917167e9 | 506 | static void igorplugusb_remote_disconnect(struct usb_interface *intf) |
eedaf1ae | 507 | { |
917167e9 | 508 | struct usb_device *usbdev = interface_to_usbdev(intf); |
eedaf1ae | 509 | struct igorplug *ir = usb_get_intfdata(intf); |
917167e9 JW |
510 | struct device *dev = &intf->dev; |
511 | int devnum; | |
512 | ||
eedaf1ae JW |
513 | usb_set_intfdata(intf, NULL); |
514 | ||
515 | if (!ir || !ir->d) | |
516 | return; | |
517 | ||
518 | ir->usbdev = NULL; | |
eedaf1ae | 519 | |
917167e9 JW |
520 | usb_free_coherent(usbdev, ir->len_in, ir->buf_in, ir->dma_in); |
521 | ||
522 | devnum = unregister_from_lirc(ir); | |
eedaf1ae | 523 | |
917167e9 | 524 | dev_info(dev, DRIVER_NAME "[%d]: %s done\n", devnum, __func__); |
eedaf1ae JW |
525 | } |
526 | ||
917167e9 | 527 | static struct usb_device_id igorplugusb_remote_id_table[] = { |
eedaf1ae JW |
528 | /* Igor Plug USB (Atmel's Manufact. ID) */ |
529 | { USB_DEVICE(0x03eb, 0x0002) }, | |
966ea5b8 JW |
530 | /* Fit PC2 Infrared Adapter */ |
531 | { USB_DEVICE(0x03eb, 0x21fe) }, | |
eedaf1ae JW |
532 | |
533 | /* Terminating entry */ | |
534 | { } | |
535 | }; | |
536 | ||
917167e9 | 537 | static struct usb_driver igorplugusb_remote_driver = { |
eedaf1ae | 538 | .name = DRIVER_NAME, |
917167e9 JW |
539 | .probe = igorplugusb_remote_probe, |
540 | .disconnect = igorplugusb_remote_disconnect, | |
541 | .id_table = igorplugusb_remote_id_table | |
eedaf1ae JW |
542 | }; |
543 | ||
917167e9 | 544 | static int __init igorplugusb_remote_init(void) |
eedaf1ae | 545 | { |
917167e9 | 546 | int ret = 0; |
eedaf1ae | 547 | |
917167e9 | 548 | dprintk(DRIVER_NAME ": loaded, debug mode enabled\n"); |
eedaf1ae | 549 | |
917167e9 JW |
550 | ret = usb_register(&igorplugusb_remote_driver); |
551 | if (ret) | |
552 | printk(KERN_ERR DRIVER_NAME ": usb register failed!\n"); | |
eedaf1ae | 553 | |
917167e9 | 554 | return ret; |
eedaf1ae JW |
555 | } |
556 | ||
917167e9 | 557 | static void __exit igorplugusb_remote_exit(void) |
eedaf1ae | 558 | { |
917167e9 | 559 | usb_deregister(&igorplugusb_remote_driver); |
eedaf1ae JW |
560 | } |
561 | ||
917167e9 JW |
562 | module_init(igorplugusb_remote_init); |
563 | module_exit(igorplugusb_remote_exit); | |
eedaf1ae JW |
564 | |
565 | #include <linux/vermagic.h> | |
566 | MODULE_INFO(vermagic, VERMAGIC_STRING); | |
567 | ||
568 | MODULE_DESCRIPTION(DRIVER_DESC); | |
569 | MODULE_AUTHOR(DRIVER_AUTHOR); | |
570 | MODULE_LICENSE("GPL"); | |
917167e9 | 571 | MODULE_DEVICE_TABLE(usb, igorplugusb_remote_id_table); |
eedaf1ae JW |
572 | |
573 | module_param(sample_rate, int, S_IRUGO | S_IWUSR); | |
574 | MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)"); | |
575 | ||
917167e9 JW |
576 | module_param(debug, bool, S_IRUGO | S_IWUSR); |
577 | MODULE_PARM_DESC(debug, "Debug enabled or not"); |