aboutsummaryrefslogtreecommitdiffstats
path: root/usb/usb-serial-dynamic-id.patch
blob: d249887fb1d070c7829ba7abe08e4646898ed416 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
From foo@baz.org Thu Dec 15 13:04:03 2005
Date: Thu, 15 Dec 2005 12:58:42 -0800
From: Greg Kroah-Hartman <gregkh@suse.de>
Subject: USB serial: add dynamic id support to usb-serial core

This adds support for dynamic usb ids to the usb serial core.  The file
"new_id" will show up under the usb serial driver, not the usb driver
associated with the usb-serial driver (yeah, it can be a bit confusing
at first glance...)

This patch also modifies the USB core to allow the usb-serial core to
reuse much of the dynamic id logic.


Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>


 drivers/usb/core/driver.c       |   37 ++++++++++++++++-------------
 drivers/usb/serial/bus.c        |   50 ++++++++++++++++++++++++++++++++++++++++
 drivers/usb/serial/usb-serial.c |   43 +++++++++++++++++++++++++++++-----
 drivers/usb/serial/usb-serial.h |    2 +
 include/linux/usb.h             |   13 ++++++++++
 5 files changed, 122 insertions(+), 23 deletions(-)

---
--- gregkh-2.6.orig/drivers/usb/core/driver.c
+++ gregkh-2.6/drivers/usb/core/driver.c
@@ -26,15 +26,6 @@
 #include "hcd.h"
 #include "usb.h"
 
