1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * dbgp.c -- EHCI Debug Port device gadget |
4 | * |
5 | * Copyright (C) 2010 Stephane Duverger |
6 | * |
7 | * Released under the GPLv2. |
8 | */ |
9 | |
10 | /* verbose messages */ |
11 | #include <linux/kernel.h> |
12 | #include <linux/device.h> |
13 | #include <linux/module.h> |
14 | #include <linux/usb/ch9.h> |
15 | #include <linux/usb/gadget.h> |
16 | |
17 | #include "u_serial.h" |
18 | |
19 | #define DRIVER_VENDOR_ID 0x0525 /* NetChip */ |
20 | #define DRIVER_PRODUCT_ID 0xc0de /* undefined */ |
21 | |
22 | #define USB_DEBUG_MAX_PACKET_SIZE 8 |
23 | #define DBGP_REQ_EP0_LEN 128 |
24 | #define DBGP_REQ_LEN 512 |
25 | |
26 | static struct dbgp { |
27 | struct usb_gadget *gadget; |
28 | struct usb_request *req; |
29 | struct usb_ep *i_ep; |
30 | struct usb_ep *o_ep; |
31 | #ifdef CONFIG_USB_G_DBGP_SERIAL |
32 | struct gserial *serial; |
33 | #endif |
34 | } dbgp; |
35 | |
36 | static struct usb_device_descriptor device_desc = { |
37 | .bLength = sizeof device_desc, |
38 | .bDescriptorType = USB_DT_DEVICE, |
39 | .bcdUSB = cpu_to_le16(0x0200), |
40 | .bDeviceClass = USB_CLASS_VENDOR_SPEC, |
41 | .idVendor = cpu_to_le16(DRIVER_VENDOR_ID), |
42 | .idProduct = cpu_to_le16(DRIVER_PRODUCT_ID), |
43 | .bNumConfigurations = 1, |
44 | }; |
45 | |
46 | static struct usb_debug_descriptor dbg_desc = { |
47 | .bLength = sizeof dbg_desc, |
48 | .bDescriptorType = USB_DT_DEBUG, |
49 | }; |
50 | |
51 | static struct usb_endpoint_descriptor i_desc = { |
52 | .bLength = USB_DT_ENDPOINT_SIZE, |
53 | .bDescriptorType = USB_DT_ENDPOINT, |
54 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
55 | .bEndpointAddress = USB_DIR_IN, |
56 | }; |
57 | |
58 | static struct usb_endpoint_descriptor o_desc = { |
59 | .bLength = USB_DT_ENDPOINT_SIZE, |
60 | .bDescriptorType = USB_DT_ENDPOINT, |
61 | .bmAttributes = USB_ENDPOINT_XFER_BULK, |
62 | .bEndpointAddress = USB_DIR_OUT, |
63 | }; |
64 | |
65 | #ifdef CONFIG_USB_G_DBGP_PRINTK |
66 | static int dbgp_consume(char *buf, unsigned len) |
67 | { |
68 | char c; |
69 | |
70 | if (!len) |
71 | return 0; |
72 | |
73 | c = buf[len-1]; |
74 | if (c != 0) |
75 | buf[len-1] = 0; |
76 | |
77 | printk(KERN_NOTICE "%s%c" , buf, c); |
78 | return 0; |
79 | } |
80 | |
81 | static void __disable_ep(struct usb_ep *ep) |
82 | { |
83 | usb_ep_disable(ep); |
84 | } |
85 | |
86 | static void dbgp_disable_ep(void) |
87 | { |
88 | __disable_ep(dbgp.i_ep); |
89 | __disable_ep(dbgp.o_ep); |
90 | } |
91 | |
92 | static void dbgp_complete(struct usb_ep *ep, struct usb_request *req) |
93 | { |
94 | int stp; |
95 | int err = 0; |
96 | int status = req->status; |
97 | |
98 | if (ep == dbgp.i_ep) { |
99 | stp = 1; |
100 | goto fail; |
101 | } |
102 | |
103 | if (status != 0) { |
104 | stp = 2; |
105 | goto release_req; |
106 | } |
107 | |
108 | dbgp_consume(req->buf, req->actual); |
109 | |
110 | req->length = DBGP_REQ_LEN; |
111 | err = usb_ep_queue(ep, req, GFP_ATOMIC); |
112 | if (err < 0) { |
113 | stp = 3; |
114 | goto release_req; |
115 | } |
116 | |
117 | return; |
118 | |
119 | release_req: |
120 | kfree(req->buf); |
121 | usb_ep_free_request(dbgp.o_ep, req); |
122 | dbgp_disable_ep(); |
123 | fail: |
124 | dev_dbg(&dbgp.gadget->dev, |
125 | "complete: failure (%d:%d) ==> %d\n" , stp, err, status); |
126 | } |
127 | |
128 | static int dbgp_enable_ep_req(struct usb_ep *ep) |
129 | { |
130 | int err, stp; |
131 | struct usb_request *req; |
132 | |
133 | req = usb_ep_alloc_request(ep, GFP_KERNEL); |
134 | if (!req) { |
135 | err = -ENOMEM; |
136 | stp = 1; |
137 | goto fail_1; |
138 | } |
139 | |
140 | req->buf = kzalloc(DBGP_REQ_LEN, GFP_KERNEL); |
141 | if (!req->buf) { |
142 | err = -ENOMEM; |
143 | stp = 2; |
144 | goto fail_2; |
145 | } |
146 | |
147 | req->complete = dbgp_complete; |
148 | req->length = DBGP_REQ_LEN; |
149 | err = usb_ep_queue(ep, req, GFP_ATOMIC); |
150 | if (err < 0) { |
151 | stp = 3; |
152 | goto fail_3; |
153 | } |
154 | |
155 | return 0; |
156 | |
157 | fail_3: |
158 | kfree(req->buf); |
159 | fail_2: |
160 | usb_ep_free_request(dbgp.o_ep, req); |
161 | fail_1: |
162 | dev_dbg(&dbgp.gadget->dev, |
163 | "enable ep req: failure (%d:%d)\n" , stp, err); |
164 | return err; |
165 | } |
166 | |
167 | static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc) |
168 | { |
169 | int err; |
170 | ep->desc = desc; |
171 | err = usb_ep_enable(ep); |
172 | return err; |
173 | } |
174 | |
175 | static int dbgp_enable_ep(void) |
176 | { |
177 | int err, stp; |
178 | |
179 | err = __enable_ep(dbgp.i_ep, &i_desc); |
180 | if (err < 0) { |
181 | stp = 1; |
182 | goto fail_1; |
183 | } |
184 | |
185 | err = __enable_ep(dbgp.o_ep, &o_desc); |
186 | if (err < 0) { |
187 | stp = 2; |
188 | goto fail_2; |
189 | } |
190 | |
191 | err = dbgp_enable_ep_req(dbgp.o_ep); |
192 | if (err < 0) { |
193 | stp = 3; |
194 | goto fail_3; |
195 | } |
196 | |
197 | return 0; |
198 | |
199 | fail_3: |
200 | __disable_ep(dbgp.o_ep); |
201 | fail_2: |
202 | __disable_ep(dbgp.i_ep); |
203 | fail_1: |
204 | dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n" , stp, err); |
205 | return err; |
206 | } |
207 | #endif |
208 | |
209 | static void dbgp_disconnect(struct usb_gadget *gadget) |
210 | { |
211 | #ifdef CONFIG_USB_G_DBGP_PRINTK |
212 | dbgp_disable_ep(); |
213 | #else |
214 | gserial_disconnect(dbgp.serial); |
215 | #endif |
216 | } |
217 | |
218 | static void dbgp_unbind(struct usb_gadget *gadget) |
219 | { |
220 | #ifdef CONFIG_USB_G_DBGP_SERIAL |
221 | kfree(objp: dbgp.serial); |
222 | dbgp.serial = NULL; |
223 | #endif |
224 | if (dbgp.req) { |
225 | kfree(objp: dbgp.req->buf); |
226 | usb_ep_free_request(ep: gadget->ep0, req: dbgp.req); |
227 | dbgp.req = NULL; |
228 | } |
229 | } |
230 | |
231 | #ifdef CONFIG_USB_G_DBGP_SERIAL |
232 | static unsigned char tty_line; |
233 | #endif |
234 | |
235 | static int dbgp_configure_endpoints(struct usb_gadget *gadget) |
236 | { |
237 | int stp; |
238 | |
239 | usb_ep_autoconfig_reset(gadget); |
240 | |
241 | dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc); |
242 | if (!dbgp.i_ep) { |
243 | stp = 1; |
244 | goto fail_1; |
245 | } |
246 | |
247 | i_desc.wMaxPacketSize = |
248 | cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); |
249 | |
250 | dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc); |
251 | if (!dbgp.o_ep) { |
252 | stp = 2; |
253 | goto fail_1; |
254 | } |
255 | |
256 | o_desc.wMaxPacketSize = |
257 | cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); |
258 | |
259 | dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress; |
260 | dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress; |
261 | |
262 | #ifdef CONFIG_USB_G_DBGP_SERIAL |
263 | dbgp.serial->in = dbgp.i_ep; |
264 | dbgp.serial->out = dbgp.o_ep; |
265 | |
266 | dbgp.serial->in->desc = &i_desc; |
267 | dbgp.serial->out->desc = &o_desc; |
268 | #endif |
269 | |
270 | return 0; |
271 | |
272 | fail_1: |
273 | dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n" , stp); |
274 | return -ENODEV; |
275 | } |
276 | |
277 | static int dbgp_bind(struct usb_gadget *gadget, |
278 | struct usb_gadget_driver *driver) |
279 | { |
280 | int err, stp; |
281 | |
282 | dbgp.gadget = gadget; |
283 | |
284 | dbgp.req = usb_ep_alloc_request(ep: gadget->ep0, GFP_KERNEL); |
285 | if (!dbgp.req) { |
286 | err = -ENOMEM; |
287 | stp = 1; |
288 | goto fail; |
289 | } |
290 | |
291 | dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL); |
292 | if (!dbgp.req->buf) { |
293 | err = -ENOMEM; |
294 | stp = 2; |
295 | goto fail; |
296 | } |
297 | |
298 | dbgp.req->length = DBGP_REQ_EP0_LEN; |
299 | |
300 | #ifdef CONFIG_USB_G_DBGP_SERIAL |
301 | dbgp.serial = kzalloc(size: sizeof(struct gserial), GFP_KERNEL); |
302 | if (!dbgp.serial) { |
303 | stp = 3; |
304 | err = -ENOMEM; |
305 | goto fail; |
306 | } |
307 | |
308 | if (gserial_alloc_line(port_line: &tty_line)) { |
309 | stp = 4; |
310 | err = -ENODEV; |
311 | goto fail; |
312 | } |
313 | #endif |
314 | |
315 | err = dbgp_configure_endpoints(gadget); |
316 | if (err < 0) { |
317 | stp = 5; |
318 | goto fail; |
319 | } |
320 | |
321 | dev_dbg(&dbgp.gadget->dev, "bind: success\n" ); |
322 | return 0; |
323 | |
324 | fail: |
325 | dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n" , stp, err); |
326 | dbgp_unbind(gadget); |
327 | return err; |
328 | } |
329 | |
330 | static void dbgp_setup_complete(struct usb_ep *ep, |
331 | struct usb_request *req) |
332 | { |
333 | dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n" , |
334 | req->status, req->actual, req->length); |
335 | } |
336 | |
337 | static int dbgp_setup(struct usb_gadget *gadget, |
338 | const struct usb_ctrlrequest *ctrl) |
339 | { |
340 | struct usb_request *req = dbgp.req; |
341 | u8 request = ctrl->bRequest; |
342 | u16 value = le16_to_cpu(ctrl->wValue); |
343 | u16 length = le16_to_cpu(ctrl->wLength); |
344 | int err = -EOPNOTSUPP; |
345 | void *data = NULL; |
346 | u16 len = 0; |
347 | |
348 | if (length > DBGP_REQ_LEN) { |
349 | if (ctrl->bRequestType & USB_DIR_IN) { |
350 | /* Cast away the const, we are going to overwrite on purpose. */ |
351 | __le16 *temp = (__le16 *)&ctrl->wLength; |
352 | |
353 | *temp = cpu_to_le16(DBGP_REQ_LEN); |
354 | length = DBGP_REQ_LEN; |
355 | } else { |
356 | return err; |
357 | } |
358 | } |
359 | |
360 | |
361 | if (request == USB_REQ_GET_DESCRIPTOR) { |
362 | switch (value>>8) { |
363 | case USB_DT_DEVICE: |
364 | dev_dbg(&dbgp.gadget->dev, "setup: desc device\n" ); |
365 | len = sizeof device_desc; |
366 | data = &device_desc; |
367 | device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; |
368 | break; |
369 | case USB_DT_DEBUG: |
370 | dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n" ); |
371 | len = sizeof dbg_desc; |
372 | data = &dbg_desc; |
373 | break; |
374 | default: |
375 | goto fail; |
376 | } |
377 | err = 0; |
378 | } else if (request == USB_REQ_SET_FEATURE && |
379 | value == USB_DEVICE_DEBUG_MODE) { |
380 | dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n" ); |
381 | #ifdef CONFIG_USB_G_DBGP_PRINTK |
382 | err = dbgp_enable_ep(); |
383 | #else |
384 | err = dbgp_configure_endpoints(gadget); |
385 | if (err < 0) { |
386 | goto fail; |
387 | } |
388 | err = gserial_connect(dbgp.serial, port_num: tty_line); |
389 | #endif |
390 | if (err < 0) |
391 | goto fail; |
392 | } else |
393 | goto fail; |
394 | |
395 | req->length = min(length, len); |
396 | req->zero = len < req->length; |
397 | if (data && req->length) |
398 | memcpy(req->buf, data, req->length); |
399 | |
400 | req->complete = dbgp_setup_complete; |
401 | return usb_ep_queue(ep: gadget->ep0, req, GFP_ATOMIC); |
402 | |
403 | fail: |
404 | dev_dbg(&dbgp.gadget->dev, |
405 | "setup: failure req %x v %x\n" , request, value); |
406 | return err; |
407 | } |
408 | |
409 | static struct usb_gadget_driver dbgp_driver = { |
410 | .function = "dbgp" , |
411 | .max_speed = USB_SPEED_HIGH, |
412 | .bind = dbgp_bind, |
413 | .unbind = dbgp_unbind, |
414 | .setup = dbgp_setup, |
415 | .reset = dbgp_disconnect, |
416 | .disconnect = dbgp_disconnect, |
417 | .driver = { |
418 | .owner = THIS_MODULE, |
419 | .name = "dbgp" |
420 | }, |
421 | }; |
422 | |
423 | static int __init dbgp_init(void) |
424 | { |
425 | return usb_gadget_register_driver(&dbgp_driver); |
426 | } |
427 | |
428 | static void __exit dbgp_exit(void) |
429 | { |
430 | usb_gadget_unregister_driver(driver: &dbgp_driver); |
431 | #ifdef CONFIG_USB_G_DBGP_SERIAL |
432 | gserial_free_line(port_line: tty_line); |
433 | #endif |
434 | } |
435 | |
436 | MODULE_AUTHOR("Stephane Duverger" ); |
437 | MODULE_LICENSE("GPL" ); |
438 | module_init(dbgp_init); |
439 | module_exit(dbgp_exit); |
440 | |