aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorSean Young <sean@mess.org>2006-07-09 09:01:02 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-09-27 14:58:49 -0400
commitd5176b413dcce85334e270021fc0d723d1714c84 (patch)
tree690b664d7f75a6f4376c9a8424af79fa6eca5659 /drivers/usb
parentb6eb2d84d2bb01e9fcc46a032a3429b4747b1c47 (diff)
USB: Add driver for PhidgetMotorControl
This driver add support for the Phidgets Inc., MotorControl via sysfs. Also some minor fixes for the InterfaceKit. Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/input/hid-core.c12
-rw-r--r--drivers/usb/misc/Kconfig16
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/phidgetmotorcontrol.c427
4 files changed, 451 insertions, 5 deletions
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index 3305fb6079eb..8f8d4af3f6ef 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -1535,13 +1535,17 @@ void hid_init_reports(struct hid_device *hid)
1535#define USB_VENDOR_ID_GLAB 0x06c2 1535#define USB_VENDOR_ID_GLAB 0x06c2
1536#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 1536#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
1537#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 1537#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
1538#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
1539#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 1538#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
1539#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
1540#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
1541#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
1540#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 1542#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
1543#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
1541 1544
1542#define USB_VENDOR_ID_WISEGROUP 0x0925 1545#define USB_VENDOR_ID_WISEGROUP 0x0925
1543#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 1546#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
1544#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 1547#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
1548#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
1545#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 1549#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
1546 1550
1547#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 1551#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677
@@ -1620,9 +1624,12 @@ static const struct hid_blacklist {
1620 { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, 1624 { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
1621 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, 1625 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
1622 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, 1626 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
1623 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
1624 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, 1627 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
1628 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
1629 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
1630 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
1625 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, 1631 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
1632 { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
1626 { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, 1633 { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
1627 { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, 1634 { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
1628 { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE }, 1635 { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
@@ -1701,6 +1708,7 @@ static const struct hid_blacklist {
1701 { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE }, 1708 { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
1702 { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, 1709 { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
1703 { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, 1710 { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
1711 { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
1704 { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, 1712 { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
1705 1713
1706 { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, 1714 { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 88928a4be805..8affc137d618 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -116,15 +116,25 @@ config USB_CYTHERM
116 module will be called cytherm. 116 module will be called cytherm.
117 117
118config USB_PHIDGETKIT 118config USB_PHIDGETKIT
119 tristate "USB PhidgetKit support" 119 tristate "USB PhidgetInterfaceKit support"
120 depends on USB 120 depends on USB
121 help 121 help
122 Say Y here if you want to connect a PhidgetKit USB device from 122 Say Y here if you want to connect a PhidgetInterfaceKit USB device
123 Phidgets Inc. 123 from Phidgets Inc.
124 124
125 To compile this driver as a module, choose M here: the 125 To compile this driver as a module, choose M here: the
126 module will be called phidgetkit. 126 module will be called phidgetkit.
127 127
128config USB_PHIDGETMOTORCONTROL
129 tristate "USB PhidgetMotorControl support"
130 depends on USB
131 help
132 Say Y here if you want to connect a PhidgetMotorControl USB device
133 from Phidgets Inc.
134
135 To compile this driver as a module, choose M here: the
136 module will be called phidgetmotorcontrol.
137
128config USB_PHIDGETSERVO 138config USB_PHIDGETSERVO
129 tristate "USB PhidgetServo support" 139 tristate "USB PhidgetServo support"
130 depends on USB 140 depends on USB
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 2927260c5812..5f329176451a 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_LD) += ldusb.o
14obj-$(CONFIG_USB_LED) += usbled.o 14obj-$(CONFIG_USB_LED) += usbled.o
15obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o 15obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
16obj-$(CONFIG_USB_PHIDGETKIT) += phidgetkit.o 16obj-$(CONFIG_USB_PHIDGETKIT) += phidgetkit.o
17obj-$(CONFIG_USB_PHIDGETMOTORCONTROL) += phidgetmotorcontrol.o
17obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o 18obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
18obj-$(CONFIG_USB_RIO500) += rio500.o 19obj-$(CONFIG_USB_RIO500) += rio500.o
19obj-$(CONFIG_USB_TEST) += usbtest.o 20obj-$(CONFIG_USB_TEST) += usbtest.o
diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c
new file mode 100644
index 000000000000..2972dc2eb27f
--- /dev/null
+++ b/drivers/usb/misc/phidgetmotorcontrol.c
@@ -0,0 +1,427 @@
1/*
2 * USB Phidget MotorControl driver
3 *
4 * Copyright (C) 2006 Sean Young <sean@mess.org>
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 as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <linux/kernel.h>
13#include <linux/errno.h>
14#include <linux/init.h>
15#include <linux/module.h>
16#include <linux/usb.h>
17
18#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
19#define DRIVER_DESC "USB PhidgetMotorControl Driver"
20
21#define USB_VENDOR_ID_GLAB 0x06c2
22#define USB_DEVICE_ID_MOTORCONTROL 0x0058
23
24#define URB_INT_SIZE 8
25
26struct motorcontrol {
27 struct usb_device *udev;
28 struct usb_interface *intf;
29 u8 inputs[4];
30 s8 desired_speed[2];
31 s8 speed[2];
32 s16 _current[2];
33 s8 acceleration[2];
34 struct urb *irq;
35 unsigned char *data;
36 dma_addr_t data_dma;
37
38 struct work_struct do_notify;
39 unsigned long input_events;
40 unsigned long speed_events;
41 unsigned long exceed_events;
42};
43
44static struct usb_device_id id_table[] = {
45 { USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_MOTORCONTROL) },
46 {}
47};
48MODULE_DEVICE_TABLE(usb, id_table);
49
50static int set_motor(struct motorcontrol *mc, int motor)
51{
52 u8 *buffer;
53 int speed, speed2, acceleration;
54 int retval;
55
56 buffer = kzalloc(8, GFP_KERNEL);
57 if (!buffer) {
58 dev_err(&mc->intf->dev, "%s - out of memory\n", __FUNCTION__);
59 return -ENOMEM;
60 }
61
62 acceleration = mc->acceleration[motor] * 10;
63 /* -127 <= speed <= 127 */
64 speed = (mc->desired_speed[motor] * 127) / 100;
65 /* -0x7300 <= speed2 <= 0x7300 */
66 speed2 = (mc->desired_speed[motor] * 230 * 128) / 100;
67
68 buffer[0] = motor;
69 buffer[1] = speed;
70 buffer[2] = acceleration >> 8;
71 buffer[3] = acceleration;
72 buffer[4] = speed2 >> 8;
73 buffer[5] = speed2;
74
75 retval = usb_control_msg(mc->udev,
76 usb_sndctrlpipe(mc->udev, 0),
77 0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000);
78
79 if (retval != 8)
80 dev_err(&mc->intf->dev, "usb_control_msg returned %d\n",
81 retval);
82 kfree(buffer);
83
84 return retval < 0 ? retval : 0;
85}
86
87static void motorcontrol_irq(struct urb *urb, struct pt_regs *regs)
88{
89 struct motorcontrol *mc = urb->context;
90 unsigned char *buffer = mc->data;
91 int i, level;
92 int status;
93
94 switch (urb->status) {
95 case 0: /* success */
96 break;
97 case -ECONNRESET: /* unlink */
98 case -ENOENT:
99 case -ESHUTDOWN:
100 return;
101 /* -EPIPE: should clear the halt */
102 default: /* error */
103 goto resubmit;
104 }
105
106 /* digital inputs */
107 for (i=0; i<4; i++) {
108 level = (buffer[0] >> i) & 1;
109 if (mc->inputs[i] != level) {
110 mc->inputs[i] = level;
111 set_bit(i, &mc->input_events);
112 }
113 }
114
115 /* motor speed */
116 if (buffer[2] == 0) {
117 for (i=0; i<2; i++) {
118 level = ((s8)buffer[4+i]) * 100 / 127;
119 if (mc->speed[i] != level) {
120 mc->speed[i] = level;
121 set_bit(i, &mc->speed_events);
122 }
123 }
124 } else {
125 int index = buffer[3] & 1;
126
127 level = ((s8)buffer[4] << 8) | buffer[5];
128 level = level * 100 / 29440;
129 if (mc->speed[index] != level) {
130 mc->speed[index] = level;
131 set_bit(index, &mc->speed_events);
132 }
133
134 level = ((s8)buffer[6] << 8) | buffer[7];
135 mc->_current[index] = level * 100 / 1572;
136 }
137
138 if (buffer[1] & 1)
139 set_bit(0, &mc->exceed_events);
140
141 if (buffer[1] & 2)
142 set_bit(1, &mc->exceed_events);
143
144 if (mc->input_events || mc->exceed_events || mc->speed_events)
145 schedule_work(&mc->do_notify);
146
147resubmit:
148 status = usb_submit_urb(urb, SLAB_ATOMIC);
149 if (status)
150 dev_err(&mc->intf->dev,
151 "can't resubmit intr, %s-%s/motorcontrol0, status %d",
152 mc->udev->bus->bus_name,
153 mc->udev->devpath, status);
154}
155
156static void do_notify(void *data)
157{
158 struct motorcontrol *mc = data;
159 int i;
160 char sysfs_file[8];
161
162 for (i=0; i<4; i++) {
163 if (test_and_clear_bit(i, &mc->input_events)) {
164 sprintf(sysfs_file, "input%d", i);
165 sysfs_notify(&mc->intf->dev.kobj, NULL, sysfs_file);
166 }
167 }
168
169 for (i=0; i<2; i++) {
170 if (test_and_clear_bit(i, &mc->speed_events)) {
171 sprintf(sysfs_file, "speed%d", i);
172 sysfs_notify(&mc->intf->dev.kobj, NULL, sysfs_file);
173 }
174 }
175
176 for (i=0; i<2; i++) {
177 if (test_and_clear_bit(i, &mc->exceed_events))
178 dev_warn(&mc->intf->dev,
179 "motor #%d exceeds 1.5 Amp current limit\n", i);
180 }
181}
182
183#define show_set_speed(value) \
184static ssize_t set_speed##value(struct device *dev, \
185 struct device_attribute *attr, const char *buf, size_t count) \
186{ \
187 struct usb_interface *intf = to_usb_interface(dev); \
188 struct motorcontrol *mc = usb_get_intfdata(intf); \
189 int speed; \
190 int retval; \
191 \
192 if (sscanf(buf, "%d", &speed) < 1) \
193 return -EINVAL; \
194 \
195 if (speed < -100 || speed > 100) \
196 return -EINVAL; \
197 \
198 mc->desired_speed[value] = speed; \
199 \
200 retval = set_motor(mc, value); \
201 \
202 return retval ? retval : count; \
203} \
204 \
205static ssize_t show_speed##value(struct device *dev, \
206 struct device_attribute *attr, char *buf) \
207{ \
208 struct usb_interface *intf = to_usb_interface(dev); \
209 struct motorcontrol *mc = usb_get_intfdata(intf); \
210 \
211 return sprintf(buf, "%d\n", mc->speed[value]); \
212} \
213static DEVICE_ATTR(speed##value, S_IWUGO | S_IRUGO, \
214 show_speed##value, set_speed##value);
215show_set_speed(0);
216show_set_speed(1);
217
218#define show_set_acceleration(value) \
219static ssize_t set_acceleration##value(struct device *dev, \
220 struct device_attribute *attr, const char *buf, size_t count) \
221{ \
222 struct usb_interface *intf = to_usb_interface(dev); \
223 struct motorcontrol *mc = usb_get_intfdata(intf); \
224 int acceleration; \
225 int retval; \
226 \
227 if (sscanf(buf, "%d", &acceleration) < 1) \
228 return -EINVAL; \
229 \
230 if (acceleration < 0 || acceleration > 100) \
231 return -EINVAL; \
232 \
233 mc->acceleration[value] = acceleration; \
234 \
235 retval = set_motor(mc, value); \
236 \
237 return retval ? retval : count; \
238} \
239 \
240static ssize_t show_acceleration##value(struct device *dev, \
241 struct device_attribute *attr, char *buf) \
242{ \
243 struct usb_interface *intf = to_usb_interface(dev); \
244 struct motorcontrol *mc = usb_get_intfdata(intf); \
245 \
246 return sprintf(buf, "%d\n", mc->acceleration[value]); \
247} \
248static DEVICE_ATTR(acceleration##value, S_IWUGO | S_IRUGO, \
249 show_acceleration##value, set_acceleration##value);
250show_set_acceleration(0);
251show_set_acceleration(1);
252
253#define show_current(value) \
254static ssize_t show_current##value(struct device *dev, \
255 struct device_attribute *attr, char *buf) \
256{ \
257 struct usb_interface *intf = to_usb_interface(dev); \
258 struct motorcontrol *mc = usb_get_intfdata(intf); \
259 \
260 return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \
261} \
262static DEVICE_ATTR(current##value, S_IRUGO, show_current##value, NULL);
263
264show_current(0);
265show_current(1);
266
267#define show_input(value) \
268static ssize_t show_input##value(struct device *dev, \
269 struct device_attribute *attr, char *buf) \
270{ \
271 struct usb_interface *intf = to_usb_interface(dev); \
272 struct motorcontrol *mc = usb_get_intfdata(intf); \
273 \
274 return sprintf(buf, "%d\n", (int)mc->inputs[value]); \
275} \
276static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL);
277
278show_input(0);
279show_input(1);
280show_input(2);
281show_input(3);
282
283static int motorcontrol_probe(struct usb_interface *intf, const struct usb_device_id *id)
284{
285 struct usb_device *dev = interface_to_usbdev(intf);
286 struct usb_host_interface *interface;
287 struct usb_endpoint_descriptor *endpoint;
288 struct motorcontrol *mc;
289 int pipe, maxp, rc = -ENOMEM;
290
291 interface = intf->cur_altsetting;
292 if (interface->desc.bNumEndpoints != 1)
293 return -ENODEV;
294
295 endpoint = &interface->endpoint[0].desc;
296 if (!(endpoint->bEndpointAddress & 0x80))
297 return -ENODEV;
298
299 /*
300 * bmAttributes
301 */
302 pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
303 maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
304
305 mc = kzalloc(sizeof(*mc), GFP_KERNEL);
306 if (!mc)
307 goto out;
308
309 mc->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &mc->data_dma);
310 if (!mc->data)
311 goto out;
312
313 mc->irq = usb_alloc_urb(0, GFP_KERNEL);
314 if (!mc->irq)
315 goto out;
316
317 mc->udev = usb_get_dev(dev);
318 mc->intf = intf;
319 mc->acceleration[0] = mc->acceleration[1] = 10;
320 INIT_WORK(&mc->do_notify, do_notify, mc);
321 usb_fill_int_urb(mc->irq, mc->udev, pipe, mc->data,
322 maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
323 motorcontrol_irq, mc, endpoint->bInterval);
324 mc->irq->transfer_dma = mc->data_dma;
325 mc->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
326
327 usb_set_intfdata(intf, mc);
328
329 if (usb_submit_urb(mc->irq, GFP_KERNEL)) {
330 rc = -EIO;
331 goto out;
332 }
333
334 device_create_file(&intf->dev, &dev_attr_input0);
335 device_create_file(&intf->dev, &dev_attr_input1);
336 device_create_file(&intf->dev, &dev_attr_input2);
337 device_create_file(&intf->dev, &dev_attr_input3);
338
339 device_create_file(&intf->dev, &dev_attr_speed0);
340 device_create_file(&intf->dev, &dev_attr_speed1);
341
342 device_create_file(&intf->dev, &dev_attr_acceleration0);
343 device_create_file(&intf->dev, &dev_attr_acceleration1);
344
345 device_create_file(&intf->dev, &dev_attr_current0);
346 device_create_file(&intf->dev, &dev_attr_current1);
347
348 dev_info(&intf->dev, "USB Phidget MotorControl attached\n");
349
350 return 0;
351
352out:
353 if (mc) {
354 if (mc->irq)
355 usb_free_urb(mc->irq);
356 if (mc->data)
357 usb_buffer_free(dev, URB_INT_SIZE, mc->data, mc->data_dma);
358 kfree(mc);
359 }
360
361 return rc;
362}
363
364static void motorcontrol_disconnect(struct usb_interface *interface)
365{
366 struct motorcontrol *mc;
367
368 mc = usb_get_intfdata(interface);
369 usb_set_intfdata(interface, NULL);
370 if (!mc)
371 return;
372
373 usb_kill_urb(mc->irq);
374 usb_free_urb(mc->irq);
375 usb_buffer_free(mc->udev, URB_INT_SIZE, mc->data, mc->data_dma);
376
377 cancel_delayed_work(&mc->do_notify);
378
379 device_remove_file(&interface->dev, &dev_attr_input0);
380 device_remove_file(&interface->dev, &dev_attr_input1);
381 device_remove_file(&interface->dev, &dev_attr_input2);
382 device_remove_file(&interface->dev, &dev_attr_input3);
383
384 device_remove_file(&interface->dev, &dev_attr_speed0);
385 device_remove_file(&interface->dev, &dev_attr_speed1);
386
387 device_remove_file(&interface->dev, &dev_attr_acceleration0);
388 device_remove_file(&interface->dev, &dev_attr_acceleration1);
389
390 device_remove_file(&interface->dev, &dev_attr_current0);
391 device_remove_file(&interface->dev, &dev_attr_current1);
392
393 dev_info(&interface->dev, "USB Phidget MotorControl disconnected\n");
394
395 usb_put_dev(mc->udev);
396 kfree(mc);
397}
398
399static struct usb_driver motorcontrol_driver = {
400 .name = "phidgetmotorcontrol",
401 .probe = motorcontrol_probe,
402 .disconnect = motorcontrol_disconnect,
403 .id_table = id_table
404};
405
406static int __init motorcontrol_init(void)
407{
408 int retval = 0;
409
410 retval = usb_register(&motorcontrol_driver);
411 if (retval)
412 err("usb_register failed. Error number %d", retval);
413
414 return retval;
415}
416
417static void __exit motorcontrol_exit(void)
418{
419 usb_deregister(&motorcontrol_driver);
420}
421
422module_init(motorcontrol_init);
423module_exit(motorcontrol_exit);
424
425MODULE_AUTHOR(DRIVER_AUTHOR);
426MODULE_DESCRIPTION(DRIVER_DESC);
427MODULE_LICENSE("GPL");