diff options
author | Ville Syrjälä <syrjala@sci.fi> | 2005-12-10 13:30:54 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-01-04 16:51:43 -0500 |
commit | 735b0cbb5bbb981d726a465c157f20976794aab0 (patch) | |
tree | e0f5cf4e14534028bc0c174e3b53d8cf8867682f /drivers | |
parent | 52950ed40dc97456209979af1d8f51b63cf6dcab (diff) |
[PATCH] USB: add driver for ATI/Philips USB RF remotes
Summary: Driver for ATI/Philips USB RF remotes
This is a new input driver for ATI/Philips USB RF remotes (eg. ATI
Remote Wonder II).
Signed-off-by: Ville Syrjälä <syrjala@sci.fi>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/input/Kconfig | 14 | ||||
-rw-r--r-- | drivers/usb/input/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/input/ati_remote2.c | 477 |
3 files changed, 492 insertions, 0 deletions
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 1e53934907c0..509dd0a04c54 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig | |||
@@ -273,6 +273,20 @@ config USB_ATI_REMOTE | |||
273 | To compile this driver as a module, choose M here: the module will be | 273 | To compile this driver as a module, choose M here: the module will be |
274 | called ati_remote. | 274 | called ati_remote. |
275 | 275 | ||
276 | config USB_ATI_REMOTE2 | ||
277 | tristate "ATI / Philips USB RF remote control" | ||
278 | depends on USB && INPUT | ||
279 | ---help--- | ||
280 | Say Y here if you want to use an ATI or Philips USB RF remote control. | ||
281 | These are RF remotes with USB receivers. | ||
282 | ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards | ||
283 | and is also available as a separate product. | ||
284 | This driver provides mouse pointer, left and right mouse buttons, | ||
285 | and maps all the other remote buttons to keypress events. | ||
286 | |||
287 | To compile this driver as a module, choose M here: the module will be | ||
288 | called ati_remote2. | ||
289 | |||
276 | config USB_KEYSPAN_REMOTE | 290 | config USB_KEYSPAN_REMOTE |
277 | tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" | 291 | tristate "Keyspan DMR USB remote control (EXPERIMENTAL)" |
278 | depends on USB && INPUT && EXPERIMENTAL | 292 | depends on USB && INPUT && EXPERIMENTAL |
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index 07cb17db42fc..d512d9f488fe 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile | |||
@@ -28,6 +28,7 @@ endif | |||
28 | 28 | ||
29 | obj-$(CONFIG_USB_AIPTEK) += aiptek.o | 29 | obj-$(CONFIG_USB_AIPTEK) += aiptek.o |
30 | obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o | 30 | obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o |
31 | obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o | ||
31 | obj-$(CONFIG_USB_HID) += usbhid.o | 32 | obj-$(CONFIG_USB_HID) += usbhid.o |
32 | obj-$(CONFIG_USB_KBD) += usbkbd.o | 33 | obj-$(CONFIG_USB_KBD) += usbkbd.o |
33 | obj-$(CONFIG_USB_KBTAB) += kbtab.o | 34 | obj-$(CONFIG_USB_KBTAB) += kbtab.o |
diff --git a/drivers/usb/input/ati_remote2.c b/drivers/usb/input/ati_remote2.c new file mode 100644 index 000000000000..ab1a1ae24be9 --- /dev/null +++ b/drivers/usb/input/ati_remote2.c | |||
@@ -0,0 +1,477 @@ | |||
1 | /* | ||
2 | * ati_remote2 - ATI/Philips USB RF remote driver | ||
3 | * | ||
4 | * Copyright (C) 2005 Ville Syrjala <syrjala@sci.fi> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 | ||
8 | * as published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/usb_input.h> | ||
12 | |||
13 | #define DRIVER_DESC "ATI/Philips USB RF remote driver" | ||
14 | #define DRIVER_VERSION "0.1" | ||
15 | |||
16 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
17 | MODULE_VERSION(DRIVER_VERSION); | ||
18 | MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>"); | ||
19 | MODULE_LICENSE("GPL"); | ||
20 | |||
21 | static unsigned int mode_mask = 0x1F; | ||
22 | module_param(mode_mask, uint, 0644); | ||
23 | MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>"); | ||
24 | |||
25 | static struct usb_device_id ati_remote2_id_table[] = { | ||
26 | { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */ | ||
27 | { } | ||
28 | }; | ||
29 | MODULE_DEVICE_TABLE(usb, ati_remote2_id_table); | ||
30 | |||
31 | static struct { | ||
32 | int hw_code; | ||
33 | int key_code; | ||
34 | } ati_remote2_key_table[] = { | ||
35 | { 0x00, KEY_0 }, | ||
36 | { 0x01, KEY_1 }, | ||
37 | { 0x02, KEY_2 }, | ||
38 | { 0x03, KEY_3 }, | ||
39 | { 0x04, KEY_4 }, | ||
40 | { 0x05, KEY_5 }, | ||
41 | { 0x06, KEY_6 }, | ||
42 | { 0x07, KEY_7 }, | ||
43 | { 0x08, KEY_8 }, | ||
44 | { 0x09, KEY_9 }, | ||
45 | { 0x0c, KEY_POWER }, | ||
46 | { 0x0d, KEY_MUTE }, | ||
47 | { 0x10, KEY_VOLUMEUP }, | ||
48 | { 0x11, KEY_VOLUMEDOWN }, | ||
49 | { 0x20, KEY_CHANNELUP }, | ||
50 | { 0x21, KEY_CHANNELDOWN }, | ||
51 | { 0x28, KEY_FORWARD }, | ||
52 | { 0x29, KEY_REWIND }, | ||
53 | { 0x2c, KEY_PLAY }, | ||
54 | { 0x30, KEY_PAUSE }, | ||
55 | { 0x31, KEY_STOP }, | ||
56 | { 0x37, KEY_RECORD }, | ||
57 | { 0x38, KEY_DVD }, | ||
58 | { 0x39, KEY_TV }, | ||
59 | { 0x54, KEY_MENU }, | ||
60 | { 0x58, KEY_UP }, | ||
61 | { 0x59, KEY_DOWN }, | ||
62 | { 0x5a, KEY_LEFT }, | ||
63 | { 0x5b, KEY_RIGHT }, | ||
64 | { 0x5c, KEY_OK }, | ||
65 | { 0x78, KEY_A }, | ||
66 | { 0x79, KEY_B }, | ||
67 | { 0x7a, KEY_C }, | ||
68 | { 0x7b, KEY_D }, | ||
69 | { 0x7c, KEY_E }, | ||
70 | { 0x7d, KEY_F }, | ||
71 | { 0x82, KEY_ENTER }, | ||
72 | { 0x8e, KEY_VENDOR }, | ||
73 | { 0x96, KEY_COFFEE }, | ||
74 | { 0xa9, BTN_LEFT }, | ||
75 | { 0xaa, BTN_RIGHT }, | ||
76 | { 0xbe, KEY_QUESTION }, | ||
77 | { 0xd5, KEY_FRONT }, | ||
78 | { 0xd0, KEY_EDIT }, | ||
79 | { 0xf9, KEY_INFO }, | ||
80 | { (0x00 << 8) | 0x3f, KEY_PROG1 }, | ||
81 | { (0x01 << 8) | 0x3f, KEY_PROG2 }, | ||
82 | { (0x02 << 8) | 0x3f, KEY_PROG3 }, | ||
83 | { (0x03 << 8) | 0x3f, KEY_PROG4 }, | ||
84 | { (0x04 << 8) | 0x3f, KEY_PC }, | ||
85 | { 0, KEY_RESERVED } | ||
86 | }; | ||
87 | |||
88 | struct ati_remote2 { | ||
89 | struct input_dev *idev; | ||
90 | struct usb_device *udev; | ||
91 | |||
92 | struct usb_interface *intf[2]; | ||
93 | struct usb_endpoint_descriptor *ep[2]; | ||
94 | struct urb *urb[2]; | ||
95 | void *buf[2]; | ||
96 | dma_addr_t buf_dma[2]; | ||
97 | |||
98 | unsigned long jiffies; | ||
99 | int mode; | ||
100 | |||
101 | char name[64]; | ||
102 | char phys[64]; | ||
103 | }; | ||
104 | |||
105 | static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id); | ||
106 | static void ati_remote2_disconnect(struct usb_interface *interface); | ||
107 | |||
108 | static struct usb_driver ati_remote2_driver = { | ||
109 | .name = "ati_remote2", | ||
110 | .probe = ati_remote2_probe, | ||
111 | .disconnect = ati_remote2_disconnect, | ||
112 | .id_table = ati_remote2_id_table, | ||
113 | }; | ||
114 | |||
115 | static int ati_remote2_open(struct input_dev *idev) | ||
116 | { | ||
117 | struct ati_remote2 *ar2 = idev->private; | ||
118 | int r; | ||
119 | |||
120 | r = usb_submit_urb(ar2->urb[0], GFP_KERNEL); | ||
121 | if (r) { | ||
122 | dev_err(&ar2->intf[0]->dev, | ||
123 | "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
124 | return r; | ||
125 | } | ||
126 | r = usb_submit_urb(ar2->urb[1], GFP_KERNEL); | ||
127 | if (r) { | ||
128 | usb_kill_urb(ar2->urb[0]); | ||
129 | dev_err(&ar2->intf[1]->dev, | ||
130 | "%s: usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
131 | return r; | ||
132 | } | ||
133 | |||
134 | return 0; | ||
135 | } | ||
136 | |||
137 | static void ati_remote2_close(struct input_dev *idev) | ||
138 | { | ||
139 | struct ati_remote2 *ar2 = idev->private; | ||
140 | |||
141 | usb_kill_urb(ar2->urb[0]); | ||
142 | usb_kill_urb(ar2->urb[1]); | ||
143 | } | ||
144 | |||
145 | static void ati_remote2_input_mouse(struct ati_remote2 *ar2, struct pt_regs *regs) | ||
146 | { | ||
147 | struct input_dev *idev = ar2->idev; | ||
148 | u8 *data = ar2->buf[0]; | ||
149 | |||
150 | if (data[0] > 4) { | ||
151 | dev_err(&ar2->intf[0]->dev, | ||
152 | "Unknown mode byte (%02x %02x %02x %02x)\n", | ||
153 | data[3], data[2], data[1], data[0]); | ||
154 | return; | ||
155 | } | ||
156 | |||
157 | if (!((1 << data[0]) & mode_mask)) | ||
158 | return; | ||
159 | |||
160 | input_regs(idev, regs); | ||
161 | input_event(idev, EV_REL, REL_X, (s8) data[1]); | ||
162 | input_event(idev, EV_REL, REL_Y, (s8) data[2]); | ||
163 | input_sync(idev); | ||
164 | } | ||
165 | |||
166 | static int ati_remote2_lookup(unsigned int hw_code) | ||
167 | { | ||
168 | int i; | ||
169 | |||
170 | for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) | ||
171 | if (ati_remote2_key_table[i].hw_code == hw_code) | ||
172 | return i; | ||
173 | |||
174 | return -1; | ||
175 | } | ||
176 | |||
177 | static void ati_remote2_input_key(struct ati_remote2 *ar2, struct pt_regs *regs) | ||
178 | { | ||
179 | struct input_dev *idev = ar2->idev; | ||
180 | u8 *data = ar2->buf[1]; | ||
181 | int hw_code, index; | ||
182 | |||
183 | if (data[0] > 4) { | ||
184 | dev_err(&ar2->intf[1]->dev, | ||
185 | "Unknown mode byte (%02x %02x %02x %02x)\n", | ||
186 | data[3], data[2], data[1], data[0]); | ||
187 | return; | ||
188 | } | ||
189 | |||
190 | hw_code = data[2]; | ||
191 | /* | ||
192 | * Mode keys (AUX1-AUX4, PC) all generate the same code byte. | ||
193 | * Use the mode byte to figure out which one was pressed. | ||
194 | */ | ||
195 | if (hw_code == 0x3f) { | ||
196 | /* | ||
197 | * For some incomprehensible reason the mouse pad generates | ||
198 | * events which look identical to the events from the last | ||
199 | * pressed mode key. Naturally we don't want to generate key | ||
200 | * events for the mouse pad so we filter out any subsequent | ||
201 | * events from the same mode key. | ||
202 | */ | ||
203 | if (ar2->mode == data[0]) | ||
204 | return; | ||
205 | |||
206 | if (data[1] == 0) | ||
207 | ar2->mode = data[0]; | ||
208 | |||
209 | hw_code |= data[0] << 8; | ||
210 | } | ||
211 | |||
212 | if (!((1 << data[0]) & mode_mask)) | ||
213 | return; | ||
214 | |||
215 | index = ati_remote2_lookup(hw_code); | ||
216 | if (index < 0) { | ||
217 | dev_err(&ar2->intf[1]->dev, | ||
218 | "Unknown code byte (%02x %02x %02x %02x)\n", | ||
219 | data[3], data[2], data[1], data[0]); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | switch (data[1]) { | ||
224 | case 0: /* release */ | ||
225 | break; | ||
226 | case 1: /* press */ | ||
227 | ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]); | ||
228 | break; | ||
229 | case 2: /* repeat */ | ||
230 | |||
231 | /* No repeat for mouse buttons. */ | ||
232 | if (ati_remote2_key_table[index].key_code == BTN_LEFT || | ||
233 | ati_remote2_key_table[index].key_code == BTN_RIGHT) | ||
234 | return; | ||
235 | |||
236 | if (!time_after_eq(jiffies, ar2->jiffies)) | ||
237 | return; | ||
238 | |||
239 | ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]); | ||
240 | break; | ||
241 | default: | ||
242 | dev_err(&ar2->intf[1]->dev, | ||
243 | "Unknown state byte (%02x %02x %02x %02x)\n", | ||
244 | data[3], data[2], data[1], data[0]); | ||
245 | return; | ||
246 | } | ||
247 | |||
248 | input_regs(idev, regs); | ||
249 | input_event(idev, EV_KEY, ati_remote2_key_table[index].key_code, data[1]); | ||
250 | input_sync(idev); | ||
251 | } | ||
252 | |||
253 | static void ati_remote2_complete_mouse(struct urb *urb, struct pt_regs *regs) | ||
254 | { | ||
255 | struct ati_remote2 *ar2 = urb->context; | ||
256 | int r; | ||
257 | |||
258 | switch (urb->status) { | ||
259 | case 0: | ||
260 | ati_remote2_input_mouse(ar2, regs); | ||
261 | break; | ||
262 | case -ENOENT: | ||
263 | case -EILSEQ: | ||
264 | case -ECONNRESET: | ||
265 | case -ESHUTDOWN: | ||
266 | dev_dbg(&ar2->intf[0]->dev, | ||
267 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
268 | return; | ||
269 | default: | ||
270 | dev_err(&ar2->intf[0]->dev, | ||
271 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
272 | } | ||
273 | |||
274 | r = usb_submit_urb(urb, GFP_ATOMIC); | ||
275 | if (r) | ||
276 | dev_err(&ar2->intf[0]->dev, | ||
277 | "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
278 | } | ||
279 | |||
280 | static void ati_remote2_complete_key(struct urb *urb, struct pt_regs *regs) | ||
281 | { | ||
282 | struct ati_remote2 *ar2 = urb->context; | ||
283 | int r; | ||
284 | |||
285 | switch (urb->status) { | ||
286 | case 0: | ||
287 | ati_remote2_input_key(ar2, regs); | ||
288 | break; | ||
289 | case -ENOENT: | ||
290 | case -EILSEQ: | ||
291 | case -ECONNRESET: | ||
292 | case -ESHUTDOWN: | ||
293 | dev_dbg(&ar2->intf[1]->dev, | ||
294 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
295 | return; | ||
296 | default: | ||
297 | dev_err(&ar2->intf[1]->dev, | ||
298 | "%s(): urb status = %d\n", __FUNCTION__, urb->status); | ||
299 | } | ||
300 | |||
301 | r = usb_submit_urb(urb, GFP_ATOMIC); | ||
302 | if (r) | ||
303 | dev_err(&ar2->intf[1]->dev, | ||
304 | "%s(): usb_submit_urb() = %d\n", __FUNCTION__, r); | ||
305 | } | ||
306 | |||
307 | static int ati_remote2_input_init(struct ati_remote2 *ar2) | ||
308 | { | ||
309 | struct input_dev *idev; | ||
310 | int i; | ||
311 | |||
312 | idev = input_allocate_device(); | ||
313 | if (!idev) | ||
314 | return -ENOMEM; | ||
315 | |||
316 | ar2->idev = idev; | ||
317 | idev->private = ar2; | ||
318 | |||
319 | idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL); | ||
320 | idev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT); | ||
321 | idev->relbit[0] = BIT(REL_X) | BIT(REL_Y); | ||
322 | for (i = 0; ati_remote2_key_table[i].key_code != KEY_RESERVED; i++) | ||
323 | set_bit(ati_remote2_key_table[i].key_code, idev->keybit); | ||
324 | |||
325 | idev->rep[REP_DELAY] = 250; | ||
326 | idev->rep[REP_PERIOD] = 33; | ||
327 | |||
328 | idev->open = ati_remote2_open; | ||
329 | idev->close = ati_remote2_close; | ||
330 | |||
331 | idev->name = ar2->name; | ||
332 | idev->phys = ar2->phys; | ||
333 | |||
334 | usb_to_input_id(ar2->udev, &idev->id); | ||
335 | idev->cdev.dev = &ar2->udev->dev; | ||
336 | |||
337 | i = input_register_device(idev); | ||
338 | if (i) | ||
339 | input_free_device(idev); | ||
340 | |||
341 | return i; | ||
342 | } | ||
343 | |||
344 | static int ati_remote2_urb_init(struct ati_remote2 *ar2) | ||
345 | { | ||
346 | struct usb_device *udev = ar2->udev; | ||
347 | int i, pipe, maxp; | ||
348 | |||
349 | for (i = 0; i < 2; i++) { | ||
350 | ar2->buf[i] = usb_buffer_alloc(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]); | ||
351 | if (!ar2->buf[i]) | ||
352 | return -ENOMEM; | ||
353 | |||
354 | ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL); | ||
355 | if (!ar2->urb[i]) | ||
356 | return -ENOMEM; | ||
357 | |||
358 | pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress); | ||
359 | maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe)); | ||
360 | maxp = maxp > 4 ? 4 : maxp; | ||
361 | |||
362 | usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp, | ||
363 | i ? ati_remote2_complete_key : ati_remote2_complete_mouse, | ||
364 | ar2, ar2->ep[i]->bInterval); | ||
365 | ar2->urb[i]->transfer_dma = ar2->buf_dma[i]; | ||
366 | ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | ||
367 | } | ||
368 | |||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2) | ||
373 | { | ||
374 | int i; | ||
375 | |||
376 | for (i = 0; i < 2; i++) { | ||
377 | if (ar2->urb[i]) | ||
378 | usb_free_urb(ar2->urb[i]); | ||
379 | |||
380 | if (ar2->buf[i]) | ||
381 | usb_buffer_free(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]); | ||
382 | } | ||
383 | } | ||
384 | |||
385 | static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id) | ||
386 | { | ||
387 | struct usb_device *udev = interface_to_usbdev(interface); | ||
388 | struct usb_host_interface *alt = interface->cur_altsetting; | ||
389 | struct ati_remote2 *ar2; | ||
390 | int r; | ||
391 | |||
392 | if (alt->desc.bInterfaceNumber) | ||
393 | return -ENODEV; | ||
394 | |||
395 | ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL); | ||
396 | if (!ar2) | ||
397 | return -ENOMEM; | ||
398 | |||
399 | ar2->udev = udev; | ||
400 | |||
401 | ar2->intf[0] = interface; | ||
402 | ar2->ep[0] = &alt->endpoint[0].desc; | ||
403 | |||
404 | ar2->intf[1] = usb_ifnum_to_if(udev, 1); | ||
405 | r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2); | ||
406 | if (r) | ||
407 | goto fail1; | ||
408 | alt = ar2->intf[1]->cur_altsetting; | ||
409 | ar2->ep[1] = &alt->endpoint[0].desc; | ||
410 | |||
411 | r = ati_remote2_urb_init(ar2); | ||
412 | if (r) | ||
413 | goto fail2; | ||
414 | |||
415 | usb_make_path(udev, ar2->phys, sizeof(ar2->phys)); | ||
416 | strlcat(ar2->phys, "/input0", sizeof(ar2->phys)); | ||
417 | |||
418 | strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name)); | ||
419 | |||
420 | r = ati_remote2_input_init(ar2); | ||
421 | if (r) | ||
422 | goto fail2; | ||
423 | |||
424 | usb_set_intfdata(interface, ar2); | ||
425 | |||
426 | return 0; | ||
427 | |||
428 | fail2: | ||
429 | ati_remote2_urb_cleanup(ar2); | ||
430 | |||
431 | usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); | ||
432 | fail1: | ||
433 | kfree(ar2); | ||
434 | |||
435 | return r; | ||
436 | } | ||
437 | |||
438 | static void ati_remote2_disconnect(struct usb_interface *interface) | ||
439 | { | ||
440 | struct ati_remote2 *ar2; | ||
441 | struct usb_host_interface *alt = interface->cur_altsetting; | ||
442 | |||
443 | if (alt->desc.bInterfaceNumber) | ||
444 | return; | ||
445 | |||
446 | ar2 = usb_get_intfdata(interface); | ||
447 | usb_set_intfdata(interface, NULL); | ||
448 | |||
449 | input_unregister_device(ar2->idev); | ||
450 | |||
451 | ati_remote2_urb_cleanup(ar2); | ||
452 | |||
453 | usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]); | ||
454 | |||
455 | kfree(ar2); | ||
456 | } | ||
457 | |||
458 | static int __init ati_remote2_init(void) | ||
459 | { | ||
460 | int r; | ||
461 | |||
462 | r = usb_register(&ati_remote2_driver); | ||
463 | if (r) | ||
464 | printk(KERN_ERR "ati_remote2: usb_register() = %d\n", r); | ||
465 | else | ||
466 | printk(KERN_INFO "ati_remote2: " DRIVER_DESC " " DRIVER_VERSION "\n"); | ||
467 | |||
468 | return r; | ||
469 | } | ||
470 | |||
471 | static void __exit ati_remote2_exit(void) | ||
472 | { | ||
473 | usb_deregister(&ati_remote2_driver); | ||
474 | } | ||
475 | |||
476 | module_init(ati_remote2_init); | ||
477 | module_exit(ati_remote2_exit); | ||