aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorstephane duverger <stephane.duverger@gmail.com>2010-07-12 12:37:53 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-08-10 17:35:39 -0400
commitf6c826a90055dd05905982f7a3f60e0bcaa0434e (patch)
tree617dfc7599c415a97808485f9b385810cd0ad667 /drivers/usb
parentfd63b10ba33f272308d9f976a40f2cd064d8b21b (diff)
USB: EHCI Debug Port Device Gadget
This is a patch that implements an USB EHCI Debug Device using the Gadget API. This patch applies to a 2.6.35-rc3 kernel. The gadget needs a compliant usb controller that forwards the USB_DEVICE_DEBUG_MODE feature to its gadget. The gadget provides two configuration modes, one that only printk() the received data, and one that exposes a serial device to userland (/dev/ttyGSxxx). The gadget has been tested on an IGEPv2 board running a 2.6.35-rc1 kernel. The debug port was fed on the host side by a 2.6.34 kernel. Signed-off-by: Stephane Duverger <stephane.duverger@gmail.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/gadget/Kconfig28
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/dbgp.c434
3 files changed, 464 insertions, 0 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index dd3b25101858..cd27f9bde2c8 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -915,6 +915,34 @@ config USB_G_HID
915 Say "y" to link the driver statically, or "m" to build a 915 Say "y" to link the driver statically, or "m" to build a
916 dynamically linked module called "g_hid". 916 dynamically linked module called "g_hid".
917 917
918config USB_G_DBGP
919 tristate "EHCI Debug Device Gadget"
920 help
921 This gadget emulates an EHCI Debug device. This is useful when you want
922 to interact with an EHCI Debug Port.
923
924 Say "y" to link the driver statically, or "m" to build a
925 dynamically linked module called "g_dbgp".
926
927if USB_G_DBGP
928choice
929 prompt "EHCI Debug Device mode"
930 default USB_G_DBGP_SERIAL
931
932config USB_G_DBGP_PRINTK
933 depends on USB_G_DBGP
934 bool "printk"
935 help
936 Directly printk() received data. No interaction.
937
938config USB_G_DBGP_SERIAL
939 depends on USB_G_DBGP
940 bool "serial"
941 help
942 Userland can interact using /dev/ttyGSxxx.
943endchoice
944endif
945
918# put drivers that need isochronous transfer support (for audio 946# put drivers that need isochronous transfer support (for audio
919# or video class gadget drivers), or specific hardware, here. 947# or video class gadget drivers), or specific hardware, here.
920config USB_G_WEBCAM 948config USB_G_WEBCAM
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 9bcde110feb1..397b892e90e2 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -44,6 +44,7 @@ g_printer-objs := printer.o
44g_cdc-objs := cdc2.o 44g_cdc-objs := cdc2.o
45g_multi-objs := multi.o 45g_multi-objs := multi.o
46g_hid-objs := hid.o 46g_hid-objs := hid.o
47g_dbgp-objs := dbgp.o
47g_nokia-objs := nokia.o 48g_nokia-objs := nokia.o
48g_webcam-objs := webcam.o 49g_webcam-objs := webcam.o
49 50
@@ -60,6 +61,7 @@ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
60obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o 61obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
61obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o 62obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
62obj-$(CONFIG_USB_G_HID) += g_hid.o 63obj-$(CONFIG_USB_G_HID) += g_hid.o
64obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
63obj-$(CONFIG_USB_G_MULTI) += g_multi.o 65obj-$(CONFIG_USB_G_MULTI) += g_multi.o
64obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o 66obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
65obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o 67obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c
new file mode 100644
index 000000000000..0ed50a2c0a36
--- /dev/null
+++ b/drivers/usb/gadget/dbgp.c
@@ -0,0 +1,434 @@
1/*
2 * dbgp.c -- EHCI Debug Port device gadget
3 *
4 * Copyright (C) 2010 Stephane Duverger
5 *
6 * Released under the GPLv2.
7 *
8 */
9
10/* verbose messages */
11#include <linux/kernel.h>
12#include <linux/device.h>
13#include <linux/usb/ch9.h>
14#include <linux/usb/gadget.h>
15
16/* See comments in "zero.c" */
17#include "epautoconf.c"
18
19#ifdef CONFIG_USB_G_DBGP_SERIAL
20#include "u_serial.c"
21#endif
22
23#define DRIVER_VENDOR_ID 0x0525 /* NetChip */
24#define DRIVER_PRODUCT_ID 0xc0de /* undefined */
25
26#define USB_DEBUG_MAX_PACKET_SIZE 8
27#define DBGP_REQ_EP0_LEN 128
28#define DBGP_REQ_LEN 512
29
30static struct dbgp {
31 struct usb_gadget *gadget;
32 struct usb_request *req;
33 struct usb_ep *i_ep;
34 struct usb_ep *o_ep;
35#ifdef CONFIG_USB_G_DBGP_SERIAL
36 struct gserial *serial;
37#endif
38} dbgp;
39
40static struct usb_device_descriptor device_desc = {
41 .bLength = sizeof device_desc,
42 .bDescriptorType = USB_DT_DEVICE,
43 .bcdUSB = __constant_cpu_to_le16(0x0200),
44 .bDeviceClass = USB_CLASS_VENDOR_SPEC,
45 .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID),
46 .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID),
47 .bNumConfigurations = 1,
48};
49
50static struct usb_debug_descriptor dbg_desc = {
51 .bLength = sizeof dbg_desc,
52 .bDescriptorType = USB_DT_DEBUG,
53};
54
55static struct usb_endpoint_descriptor i_desc = {
56 .bLength = USB_DT_ENDPOINT_SIZE,
57 .bDescriptorType = USB_DT_ENDPOINT,
58 .bmAttributes = USB_ENDPOINT_XFER_BULK,
59 .bEndpointAddress = USB_DIR_IN,
60};
61
62static struct usb_endpoint_descriptor o_desc = {
63 .bLength = USB_DT_ENDPOINT_SIZE,
64 .bDescriptorType = USB_DT_ENDPOINT,
65 .bmAttributes = USB_ENDPOINT_XFER_BULK,
66 .bEndpointAddress = USB_DIR_OUT,
67};
68
69#ifdef CONFIG_USB_G_DBGP_PRINTK
70static int dbgp_consume(char *buf, unsigned len)
71{
72 char c;
73
74 if (!len)
75 return 0;
76
77 c = buf[len-1];
78 if (c != 0)
79 buf[len-1] = 0;
80
81 printk(KERN_NOTICE "%s%c", buf, c);
82 return 0;
83}
84
85static void __disable_ep(struct usb_ep *ep)
86{
87 if (ep && ep->driver_data == dbgp.gadget) {
88 usb_ep_disable(ep);
89 ep->driver_data = NULL;
90 }
91}
92
93static void dbgp_disable_ep(void)
94{
95 __disable_ep(dbgp.i_ep);
96 __disable_ep(dbgp.o_ep);
97}
98
99static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
100{
101 int stp;
102 int err = 0;
103 int status = req->status;
104
105 if (ep == dbgp.i_ep) {
106 stp = 1;
107 goto fail;
108 }
109
110 if (status != 0) {
111 stp = 2;
112 goto release_req;
113 }
114
115 dbgp_consume(req->buf, req->actual);
116
117 req->length = DBGP_REQ_LEN;
118 err = usb_ep_queue(ep, req, GFP_ATOMIC);
119 if (err < 0) {
120 stp = 3;
121 goto release_req;
122 }
123
124 return;
125
126release_req:
127 kfree(req->buf);
128 usb_ep_free_request(dbgp.o_ep, req);
129 dbgp_disable_ep();
130fail:
131 dev_dbg(&dbgp.gadget->dev,
132 "complete: failure (%d:%d) ==> %d\n", stp, err, status);
133}
134
135static int dbgp_enable_ep_req(struct usb_ep *ep)
136{
137 int err, stp;
138 struct usb_request *req;
139
140 req = usb_ep_alloc_request(ep, GFP_KERNEL);
141 if (!req) {
142 err = -ENOMEM;
143 stp = 1;
144 goto fail_1;
145 }
146
147 req->buf = kmalloc(DBGP_REQ_LEN, GFP_KERNEL);
148 if (!req->buf) {
149 err = -ENOMEM;
150 stp = 2;
151 goto fail_2;
152 }
153
154 req->complete = dbgp_complete;
155 req->length = DBGP_REQ_LEN;
156 err = usb_ep_queue(ep, req, GFP_ATOMIC);
157 if (err < 0) {
158 stp = 3;
159 goto fail_3;
160 }
161
162 return 0;
163
164fail_3:
165 kfree(req->buf);
166fail_2:
167 usb_ep_free_request(dbgp.o_ep, req);
168fail_1:
169 dev_dbg(&dbgp.gadget->dev,
170 "enable ep req: failure (%d:%d)\n", stp, err);
171 return err;
172}
173
174static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
175{
176 int err = usb_ep_enable(ep, desc);
177 ep->driver_data = dbgp.gadget;
178 return err;
179}
180
181static int dbgp_enable_ep(void)
182{
183 int err, stp;
184
185 err = __enable_ep(dbgp.i_ep, &i_desc);
186 if (err < 0) {
187 stp = 1;
188 goto fail_1;
189 }
190
191 err = __enable_ep(dbgp.o_ep, &o_desc);
192 if (err < 0) {
193 stp = 2;
194 goto fail_2;
195 }
196
197 err = dbgp_enable_ep_req(dbgp.o_ep);
198 if (err < 0) {
199 stp = 3;
200 goto fail_3;
201 }
202
203 return 0;
204
205fail_3:
206 __disable_ep(dbgp.o_ep);
207fail_2:
208 __disable_ep(dbgp.i_ep);
209fail_1:
210 dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
211 return err;
212}
213#endif
214
215static void dbgp_disconnect(struct usb_gadget *gadget)
216{
217#ifdef CONFIG_USB_G_DBGP_PRINTK
218 dbgp_disable_ep();
219#else
220 gserial_disconnect(dbgp.serial);
221#endif
222}
223
224static void dbgp_unbind(struct usb_gadget *gadget)
225{
226#ifdef CONFIG_USB_G_DBGP_SERIAL
227 kfree(dbgp.serial);
228#endif
229 if (dbgp.req) {
230 kfree(dbgp.req->buf);
231 usb_ep_free_request(gadget->ep0, dbgp.req);
232 }
233
234 gadget->ep0->driver_data = NULL;
235}
236
237static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
238{
239 int stp;
240
241 usb_ep_autoconfig_reset(gadget);
242
243 dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
244 if (!dbgp.i_ep) {
245 stp = 1;
246 goto fail_1;
247 }
248
249 dbgp.i_ep->driver_data = gadget;
250 i_desc.wMaxPacketSize =
251 __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
252
253 dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
254 if (!dbgp.o_ep) {
255 dbgp.i_ep->driver_data = NULL;
256 stp = 2;
257 goto fail_2;
258 }
259
260 dbgp.o_ep->driver_data = gadget;
261 o_desc.wMaxPacketSize =
262 __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
263
264 dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f;
265 dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f;
266
267#ifdef CONFIG_USB_G_DBGP_SERIAL
268 dbgp.serial->in = dbgp.i_ep;
269 dbgp.serial->out = dbgp.o_ep;
270
271 dbgp.serial->in_desc = &i_desc;
272 dbgp.serial->out_desc = &o_desc;
273
274 if (gserial_setup(gadget, 1) < 0) {
275 stp = 3;
276 goto fail_3;
277 }
278
279 return 0;
280
281fail_3:
282 dbgp.o_ep->driver_data = NULL;
283#else
284 return 0;
285#endif
286fail_2:
287 dbgp.i_ep->driver_data = NULL;
288fail_1:
289 dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
290 return -ENODEV;
291}
292
293static int __init dbgp_bind(struct usb_gadget *gadget)
294{
295 int err, stp;
296
297 dbgp.gadget = gadget;
298
299 dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
300 if (!dbgp.req) {
301 err = -ENOMEM;
302 stp = 1;
303 goto fail;
304 }
305
306 dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
307 if (!dbgp.req->buf) {
308 err = -ENOMEM;
309 stp = 2;
310 goto fail;
311 }
312
313 dbgp.req->length = DBGP_REQ_EP0_LEN;
314 gadget->ep0->driver_data = gadget;
315
316#ifdef CONFIG_USB_G_DBGP_SERIAL
317 dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
318 if (!dbgp.serial) {
319 stp = 3;
320 err = -ENOMEM;
321 goto fail;
322 }
323#endif
324 err = dbgp_configure_endpoints(gadget);
325 if (err < 0) {
326 stp = 4;
327 goto fail;
328 }
329
330 dev_dbg(&dbgp.gadget->dev, "bind: success\n");
331 return 0;
332
333fail:
334 dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
335 dbgp_unbind(gadget);
336 return err;
337}
338
339static void dbgp_setup_complete(struct usb_ep *ep,
340 struct usb_request *req)
341{
342 dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
343 req->status, req->actual, req->length);
344}
345
346static int dbgp_setup(struct usb_gadget *gadget,
347 const struct usb_ctrlrequest *ctrl)
348{
349 struct usb_request *req = dbgp.req;
350 u8 request = ctrl->bRequest;
351 u16 value = le16_to_cpu(ctrl->wValue);
352 u16 length = le16_to_cpu(ctrl->wLength);
353 int err = 0;
354 void *data;
355 u16 len;
356
357 gadget->ep0->driver_data = gadget;
358
359 if (request == USB_REQ_GET_DESCRIPTOR) {
360 switch (value>>8) {
361 case USB_DT_DEVICE:
362 dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
363 len = sizeof device_desc;
364 data = &device_desc;
365 break;
366 case USB_DT_DEBUG:
367 dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
368 len = sizeof dbg_desc;
369 data = &dbg_desc;
370 break;
371 default:
372 goto fail;
373 }
374 } else if (request == USB_REQ_SET_FEATURE &&
375 value == USB_DEVICE_DEBUG_MODE) {
376 len = 0;
377 data = NULL;
378 dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
379#ifdef CONFIG_USB_G_DBGP_PRINTK
380 err = dbgp_enable_ep();
381#else
382 err = gserial_connect(dbgp.serial, 0);
383#endif
384 if (err < 0)
385 goto fail;
386 } else
387 goto fail;
388
389 if (len >= 0) {
390 req->length = min(length, len);
391 req->zero = len < req->length;
392 if (data && req->length)
393 memcpy(req->buf, data, req->length);
394
395 req->complete = dbgp_setup_complete;
396 return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
397 }
398
399fail:
400 dev_dbg(&dbgp.gadget->dev,
401 "setup: failure req %x v %x\n", request, value);
402 return err;
403}
404
405static struct usb_gadget_driver dbgp_driver = {
406 .function = "dbgp",
407 .speed = USB_SPEED_HIGH,
408 .bind = dbgp_bind,
409 .unbind = dbgp_unbind,
410 .setup = dbgp_setup,
411 .disconnect = dbgp_disconnect,
412 .driver = {
413 .owner = THIS_MODULE,
414 .name = "dbgp"
415 },
416};
417
418static int __init dbgp_init(void)
419{
420 return usb_gadget_register_driver(&dbgp_driver);
421}
422
423static void __exit dbgp_exit(void)
424{
425 usb_gadget_unregister_driver(&dbgp_driver);
426#ifdef CONFIG_USB_G_DBGP_SERIAL
427 gserial_cleanup();
428#endif
429}
430
431MODULE_AUTHOR("Stephane Duverger");
432MODULE_LICENSE("GPL");
433module_init(dbgp_init);
434module_exit(dbgp_exit);