diff options
Diffstat (limited to 'drivers/input/misc/ati_remote2.c')
-rw-r--r-- | drivers/input/misc/ati_remote2.c | 543 |
1 files changed, 543 insertions, 0 deletions
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c new file mode 100644 index 000000000000..1031543e5c3f --- /dev/null +++ b/drivers/input/misc/ati_remote2.c | |||
@@ -0,0 +1,543 @@ | |||
1 | /* | ||
2 | * ati_remote2 - ATI/Philips USB RF remote driver | ||
3 | * | ||
4 | * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi> | ||
5 | * Copyright (C) 2007 Peter Stokes <linux@dadeos.freeserve.co.uk> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 | ||
9 | * as published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/usb/input.h> | ||
13 | |||
14 | #define DRIVER_DESC "ATI/Philips USB RF remote driver" | ||
15 | #define DRIVER_VERSION "0.2" | ||
16 | |||
17 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
18 | MODULE_VERSION(DRIVER_VERSION); | ||
19 | MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); | ||
20 | MODULE_LICENSE("GPL"); | ||
21 | |||
22 | /* | ||
23 | * ATI Remote Wonder II Channel Configuration | ||
24 | * | ||
25 | * The remote control can by assigned one of sixteen "channels" in order to facilitate | ||
26 | * the use of multiple remote controls within range of each other. | ||
27 | * A remote's "channel" may be altered by pressing and holding the "PC" button for | ||
28 | * approximately 3 seconds, after which the button will slowly flash the count of the | ||
29 | * currently configured "channel", using the numeric keypad enter a number between 1 and | ||
30 | * 16 and then the "PC" button again, the button will slowly flash the count of the | ||
31 | * newly configured "channel". | ||
32 | */ | ||
33 | |||
34 | static unsigned int channel_mask = 0xFFFF; | ||
35 | module_param(channel_mask, uint, 0644); | ||
36 | MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>"); | ||
37 | |||
38 | static unsigned int mode_mask = 0x1F; | ||
39 | module_param(mode_mask, uint, 0644); | ||
40 | MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); | ||
41 | |||
42 | static struct usb_device_id ati_remote2_id_table[] = { | ||
43 | { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */ | ||
44 | { } | ||
45 | }; | ||
46 | MODULE_DEVICE_TABLE(usb, ati_remote2_id_table); | ||
47 | |||
48 | static struct { | ||
49 | int hw_code; | ||
50 | int key_code; | ||
51 | } ati_remote2_key_table[] = { | ||
52 | { 0x00, KEY_0 }, | ||
53 | { 0x01, KEY_1 }, | ||
54 | { 0x02, KEY_2 }, | ||
55 | { 0x03, KEY_3 }, | ||
56 | { 0x04, KEY_4 }, | ||
57 | { 0x05, KEY_5 }, | ||
58 | { 0x06, KEY_6 }, | ||
59 | { 0x07, KEY_7 }, | ||
60 | { 0x08, KEY_8 }, | ||
61 | { 0x09, KEY_9 }, | ||
62 | { 0x0c, KEY_POWER }, | ||
63 | { 0x0d, KEY_MUTE }, | ||
64 | { 0x10, KEY_VOLUMEUP }, | ||
65 | { 0x11, KEY_VOLUMEDOWN }, | ||
66 | { 0x20, KEY_CHANNELUP }, | ||
67 | { 0x21, KEY_CHANNELDOWN }, | ||
68 | { 0x28, KEY_FORWARD }, | ||
69 | { 0x29, KEY_REWIND }, | ||
70 | { 0x2c, KEY_PLAY }, | ||
71 | { 0x30, KEY_PAUSE }, | ||
72 | { 0x31, KEY_STOP }, | ||
73 | { 0x37, KEY_RECORD }, | ||
74 | { 0x38, KEY_DVD }, | ||
75 | { 0x39, KEY_TV }, | ||
76 | { 0x54, KEY_MENU }, | ||
77 | { 0x58, KEY_UP }, | ||
78 | { 0x59, KEY_DOWN }, | ||
79 | { 0x5a, KEY_LEFT }, | ||
80 | { 0x5b, KEY_RIGHT }, | ||
81 | { 0x5c, KEY_OK }, | ||
82 | { 0x78, KEY_A }, | ||
83 | { 0x79, KEY_B }, | ||
84 | { 0x7a, KEY_C }, | ||
85 | { 0x7b, KEY_D }, | ||
86 | { 0x7c, KEY_E }, | ||
87 | { 0x7d, KEY_F }, | ||
88 | { 0x82, KEY_ENTER }, | ||
89 | { 0x8e, KEY_VENDOR }, | ||
90 | { 0x96, KEY_COFFEE }, | ||
91 | { 0xa9, BTN_LEFT }, | ||
92 | { 0xaa, BTN_RIGHT }, | ||
93 | { 0xbe, KEY_QUESTION }, | ||
94 | { 0xd5, KEY_FRONT }, | ||
95 | { 0xd0, KEY_EDIT }, | ||
96 | { 0xf9, KEY_INFO }, | ||
97 | { (0x00 << 8) | 0x3f, KEY_PROG1 }, | ||
98 | { (0x01 << 8) | 0x3f, KEY_PROG2 }, | ||
99 | { (0x02 << 8) | 0x3f, KEY_PROG3 }, | ||
100 | { (0x03 << 8) | 0x3f, KEY_PROG4 }, | ||
101 | { (0x04 << 8) | 0x3f, KEY_PC }, | ||
102 | { 0, KEY_RESERVED } | ||
103 | }; | ||
104 | |||
105 | struct ati_remote2 { | ||
106 | struct input_dev *idev; | ||
107 | struct usb_device *udev; | ||
108 | |||
109 | struct usb_interface *intf[2]; | ||
110 | struct usb_endpoint_descriptor *ep[2]; | ||
111 | struct urb *urb[2]; | ||
112 | void *buf[2]; | ||
113 | dma_addr_t buf_dma[2]; | ||
114 | |||
115 | unsigned long jiffies; | ||
116 | int mode; | ||
117 | |||
118 | char name[64]; | ||
119 | char phys[64]; | ||
120 | }; | ||
121 | |||
122 | static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); | ||
123 | static void ati_remote2_disconnect(struct usb_interface *interface); | ||
124 | |||
125 | static struct usb_driver ati_remote2_driver = { | ||
126 | .name = "ati_remote2", | ||
127 | .probe = ati_remote2_probe, | ||
128 | .disconnect = ati_remote2_disconnect, | ||
129 | .id_table = ati_remote2_id_table, | ||
130 | }; | ||
131 | |||
132 | static int ati_remote2_open(struct input_dev *idev) | ||
133 | { | ||
134 | struct ati_remote2 *ar2 = input_get_drvdata(idev); | ||
135 | int r; | ||
136 | |||
137 | r = usb_submit_urb(ar2->urb[0], GFP_KERNEL); | ||
138 | if (r) { | ||
139 | dev_err(&ar2->intf[0]->dev, | ||
140 | "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
141 | return r; | ||
142 | } | ||
143 | r = usb_submit_urb(ar2->urb[1], GFP_KERNEL); | ||
144 | if (r) { | ||
145 | usb_kill_urb(ar2->urb[0]); | ||
146 | dev_err(&ar2->intf[1]->dev, | ||
147 | "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
148 | return r; | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static void ati_remote2_close(struct input_dev *idev) | ||
155 | { | ||
156 | struct ati_remote2 *ar2 = input_get_drvdata(idev); | ||
157 | |||
158 | usb_kill_urb(ar2->urb[0]); | ||
159 | usb_kill_urb(ar2->urb[1]); | ||
160 | } | ||
161 | |||
162 | static void ati_remote2_input_mouse(struct ati_remote2 *ar2) | ||
163 | { | ||
164 | struct input_dev *idev = ar2->idev; | ||
165 | u8 *data = ar2->buf[0]; | ||
166 | int channel, mode; | ||
167 | |||
168 | channel = data[0] >> 4; | ||
169 | |||
170 | if (!((1 << channel) & channel_mask)) | ||
171 | return; | ||
172 | |||
173 | mode = data[0] & 0x0F; | ||
174 | |||
175 | if (mode > 4) { | ||
176 | dev_err(&ar2->intf[0]->dev, | ||
177 | "Unknown mode byte (%02x %02x %02x %02x)\n", | ||
178 | data[3], data[2], data[1], data[0]); | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | if (!((1 << mode) & mode_mask)) | ||
183 | return; | ||
184 | |||
185 | input_event(idev, EV_REL, REL_X, (s8) data[1]); | ||
186 | input_event(idev, EV_REL, REL_Y, (s8) data[2]); | ||
187 | input_sync(idev); | ||
188 | } | ||
189 | |||
190 | static int ati_remote2_lookup(unsigned int hw_code) | ||
191 | { | ||
192 | int i; | ||
193 | |||
194 | for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) | ||
195 | if (ati_remote2_key_table[i].hw_code == hw_code) | ||
196 | return i; | ||
197 | |||
198 | return -1; | ||
199 | } | ||
200 | |||
201 | static void ati_remote2_input_key(struct ati_remote2 *ar2) | ||
202 | { | ||
203 | struct input_dev *idev = ar2->idev; | ||
204 | u8 *data = ar2->buf[1]; | ||
205 | int channel, mode, hw_code, index; | ||
206 | |||
207 | channel = data[0] >> 4; | ||
208 | |||
209 | if (!((1 << channel) & channel_mask)) | ||
210 | return; | ||
211 | |||
212 | mode = data[0] & 0x0F; | ||
213 | |||
214 | if (mode > 4) { | ||
215 | dev_err(&ar2->intf[1]->dev, | ||
216 | "Unknown mode byte (%02x %02x %02x %02x)\n", | ||
217 | data[3], data[2], data[1], data[0]); | ||
218 | return; | ||
219 | } | ||
220 | |||
221 | hw_code = data[2]; | ||
222 | /* | ||
223 | * Mode keys (AUX1-AUX4, PC) all generate the same code byte. | ||
224 | * Use the mode byte to figure out which one was pressed. | ||
225 | */ | ||
226 | if (hw_code == 0x3f) { | ||
227 | /* | ||
228 | * For some incomprehensible reason the mouse pad generates | ||
229 | * events which look identical to the events from the last | ||
230 | * pressed mode key. Naturally we don't want to generate key | ||
231 | * events for the mouse pad so we filter out any subsequent | ||
232 | * events from the same mode key. | ||
233 | */ | ||
234 | if (ar2->mode == mode) | ||
235 | return; | ||
236 | |||
237 | if (data[1] == 0) | ||
238 | ar2->mode = mode; | ||
239 | |||
240 | hw_code |= mode << 8; | ||
241 | } | ||
242 | |||
243 | if (!((1 << mode) & mode_mask)) | ||
244 | return; | ||
245 | |||
246 | index = ati_remote2_lookup(hw_code); | ||
247 | if (index < 0) { | ||
248 | dev_err(&ar2->intf[1]->dev, | ||
249 | "Unknown code byte (%02x %02x %02x %02x)\n", | ||
250 | data[3], data[2], data[1], data[0]); | ||
251 | return; | ||
252 | } | ||
253 | |||
254 | switch (data[1]) { | ||
255 | case 0: /* release */ | ||
256 | break; | ||
257 | case 1: /* press */ | ||
258 | ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]); | ||
259 | break; | ||
260 | case 2: /* repeat */ | ||
261 | |||
262 | /* No repeat for mouse buttons. */ | ||
263 | if (ati_remote2_key_table[index].key_code == BTN_LEFT || | ||
264 | ati_remote2_key_table[index].key_code == BTN_RIGHT) | ||
265 | return; | ||
266 | |||
267 | if (!time_after_eq(jiffies, ar2->jiffies)) | ||
268 | return; | ||
269 | |||
270 | ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]); | ||
271 | break; | ||
272 | default: | ||
273 | dev_err(&ar2->intf[1]->dev, | ||
274 | "Unknown state byte (%02x %02x %02x %02x)\n", | ||
275 | data[3], data[2], data[1], data[0]); | ||
276 | return; | ||
277 | } | ||
278 | |||
279 | input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]); | ||
280 | input_sync(idev); | ||
281 | } | ||
282 | |||
283 | static void ati_remote2_complete_mouse(struct urb *urb) | ||
284 | { | ||
285 | struct ati_remote2 *ar2 = urb->context; | ||
286 | int r; | ||
287 | |||
288 | switch (urb->status) { | ||
289 | case 0: | ||
290 | ati_remote2_input_mouse(ar2); | ||
291 | break; | ||
292 | case -ENOENT: | ||
293 | case -EILSEQ: | ||
294 | case -ECONNRESET: | ||
295 | case -ESHUTDOWN: | ||
296 | dev_dbg(&ar2->intf[0]->dev, | ||
297 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
298 | return; | ||
299 | default: | ||
300 | dev_err(&ar2->intf[0]->dev, | ||
301 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
302 | } | ||
303 | |||
304 | r = usb_submit_urb(urb, GFP_ATOMIC); | ||
305 | if (r) | ||
306 | dev_err(&ar2->intf[0]->dev, | ||
307 | "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
308 | } | ||
309 | |||
310 | static void ati_remote2_complete_key(struct urb *urb) | ||
311 | { | ||
312 | struct ati_remote2 *ar2 = urb->context; | ||
313 | int r; | ||
314 | |||
315 | switch (urb->status) { | ||
316 | case 0: | ||
317 | ati_remote2_input_key(ar2); | ||
318 | break; | ||
319 | case -ENOENT: | ||
320 | case -EILSEQ: | ||
321 | case -ECONNRESET: | ||
322 | case -ESHUTDOWN: | ||
323 | dev_dbg(&ar2->intf[1]->dev, | ||
324 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
325 | return; | ||
326 | default: | ||
327 | dev_err(&ar2->intf[1]->dev, | ||
328 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
329 | } | ||
330 | |||
331 | r = usb_submit_urb(urb, GFP_ATOMIC); | ||
332 | if (r) | ||
333 | dev_err(&ar2->intf[1]->dev, | ||
334 | "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
335 | } | ||
336 | |||
337 | static int ati_remote2_input_init(struct ati_remote2 *ar2) | ||
338 | { | ||
339 | struct input_dev *idev; | ||
340 | int i, retval; | ||
341 | |||
342 | idev = input_allocate_device(); | ||
343 | if (!idev) | ||
344 | return -ENOMEM; | ||
345 | |||
346 | ar2->idev = idev; | ||
347 | input_set_drvdata(idev, ar2); | ||
348 | |||
349 | idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL); | ||
350 | idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); | ||
351 | idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
352 | for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) | ||
353 | set_bit(ati_remote2_key_table[i].key_code, idev->keybit); | ||
354 | |||
355 | idev->rep[REP_DELAY] = 250; | ||
356 | idev->rep[REP_PERIOD] = 33; | ||
357 | |||
358 | idev->open = ati_remote2_open; | ||
359 | idev->close = ati_remote2_close; | ||
360 | |||
361 | idev->name = ar2->name; | ||
362 | idev->phys = ar2->phys; | ||
363 | |||
364 | usb_to_input_id(ar2->udev, &idev->id); | ||
365 | idev->dev.parent = &ar2->udev->dev; | ||
366 | |||
367 | retval = input_register_device(idev); | ||
368 | if (retval) | ||
369 | input_free_device(idev); | ||
370 | |||
371 | return retval; | ||
372 | } | ||
373 | |||
374 | static int ati_remote2_urb_init(struct ati_remote2 *ar2) | ||
375 | { | ||
376 | struct usb_device *udev = ar2->udev; | ||
377 | int i, pipe, maxp; | ||
378 | |||
379 | for (i = 0; i < 2; i++) { | ||
380 | ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]); | ||
381 | if (!ar2->buf[i]) | ||
382 | return -ENOMEM; | ||
383 | |||
384 | ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL); | ||
385 | if (!ar2->urb[i]) | ||
386 | return -ENOMEM; | ||
387 | |||
388 | pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress); | ||
389 | maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); | ||
390 | maxp = maxp > 4 ? 4 : maxp; | ||
391 | |||
392 | usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp, | ||
393 | i ? ati_remote2_complete_key : ati_remote2_complete_mouse, | ||
394 | ar2, ar2->ep[i]->bInterval); | ||
395 | ar2->urb[i]->transfer_dma = ar2->buf_dma[i]; | ||
396 | ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | ||
397 | } | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2) | ||
403 | { | ||
404 | int i; | ||
405 | |||
406 | for (i = 0; i < 2; i++) { | ||
407 | usb_free_urb(ar2->urb[i]); | ||
408 | usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]); | ||
409 | } | ||
410 | } | ||
411 | |||
412 | static int ati_remote2_setup(struct ati_remote2 *ar2) | ||
413 | { | ||
414 | int r, i, channel; | ||
415 | |||
416 | /* | ||
417 | * Configure receiver to only accept input from remote "channel" | ||
418 | * channel == 0 -> Accept input from any remote channel | ||
419 | * channel == 1 -> Only accept input from remote channel 1 | ||
420 | * channel == 2 -> Only accept input from remote channel 2 | ||
421 | * ... | ||
422 | * channel == 16 -> Only accept input from remote channel 16 | ||
423 | */ | ||
424 | |||
425 | channel = 0; | ||
426 | for (i = 0; i < 16; i++) { | ||
427 | if ((1 << i) & channel_mask) { | ||
428 | if (!(~(1 << i) & 0xFFFF & channel_mask)) | ||
429 | channel = i + 1; | ||
430 | break; | ||
431 | } | ||
432 | } | ||
433 | |||
434 | r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0), | ||
435 | 0x20, | ||
436 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, | ||
437 | channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT); | ||
438 | if (r) { | ||
439 | dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n", | ||
440 | __FUNCTION__, r); | ||
441 | return r; | ||
442 | } | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id) | ||
448 | { | ||
449 | struct usb_device *udev = interface_to_usbdev(interface); | ||
450 | struct usb_host_interface *alt = interface->cur_altsetting; | ||
451 | struct ati_remote2 *ar2; | ||
452 | int r; | ||
453 | |||
454 | if (alt->desc.bInterfaceNumber) | ||
455 | return -ENODEV; | ||
456 | |||
457 | ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL); | ||
458 | if (!ar2) | ||
459 | return -ENOMEM; | ||
460 | |||
461 | ar2->udev = udev; | ||
462 | |||
463 | ar2->intf[0] = interface; | ||
464 | ar2->ep[0] = &alt->endpoint[0].desc; | ||
465 | |||
466 | ar2->intf[1] = usb_ifnum_to_if(udev, 1); | ||
467 | r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2); | ||
468 | if (r) | ||
469 | goto fail1; | ||
470 | alt = ar2->intf[1]->cur_altsetting; | ||
471 | ar2->ep[1] = &alt->endpoint[0].desc; | ||
472 | |||
473 | r = ati_remote2_urb_init(ar2); | ||
474 | if (r) | ||
475 | goto fail2; | ||
476 | |||
477 | r = ati_remote2_setup(ar2); | ||
478 | if (r) | ||
479 | goto fail2; | ||
480 | |||
481 | usb_make_path(udev, ar2->phys, sizeof(ar2->phys)); | ||
482 | strlcat(ar2->phys, "/input0", sizeof(ar2->phys)); | ||
483 | |||
484 | strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name)); | ||
485 | |||
486 | r = ati_remote2_input_init(ar2); | ||
487 | if (r) | ||
488 | goto fail2; | ||
489 | |||
490 | usb_set_intfdata(interface, ar2); | ||
491 | |||
492 | return 0; | ||
493 | |||
494 | fail2: | ||
495 | ati_remote2_urb_cleanup(ar2); | ||
496 | |||
497 | usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); | ||
498 | fail1: | ||
499 | kfree(ar2); | ||
500 | |||
501 | return r; | ||
502 | } | ||
503 | |||
504 | static void ati_remote2_disconnect(struct usb_interface *interface) | ||
505 | { | ||
506 | struct ati_remote2 *ar2; | ||
507 | struct usb_host_interface *alt = interface->cur_altsetting; | ||
508 | |||
509 | if (alt->desc.bInterfaceNumber) | ||
510 | return; | ||
511 | |||
512 | ar2 = usb_get_intfdata(interface); | ||
513 | usb_set_intfdata(interface, NULL); | ||
514 | |||
515 | input_unregister_device(ar2->idev); | ||
516 | |||
517 | ati_remote2_urb_cleanup(ar2); | ||
518 | |||
519 | usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); | ||
520 | |||
521 | kfree(ar2); | ||
522 | } | ||
523 | |||
524 | static int __init ati_remote2_init(void) | ||
525 | { | ||
526 | int r; | ||
527 | |||
528 | r = usb_register(&ati_remote2_driver); | ||
529 | if (r) | ||
530 | printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r); | ||
531 | else | ||
532 | printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n"); | ||
533 | |||
534 | return r; | ||
535 | } | ||
536 | |||
537 | static void __exit ati_remote2_exit(void) | ||
538 | { | ||
539 | usb_deregister(&ati_remote2_driver); | ||
540 | } | ||
541 | |||
542 | module_init(ati_remote2_init); | ||
543 | module_exit(ati_remote2_exit); | ||