-static int usb_match_one_id(struct usb_interface *interface,
-			    const struct usb_device_id *id);
-
-struct usb_dynid {
-	struct list_head node;
-	struct usb_device_id id;
-};
-
-
 static int generic_probe(struct device *dev)
 {
 	return 0;
@@ -72,10 +63,10 @@ int usb_generic_driver_data;
  * Adds a new dynamic USBdevice ID to this driver,
  * and cause the driver to probe for all devices again.
  */
-static ssize_t store_new_id(struct device_driver *driver,
-			    const char *buf, size_t count)
+ssize_t usb_store_new_id(struct usb_dynids *dynids,
+			 struct device_driver *driver,
+			 const char *buf, size_t count)
 {
-	struct usb_driver *usb_drv = to_usb_driver(driver);
 	struct usb_dynid *dynid;
 	u32 idVendor = 0;
 	u32 idProduct = 0;
@@ -84,6 +75,7 @@ static ssize_t store_new_id(struct devic
 	fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
 	if (fields < 2)
 		return -EINVAL;
+	printk("GREG: idVendor=%x idProduct=%x\n", idVendor, idProduct);
 
 	dynid = kzalloc(sizeof(*dynid), GFP_KERNEL);
 	if (!dynid)
@@ -94,9 +86,9 @@ static ssize_t store_new_id(struct devic
 	dynid->id.idProduct = idProduct;
 	dynid->id.match_flags = USB_DEVICE_ID_MATCH_DEVICE;
 
-	spin_lock(&usb_drv->dynids.lock);
-	list_add_tail(&usb_drv->dynids.list, &dynid->node);
-	spin_unlock(&usb_drv->dynids.lock);
+	spin_lock(&dynids->lock);
+	list_add_tail(&dynids->list, &dynid->node);
+	spin_unlock(&dynids->lock);
 
 	if (get_driver(driver)) {
 		driver_attach(driver);
@@ -105,6 +97,15 @@ static ssize_t store_new_id(struct devic
 
 	return count;
 }
+EXPORT_SYMBOL_GPL(usb_store_new_id);
+
+static ssize_t store_new_id(struct device_driver *driver,
+			    const char *buf, size_t count)
+{
+	struct usb_driver *usb_drv = to_usb_driver(driver);
+
+	return usb_store_new_id(&usb_drv->dynids, driver, buf, count);
+}
 static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
 
 static int usb_create_newid_file(struct usb_driver *usb_drv)
@@ -239,8 +240,8 @@ static int usb_unbind_interface(struct d
 }
 
 /* returns 0 if no match, 1 if match */
-static int usb_match_one_id(struct usb_interface *interface,
-			    const struct usb_device_id *id)
+int usb_match_one_id(struct usb_interface *interface,
+		     const struct usb_device_id *id)
 {
 	struct usb_host_interface *intf;
 	struct usb_device *dev;
@@ -296,6 +297,8 @@ static int usb_match_one_id(struct usb_i
 
 	return 1;
 }
+EXPORT_SYMBOL(usb_match_one_id);
+
 /**
  * usb_match_id - find first usb_device_id matching device or interface
  * @interface: the interface of interest
--- gregkh-2.6.orig/drivers/usb/serial/bus.c
+++ gregkh-2.6/drivers/usb/serial/bus.c
@@ -103,11 +103,57 @@ exit:
 	return retval;
 }
 
+#ifdef CONFIG_HOTPLUG
+static ssize_t store_new_id(struct device_driver *driver,
+			    const char *buf, size_t count)
+{
+	struct usb_serial_driver *usb_drv = to_usb_serial_driver(driver);
+	ssize_t retval = usb_store_new_id(&usb_drv->dynids, driver, buf, count);
+
+	if (retval >= 0) {
+		/* kick the usb-serial main driver */
+		struct device_driver *driver = &usb_serial_driver.driver;
+		if (get_driver(driver)) {
+			driver_attach(driver);
+			put_driver(driver);
+		}
+	}
+	return retval;
+}
+
+static struct driver_attribute drv_attrs[] = {
+	__ATTR(new_id, S_IWUSR, NULL, store_new_id),
+	__ATTR_NULL,
+};
+
+static void free_dynids(struct usb_serial_driver *drv)
+{
+	struct usb_dynid *dynid, *n;
+
+	printk("GREG: %s\n", __FUNCTION__);
+	spin_lock(&drv->dynids.lock);
+	list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) {
+		list_del(&dynid->node);
+		kfree(dynid);
+	}
+	spin_unlock(&drv->dynids.lock);
+}
+
+#else
+static struct driver_attribute drv_attrs[] = {
+	__ATTR_NULL,
+};
+static inline void free_dynids(struct usb_driver *drv)
+{
+}
+#endif
+
 struct bus_type usb_serial_bus_type = {
 	.name =		"usb-serial",
 	.match =	usb_serial_device_match,
 	.probe =	usb_serial_device_probe,
 	.remove =	usb_serial_device_remove,
+	.drv_attrs = 	drv_attrs,
 };
 
 int usb_serial_bus_register(struct usb_serial_driver *driver)
@@ -115,6 +161,9 @@ int usb_serial_bus_register(struct usb_s
 	int retval;
 
 	driver->driver.bus = &usb_serial_bus_type;
+	spin_lock_init(&driver->dynids.lock);
+	INIT_LIST_HEAD(&driver->dynids.list);
+
 	retval = driver_register(&driver->driver);
 
 	return retval;
@@ -122,6 +171,7 @@ int usb_serial_bus_register(struct usb_s
 
 void usb_serial_bus_deregister(struct usb_serial_driver *driver)
 {
+	free_dynids(driver);
 	driver_unregister(&driver->driver);
 }
 
--- gregkh-2.6.orig/drivers/usb/serial/usb-serial.c
+++ gregkh-2.6/drivers/usb/serial/usb-serial.c
@@ -43,7 +43,7 @@
 static void port_free(struct usb_serial_port *port);
 
 /* Driver structure we register with the USB core */
-static struct usb_driver usb_serial_driver = {
+struct usb_driver usb_serial_driver = {
 	.name =		"usbserial",
 	.probe =	usb_serial_probe,
 	.disconnect =	usb_serial_disconnect,
@@ -593,6 +593,39 @@ static struct usb_serial * create_serial
 	return serial;
 }
 
+static const struct usb_device_id *match_dynamic_id(struct usb_interface *intf,
+						    struct usb_serial_driver *drv)
+{
+	struct usb_dynid *dynid;
+
+	spin_lock(&drv->dynids.lock);
+	list_for_each_entry(dynid, &drv->dynids.list, node) {
+		if (usb_match_one_id(intf, &dynid->id)) {
+			spin_unlock(&drv->dynids.lock);
+			return &dynid->id;
+		}
+	}
+	spin_unlock(&drv->dynids.lock);
+	return NULL;
+}
+
+static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv,
+						struct usb_interface *intf)
+{
+	const struct usb_device_id *id;
+
+	id = usb_match_id(intf, drv->id_table);
+	if (id) {
+		dbg("static descriptor matches");
+		goto exit;
+	}
+	id = match_dynamic_id(intf, drv);
+	if (id)
+		dbg("dynamic descriptor matches");
+exit:
+	return id;
+}
+
 static struct usb_serial_driver *search_serial_device(struct usb_interface *iface)
 {
 	struct list_head *p;
@@ -602,11 +635,9 @@ static struct usb_serial_driver *search_
 	/* Check if the usb id matches a known device */
 	list_for_each(p, &usb_serial_driver_list) {
 		t = list_entry(p, struct usb_serial_driver, driver_list);
-		id = usb_match_id(iface, t->id_table);
-		if (id != NULL) {
-			dbg("descriptor matches");
+		id = get_iface_id(t, iface);
+		if (id)
 			return t;
-		}
 	}
 
 	return NULL;
@@ -658,7 +689,7 @@ int usb_serial_probe(struct usb_interfac
 			return -EIO;
 		}
 
-		id = usb_match_id(interface, type->id_table);
+		id = get_iface_id(type, interface);
 		retval = type->probe(serial, id);
 		module_put(type->driver.owner);
 
--- gregkh-2.6.orig/drivers/usb/serial/usb-serial.h
+++ gregkh-2.6/drivers/usb/serial/usb-serial.h
@@ -202,6 +202,7 @@ struct usb_serial_driver {
 
 	struct list_head	driver_list;
 	struct device_driver	driver;
+        struct usb_dynids	dynids;
 
 	int (*probe) (struct usb_serial *serial, const struct usb_device_id *id);
 	int (*attach) (struct usb_serial *serial);
@@ -272,6 +273,7 @@ extern int usb_serial_bus_register (stru
 extern void usb_serial_bus_deregister (struct usb_serial_driver *device);
 
 extern struct usb_serial_driver usb_serial_generic_device;
+extern struct usb_driver usb_serial_driver;
 extern struct bus_type usb_serial_bus_type;
 extern struct tty_driver *usb_serial_tty_driver;
 
--- gregkh-2.6.orig/include/linux/usb.h
+++ gregkh-2.6/include/linux/usb.h
@@ -421,6 +421,8 @@ extern void usb_driver_release_interface
 			struct usb_interface *iface);
 const struct usb_device_id *usb_match_id(struct usb_interface *interface,
 					 const struct usb_device_id *id);
+extern int usb_match_one_id(struct usb_interface *interface,
+			    const struct usb_device_id *id);
 
 extern struct usb_interface *usb_find_interface(struct usb_driver *drv,
 		int minor);
@@ -535,11 +537,21 @@ static inline int usb_make_path (struct 
 
 /* ----------------------------------------------------------------------- */
 
+/* Stuff for dynamic usb ids */
 struct usb_dynids {
 	spinlock_t lock;
 	struct list_head list;
 };
 
+struct usb_dynid {
+	struct list_head node;
+	struct usb_device_id id;
+};
+
+extern ssize_t usb_store_new_id(struct usb_dynids *dynids,
+				struct device_driver *driver,
+				const char *buf, size_t count);
+
 /**
  * struct usb_driver - identifies USB driver to usbcore
  * @name: The driver name should be unique among USB drivers,
@@ -1203,6 +1215,7 @@ usb_maxpacket(struct usb_device *udev, i
 extern void usb_register_notify(struct notifier_block *nb);
 extern void usb_unregister_notify(struct notifier_block *nb);
 
+
 #ifdef DEBUG
 #define dbg(format, arg...) printk(KERN_DEBUG "%s: " format "\n" , \
 	__FILE__ , ## arg)