15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
28443f2d2SAndrzej Pietrasiewicz /*
38443f2d2SAndrzej Pietrasiewicz * dbgp.c -- EHCI Debug Port device gadget
48443f2d2SAndrzej Pietrasiewicz *
58443f2d2SAndrzej Pietrasiewicz * Copyright (C) 2010 Stephane Duverger
68443f2d2SAndrzej Pietrasiewicz *
78443f2d2SAndrzej Pietrasiewicz * Released under the GPLv2.
88443f2d2SAndrzej Pietrasiewicz */
98443f2d2SAndrzej Pietrasiewicz
108443f2d2SAndrzej Pietrasiewicz /* verbose messages */
118443f2d2SAndrzej Pietrasiewicz #include <linux/kernel.h>
128443f2d2SAndrzej Pietrasiewicz #include <linux/device.h>
138443f2d2SAndrzej Pietrasiewicz #include <linux/module.h>
148443f2d2SAndrzej Pietrasiewicz #include <linux/usb/ch9.h>
158443f2d2SAndrzej Pietrasiewicz #include <linux/usb/gadget.h>
168443f2d2SAndrzej Pietrasiewicz
178443f2d2SAndrzej Pietrasiewicz #include "u_serial.h"
188443f2d2SAndrzej Pietrasiewicz
198443f2d2SAndrzej Pietrasiewicz #define DRIVER_VENDOR_ID 0x0525 /* NetChip */
208443f2d2SAndrzej Pietrasiewicz #define DRIVER_PRODUCT_ID 0xc0de /* undefined */
218443f2d2SAndrzej Pietrasiewicz
228443f2d2SAndrzej Pietrasiewicz #define USB_DEBUG_MAX_PACKET_SIZE 8
238443f2d2SAndrzej Pietrasiewicz #define DBGP_REQ_EP0_LEN 128
248443f2d2SAndrzej Pietrasiewicz #define DBGP_REQ_LEN 512
258443f2d2SAndrzej Pietrasiewicz
268443f2d2SAndrzej Pietrasiewicz static struct dbgp {
278443f2d2SAndrzej Pietrasiewicz struct usb_gadget *gadget;
288443f2d2SAndrzej Pietrasiewicz struct usb_request *req;
298443f2d2SAndrzej Pietrasiewicz struct usb_ep *i_ep;
308443f2d2SAndrzej Pietrasiewicz struct usb_ep *o_ep;
318443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_SERIAL
328443f2d2SAndrzej Pietrasiewicz struct gserial *serial;
338443f2d2SAndrzej Pietrasiewicz #endif
348443f2d2SAndrzej Pietrasiewicz } dbgp;
358443f2d2SAndrzej Pietrasiewicz
368443f2d2SAndrzej Pietrasiewicz static struct usb_device_descriptor device_desc = {
378443f2d2SAndrzej Pietrasiewicz .bLength = sizeof device_desc,
388443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_DEVICE,
39b8464bcfSVaishali Thakkar .bcdUSB = cpu_to_le16(0x0200),
408443f2d2SAndrzej Pietrasiewicz .bDeviceClass = USB_CLASS_VENDOR_SPEC,
41b8464bcfSVaishali Thakkar .idVendor = cpu_to_le16(DRIVER_VENDOR_ID),
42b8464bcfSVaishali Thakkar .idProduct = cpu_to_le16(DRIVER_PRODUCT_ID),
438443f2d2SAndrzej Pietrasiewicz .bNumConfigurations = 1,
448443f2d2SAndrzej Pietrasiewicz };
458443f2d2SAndrzej Pietrasiewicz
468443f2d2SAndrzej Pietrasiewicz static struct usb_debug_descriptor dbg_desc = {
478443f2d2SAndrzej Pietrasiewicz .bLength = sizeof dbg_desc,
488443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_DEBUG,
498443f2d2SAndrzej Pietrasiewicz };
508443f2d2SAndrzej Pietrasiewicz
518443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor i_desc = {
528443f2d2SAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
538443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
548443f2d2SAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
558443f2d2SAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_IN,
568443f2d2SAndrzej Pietrasiewicz };
578443f2d2SAndrzej Pietrasiewicz
588443f2d2SAndrzej Pietrasiewicz static struct usb_endpoint_descriptor o_desc = {
598443f2d2SAndrzej Pietrasiewicz .bLength = USB_DT_ENDPOINT_SIZE,
608443f2d2SAndrzej Pietrasiewicz .bDescriptorType = USB_DT_ENDPOINT,
618443f2d2SAndrzej Pietrasiewicz .bmAttributes = USB_ENDPOINT_XFER_BULK,
628443f2d2SAndrzej Pietrasiewicz .bEndpointAddress = USB_DIR_OUT,
638443f2d2SAndrzej Pietrasiewicz };
648443f2d2SAndrzej Pietrasiewicz
658443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_PRINTK
dbgp_consume(char * buf,unsigned len)668443f2d2SAndrzej Pietrasiewicz static int dbgp_consume(char *buf, unsigned len)
678443f2d2SAndrzej Pietrasiewicz {
688443f2d2SAndrzej Pietrasiewicz char c;
698443f2d2SAndrzej Pietrasiewicz
708443f2d2SAndrzej Pietrasiewicz if (!len)
718443f2d2SAndrzej Pietrasiewicz return 0;
728443f2d2SAndrzej Pietrasiewicz
738443f2d2SAndrzej Pietrasiewicz c = buf[len-1];
748443f2d2SAndrzej Pietrasiewicz if (c != 0)
758443f2d2SAndrzej Pietrasiewicz buf[len-1] = 0;
768443f2d2SAndrzej Pietrasiewicz
778443f2d2SAndrzej Pietrasiewicz printk(KERN_NOTICE "%s%c", buf, c);
788443f2d2SAndrzej Pietrasiewicz return 0;
798443f2d2SAndrzej Pietrasiewicz }
808443f2d2SAndrzej Pietrasiewicz
__disable_ep(struct usb_ep * ep)818443f2d2SAndrzej Pietrasiewicz static void __disable_ep(struct usb_ep *ep)
828443f2d2SAndrzej Pietrasiewicz {
838443f2d2SAndrzej Pietrasiewicz usb_ep_disable(ep);
848443f2d2SAndrzej Pietrasiewicz }
858443f2d2SAndrzej Pietrasiewicz
dbgp_disable_ep(void)868443f2d2SAndrzej Pietrasiewicz static void dbgp_disable_ep(void)
878443f2d2SAndrzej Pietrasiewicz {
888443f2d2SAndrzej Pietrasiewicz __disable_ep(dbgp.i_ep);
898443f2d2SAndrzej Pietrasiewicz __disable_ep(dbgp.o_ep);
908443f2d2SAndrzej Pietrasiewicz }
918443f2d2SAndrzej Pietrasiewicz
dbgp_complete(struct usb_ep * ep,struct usb_request * req)928443f2d2SAndrzej Pietrasiewicz static void dbgp_complete(struct usb_ep *ep, struct usb_request *req)
938443f2d2SAndrzej Pietrasiewicz {
948443f2d2SAndrzej Pietrasiewicz int stp;
958443f2d2SAndrzej Pietrasiewicz int err = 0;
968443f2d2SAndrzej Pietrasiewicz int status = req->status;
978443f2d2SAndrzej Pietrasiewicz
988443f2d2SAndrzej Pietrasiewicz if (ep == dbgp.i_ep) {
998443f2d2SAndrzej Pietrasiewicz stp = 1;
1008443f2d2SAndrzej Pietrasiewicz goto fail;
1018443f2d2SAndrzej Pietrasiewicz }
1028443f2d2SAndrzej Pietrasiewicz
1038443f2d2SAndrzej Pietrasiewicz if (status != 0) {
1048443f2d2SAndrzej Pietrasiewicz stp = 2;
1058443f2d2SAndrzej Pietrasiewicz goto release_req;
1068443f2d2SAndrzej Pietrasiewicz }
1078443f2d2SAndrzej Pietrasiewicz
1088443f2d2SAndrzej Pietrasiewicz dbgp_consume(req->buf, req->actual);
1098443f2d2SAndrzej Pietrasiewicz
1108443f2d2SAndrzej Pietrasiewicz req->length = DBGP_REQ_LEN;
1118443f2d2SAndrzej Pietrasiewicz err = usb_ep_queue(ep, req, GFP_ATOMIC);
1128443f2d2SAndrzej Pietrasiewicz if (err < 0) {
1138443f2d2SAndrzej Pietrasiewicz stp = 3;
1148443f2d2SAndrzej Pietrasiewicz goto release_req;
1158443f2d2SAndrzej Pietrasiewicz }
1168443f2d2SAndrzej Pietrasiewicz
1178443f2d2SAndrzej Pietrasiewicz return;
1188443f2d2SAndrzej Pietrasiewicz
1198443f2d2SAndrzej Pietrasiewicz release_req:
1208443f2d2SAndrzej Pietrasiewicz kfree(req->buf);
1218443f2d2SAndrzej Pietrasiewicz usb_ep_free_request(dbgp.o_ep, req);
1228443f2d2SAndrzej Pietrasiewicz dbgp_disable_ep();
1238443f2d2SAndrzej Pietrasiewicz fail:
1248443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev,
1258443f2d2SAndrzej Pietrasiewicz "complete: failure (%d:%d) ==> %d\n", stp, err, status);
1268443f2d2SAndrzej Pietrasiewicz }
1278443f2d2SAndrzej Pietrasiewicz
dbgp_enable_ep_req(struct usb_ep * ep)1288443f2d2SAndrzej Pietrasiewicz static int dbgp_enable_ep_req(struct usb_ep *ep)
1298443f2d2SAndrzej Pietrasiewicz {
1308443f2d2SAndrzej Pietrasiewicz int err, stp;
1318443f2d2SAndrzej Pietrasiewicz struct usb_request *req;
1328443f2d2SAndrzej Pietrasiewicz
1338443f2d2SAndrzej Pietrasiewicz req = usb_ep_alloc_request(ep, GFP_KERNEL);
1348443f2d2SAndrzej Pietrasiewicz if (!req) {
1358443f2d2SAndrzej Pietrasiewicz err = -ENOMEM;
1368443f2d2SAndrzej Pietrasiewicz stp = 1;
1378443f2d2SAndrzej Pietrasiewicz goto fail_1;
1388443f2d2SAndrzej Pietrasiewicz }
1398443f2d2SAndrzej Pietrasiewicz
14086ebbc11SGreg Kroah-Hartman req->buf = kzalloc(DBGP_REQ_LEN, GFP_KERNEL);
1418443f2d2SAndrzej Pietrasiewicz if (!req->buf) {
1428443f2d2SAndrzej Pietrasiewicz err = -ENOMEM;
1438443f2d2SAndrzej Pietrasiewicz stp = 2;
1448443f2d2SAndrzej Pietrasiewicz goto fail_2;
1458443f2d2SAndrzej Pietrasiewicz }
1468443f2d2SAndrzej Pietrasiewicz
1478443f2d2SAndrzej Pietrasiewicz req->complete = dbgp_complete;
1488443f2d2SAndrzej Pietrasiewicz req->length = DBGP_REQ_LEN;
1498443f2d2SAndrzej Pietrasiewicz err = usb_ep_queue(ep, req, GFP_ATOMIC);
1508443f2d2SAndrzej Pietrasiewicz if (err < 0) {
1518443f2d2SAndrzej Pietrasiewicz stp = 3;
1528443f2d2SAndrzej Pietrasiewicz goto fail_3;
1538443f2d2SAndrzej Pietrasiewicz }
1548443f2d2SAndrzej Pietrasiewicz
1558443f2d2SAndrzej Pietrasiewicz return 0;
1568443f2d2SAndrzej Pietrasiewicz
1578443f2d2SAndrzej Pietrasiewicz fail_3:
1588443f2d2SAndrzej Pietrasiewicz kfree(req->buf);
1598443f2d2SAndrzej Pietrasiewicz fail_2:
1608443f2d2SAndrzej Pietrasiewicz usb_ep_free_request(dbgp.o_ep, req);
1618443f2d2SAndrzej Pietrasiewicz fail_1:
1628443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev,
1638443f2d2SAndrzej Pietrasiewicz "enable ep req: failure (%d:%d)\n", stp, err);
1648443f2d2SAndrzej Pietrasiewicz return err;
1658443f2d2SAndrzej Pietrasiewicz }
1668443f2d2SAndrzej Pietrasiewicz
__enable_ep(struct usb_ep * ep,struct usb_endpoint_descriptor * desc)1678443f2d2SAndrzej Pietrasiewicz static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc)
1688443f2d2SAndrzej Pietrasiewicz {
1698443f2d2SAndrzej Pietrasiewicz int err;
1708443f2d2SAndrzej Pietrasiewicz ep->desc = desc;
1718443f2d2SAndrzej Pietrasiewicz err = usb_ep_enable(ep);
1728443f2d2SAndrzej Pietrasiewicz return err;
1738443f2d2SAndrzej Pietrasiewicz }
1748443f2d2SAndrzej Pietrasiewicz
dbgp_enable_ep(void)1758443f2d2SAndrzej Pietrasiewicz static int dbgp_enable_ep(void)
1768443f2d2SAndrzej Pietrasiewicz {
1778443f2d2SAndrzej Pietrasiewicz int err, stp;
1788443f2d2SAndrzej Pietrasiewicz
1798443f2d2SAndrzej Pietrasiewicz err = __enable_ep(dbgp.i_ep, &i_desc);
1808443f2d2SAndrzej Pietrasiewicz if (err < 0) {
1818443f2d2SAndrzej Pietrasiewicz stp = 1;
1828443f2d2SAndrzej Pietrasiewicz goto fail_1;
1838443f2d2SAndrzej Pietrasiewicz }
1848443f2d2SAndrzej Pietrasiewicz
1858443f2d2SAndrzej Pietrasiewicz err = __enable_ep(dbgp.o_ep, &o_desc);
1868443f2d2SAndrzej Pietrasiewicz if (err < 0) {
1878443f2d2SAndrzej Pietrasiewicz stp = 2;
1888443f2d2SAndrzej Pietrasiewicz goto fail_2;
1898443f2d2SAndrzej Pietrasiewicz }
1908443f2d2SAndrzej Pietrasiewicz
1918443f2d2SAndrzej Pietrasiewicz err = dbgp_enable_ep_req(dbgp.o_ep);
1928443f2d2SAndrzej Pietrasiewicz if (err < 0) {
1938443f2d2SAndrzej Pietrasiewicz stp = 3;
1948443f2d2SAndrzej Pietrasiewicz goto fail_3;
1958443f2d2SAndrzej Pietrasiewicz }
1968443f2d2SAndrzej Pietrasiewicz
1978443f2d2SAndrzej Pietrasiewicz return 0;
1988443f2d2SAndrzej Pietrasiewicz
1998443f2d2SAndrzej Pietrasiewicz fail_3:
2008443f2d2SAndrzej Pietrasiewicz __disable_ep(dbgp.o_ep);
2018443f2d2SAndrzej Pietrasiewicz fail_2:
2028443f2d2SAndrzej Pietrasiewicz __disable_ep(dbgp.i_ep);
2038443f2d2SAndrzej Pietrasiewicz fail_1:
2048443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev, "enable ep: failure (%d:%d)\n", stp, err);
2058443f2d2SAndrzej Pietrasiewicz return err;
2068443f2d2SAndrzej Pietrasiewicz }
2078443f2d2SAndrzej Pietrasiewicz #endif
2088443f2d2SAndrzej Pietrasiewicz
dbgp_disconnect(struct usb_gadget * gadget)2098443f2d2SAndrzej Pietrasiewicz static void dbgp_disconnect(struct usb_gadget *gadget)
2108443f2d2SAndrzej Pietrasiewicz {
2118443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_PRINTK
2128443f2d2SAndrzej Pietrasiewicz dbgp_disable_ep();
2138443f2d2SAndrzej Pietrasiewicz #else
2148443f2d2SAndrzej Pietrasiewicz gserial_disconnect(dbgp.serial);
2158443f2d2SAndrzej Pietrasiewicz #endif
2168443f2d2SAndrzej Pietrasiewicz }
2178443f2d2SAndrzej Pietrasiewicz
dbgp_unbind(struct usb_gadget * gadget)2188443f2d2SAndrzej Pietrasiewicz static void dbgp_unbind(struct usb_gadget *gadget)
2198443f2d2SAndrzej Pietrasiewicz {
2208443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_SERIAL
2218443f2d2SAndrzej Pietrasiewicz kfree(dbgp.serial);
2224958cf32SAlexey Khoroshilov dbgp.serial = NULL;
2238443f2d2SAndrzej Pietrasiewicz #endif
2248443f2d2SAndrzej Pietrasiewicz if (dbgp.req) {
2258443f2d2SAndrzej Pietrasiewicz kfree(dbgp.req->buf);
2268443f2d2SAndrzej Pietrasiewicz usb_ep_free_request(gadget->ep0, dbgp.req);
2274958cf32SAlexey Khoroshilov dbgp.req = NULL;
2288443f2d2SAndrzej Pietrasiewicz }
2298443f2d2SAndrzej Pietrasiewicz }
2308443f2d2SAndrzej Pietrasiewicz
2318443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_SERIAL
2328443f2d2SAndrzej Pietrasiewicz static unsigned char tty_line;
2338443f2d2SAndrzej Pietrasiewicz #endif
2348443f2d2SAndrzej Pietrasiewicz
dbgp_configure_endpoints(struct usb_gadget * gadget)2356876d58fSKyösti Mälkki static int dbgp_configure_endpoints(struct usb_gadget *gadget)
2368443f2d2SAndrzej Pietrasiewicz {
2378443f2d2SAndrzej Pietrasiewicz int stp;
2388443f2d2SAndrzej Pietrasiewicz
2398443f2d2SAndrzej Pietrasiewicz usb_ep_autoconfig_reset(gadget);
2408443f2d2SAndrzej Pietrasiewicz
2418443f2d2SAndrzej Pietrasiewicz dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc);
2428443f2d2SAndrzej Pietrasiewicz if (!dbgp.i_ep) {
2438443f2d2SAndrzej Pietrasiewicz stp = 1;
2448443f2d2SAndrzej Pietrasiewicz goto fail_1;
2458443f2d2SAndrzej Pietrasiewicz }
2468443f2d2SAndrzej Pietrasiewicz
2478443f2d2SAndrzej Pietrasiewicz i_desc.wMaxPacketSize =
248b8464bcfSVaishali Thakkar cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
2498443f2d2SAndrzej Pietrasiewicz
2508443f2d2SAndrzej Pietrasiewicz dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc);
2518443f2d2SAndrzej Pietrasiewicz if (!dbgp.o_ep) {
2528443f2d2SAndrzej Pietrasiewicz stp = 2;
2534ce86bfaSRobert Baldyga goto fail_1;
2548443f2d2SAndrzej Pietrasiewicz }
2558443f2d2SAndrzej Pietrasiewicz
2568443f2d2SAndrzej Pietrasiewicz o_desc.wMaxPacketSize =
257b8464bcfSVaishali Thakkar cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE);
2588443f2d2SAndrzej Pietrasiewicz
2598443f2d2SAndrzej Pietrasiewicz dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress;
2608443f2d2SAndrzej Pietrasiewicz dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress;
2618443f2d2SAndrzej Pietrasiewicz
2628443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_SERIAL
2638443f2d2SAndrzej Pietrasiewicz dbgp.serial->in = dbgp.i_ep;
2648443f2d2SAndrzej Pietrasiewicz dbgp.serial->out = dbgp.o_ep;
2658443f2d2SAndrzej Pietrasiewicz
2668443f2d2SAndrzej Pietrasiewicz dbgp.serial->in->desc = &i_desc;
2678443f2d2SAndrzej Pietrasiewicz dbgp.serial->out->desc = &o_desc;
2688443f2d2SAndrzej Pietrasiewicz #endif
2696876d58fSKyösti Mälkki
2706876d58fSKyösti Mälkki return 0;
2716876d58fSKyösti Mälkki
2728443f2d2SAndrzej Pietrasiewicz fail_1:
2738443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp);
2748443f2d2SAndrzej Pietrasiewicz return -ENODEV;
2758443f2d2SAndrzej Pietrasiewicz }
2768443f2d2SAndrzej Pietrasiewicz
dbgp_bind(struct usb_gadget * gadget,struct usb_gadget_driver * driver)277c94e289fSArnd Bergmann static int dbgp_bind(struct usb_gadget *gadget,
2788443f2d2SAndrzej Pietrasiewicz struct usb_gadget_driver *driver)
2798443f2d2SAndrzej Pietrasiewicz {
2808443f2d2SAndrzej Pietrasiewicz int err, stp;
2818443f2d2SAndrzej Pietrasiewicz
2828443f2d2SAndrzej Pietrasiewicz dbgp.gadget = gadget;
2838443f2d2SAndrzej Pietrasiewicz
2848443f2d2SAndrzej Pietrasiewicz dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
2858443f2d2SAndrzej Pietrasiewicz if (!dbgp.req) {
2868443f2d2SAndrzej Pietrasiewicz err = -ENOMEM;
2878443f2d2SAndrzej Pietrasiewicz stp = 1;
2888443f2d2SAndrzej Pietrasiewicz goto fail;
2898443f2d2SAndrzej Pietrasiewicz }
2908443f2d2SAndrzej Pietrasiewicz
2918443f2d2SAndrzej Pietrasiewicz dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL);
2928443f2d2SAndrzej Pietrasiewicz if (!dbgp.req->buf) {
2938443f2d2SAndrzej Pietrasiewicz err = -ENOMEM;
2948443f2d2SAndrzej Pietrasiewicz stp = 2;
2958443f2d2SAndrzej Pietrasiewicz goto fail;
2968443f2d2SAndrzej Pietrasiewicz }
2978443f2d2SAndrzej Pietrasiewicz
2988443f2d2SAndrzej Pietrasiewicz dbgp.req->length = DBGP_REQ_EP0_LEN;
2998443f2d2SAndrzej Pietrasiewicz
3008443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_SERIAL
3018443f2d2SAndrzej Pietrasiewicz dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL);
3028443f2d2SAndrzej Pietrasiewicz if (!dbgp.serial) {
3038443f2d2SAndrzej Pietrasiewicz stp = 3;
3048443f2d2SAndrzej Pietrasiewicz err = -ENOMEM;
3058443f2d2SAndrzej Pietrasiewicz goto fail;
3068443f2d2SAndrzej Pietrasiewicz }
3076876d58fSKyösti Mälkki
3086876d58fSKyösti Mälkki if (gserial_alloc_line(&tty_line)) {
3096876d58fSKyösti Mälkki stp = 4;
3106876d58fSKyösti Mälkki err = -ENODEV;
3116876d58fSKyösti Mälkki goto fail;
3126876d58fSKyösti Mälkki }
3138443f2d2SAndrzej Pietrasiewicz #endif
3146876d58fSKyösti Mälkki
3158443f2d2SAndrzej Pietrasiewicz err = dbgp_configure_endpoints(gadget);
3168443f2d2SAndrzej Pietrasiewicz if (err < 0) {
3176876d58fSKyösti Mälkki stp = 5;
3188443f2d2SAndrzej Pietrasiewicz goto fail;
3198443f2d2SAndrzej Pietrasiewicz }
3208443f2d2SAndrzej Pietrasiewicz
3218443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev, "bind: success\n");
3228443f2d2SAndrzej Pietrasiewicz return 0;
3238443f2d2SAndrzej Pietrasiewicz
3248443f2d2SAndrzej Pietrasiewicz fail:
3258443f2d2SAndrzej Pietrasiewicz dev_dbg(&gadget->dev, "bind: failure (%d:%d)\n", stp, err);
3268443f2d2SAndrzej Pietrasiewicz dbgp_unbind(gadget);
3278443f2d2SAndrzej Pietrasiewicz return err;
3288443f2d2SAndrzej Pietrasiewicz }
3298443f2d2SAndrzej Pietrasiewicz
dbgp_setup_complete(struct usb_ep * ep,struct usb_request * req)3308443f2d2SAndrzej Pietrasiewicz static void dbgp_setup_complete(struct usb_ep *ep,
3318443f2d2SAndrzej Pietrasiewicz struct usb_request *req)
3328443f2d2SAndrzej Pietrasiewicz {
3338443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n",
3348443f2d2SAndrzej Pietrasiewicz req->status, req->actual, req->length);
3358443f2d2SAndrzej Pietrasiewicz }
3368443f2d2SAndrzej Pietrasiewicz
dbgp_setup(struct usb_gadget * gadget,const struct usb_ctrlrequest * ctrl)3378443f2d2SAndrzej Pietrasiewicz static int dbgp_setup(struct usb_gadget *gadget,
3388443f2d2SAndrzej Pietrasiewicz const struct usb_ctrlrequest *ctrl)
3398443f2d2SAndrzej Pietrasiewicz {
3408443f2d2SAndrzej Pietrasiewicz struct usb_request *req = dbgp.req;
3418443f2d2SAndrzej Pietrasiewicz u8 request = ctrl->bRequest;
3428443f2d2SAndrzej Pietrasiewicz u16 value = le16_to_cpu(ctrl->wValue);
3438443f2d2SAndrzej Pietrasiewicz u16 length = le16_to_cpu(ctrl->wLength);
3448443f2d2SAndrzej Pietrasiewicz int err = -EOPNOTSUPP;
3458443f2d2SAndrzej Pietrasiewicz void *data = NULL;
3468443f2d2SAndrzej Pietrasiewicz u16 len = 0;
3478443f2d2SAndrzej Pietrasiewicz
348153a2d7eSGreg Kroah-Hartman if (length > DBGP_REQ_LEN) {
349f08adf5aSGreg Kroah-Hartman if (ctrl->bRequestType & USB_DIR_IN) {
350153a2d7eSGreg Kroah-Hartman /* Cast away the const, we are going to overwrite on purpose. */
351153a2d7eSGreg Kroah-Hartman __le16 *temp = (__le16 *)&ctrl->wLength;
352153a2d7eSGreg Kroah-Hartman
353153a2d7eSGreg Kroah-Hartman *temp = cpu_to_le16(DBGP_REQ_LEN);
354153a2d7eSGreg Kroah-Hartman length = DBGP_REQ_LEN;
355f08adf5aSGreg Kroah-Hartman } else {
356f08adf5aSGreg Kroah-Hartman return err;
357153a2d7eSGreg Kroah-Hartman }
358153a2d7eSGreg Kroah-Hartman }
359153a2d7eSGreg Kroah-Hartman
360153a2d7eSGreg Kroah-Hartman
3618443f2d2SAndrzej Pietrasiewicz if (request == USB_REQ_GET_DESCRIPTOR) {
3628443f2d2SAndrzej Pietrasiewicz switch (value>>8) {
3638443f2d2SAndrzej Pietrasiewicz case USB_DT_DEVICE:
3648443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev, "setup: desc device\n");
3658443f2d2SAndrzej Pietrasiewicz len = sizeof device_desc;
3668443f2d2SAndrzej Pietrasiewicz data = &device_desc;
3678443f2d2SAndrzej Pietrasiewicz device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
3688443f2d2SAndrzej Pietrasiewicz break;
3698443f2d2SAndrzej Pietrasiewicz case USB_DT_DEBUG:
3708443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev, "setup: desc debug\n");
3718443f2d2SAndrzej Pietrasiewicz len = sizeof dbg_desc;
3728443f2d2SAndrzej Pietrasiewicz data = &dbg_desc;
3738443f2d2SAndrzej Pietrasiewicz break;
3748443f2d2SAndrzej Pietrasiewicz default:
3758443f2d2SAndrzej Pietrasiewicz goto fail;
3768443f2d2SAndrzej Pietrasiewicz }
3778443f2d2SAndrzej Pietrasiewicz err = 0;
3788443f2d2SAndrzej Pietrasiewicz } else if (request == USB_REQ_SET_FEATURE &&
3798443f2d2SAndrzej Pietrasiewicz value == USB_DEVICE_DEBUG_MODE) {
3808443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n");
3818443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_PRINTK
3828443f2d2SAndrzej Pietrasiewicz err = dbgp_enable_ep();
3838443f2d2SAndrzej Pietrasiewicz #else
3846876d58fSKyösti Mälkki err = dbgp_configure_endpoints(gadget);
3856876d58fSKyösti Mälkki if (err < 0) {
3866876d58fSKyösti Mälkki goto fail;
3876876d58fSKyösti Mälkki }
3888443f2d2SAndrzej Pietrasiewicz err = gserial_connect(dbgp.serial, tty_line);
3898443f2d2SAndrzej Pietrasiewicz #endif
3908443f2d2SAndrzej Pietrasiewicz if (err < 0)
3918443f2d2SAndrzej Pietrasiewicz goto fail;
3928443f2d2SAndrzej Pietrasiewicz } else
3938443f2d2SAndrzej Pietrasiewicz goto fail;
3948443f2d2SAndrzej Pietrasiewicz
3958443f2d2SAndrzej Pietrasiewicz req->length = min(length, len);
3968443f2d2SAndrzej Pietrasiewicz req->zero = len < req->length;
3978443f2d2SAndrzej Pietrasiewicz if (data && req->length)
3988443f2d2SAndrzej Pietrasiewicz memcpy(req->buf, data, req->length);
3998443f2d2SAndrzej Pietrasiewicz
4008443f2d2SAndrzej Pietrasiewicz req->complete = dbgp_setup_complete;
4018443f2d2SAndrzej Pietrasiewicz return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
4028443f2d2SAndrzej Pietrasiewicz
4038443f2d2SAndrzej Pietrasiewicz fail:
4048443f2d2SAndrzej Pietrasiewicz dev_dbg(&dbgp.gadget->dev,
4058443f2d2SAndrzej Pietrasiewicz "setup: failure req %x v %x\n", request, value);
4068443f2d2SAndrzej Pietrasiewicz return err;
4078443f2d2SAndrzej Pietrasiewicz }
4088443f2d2SAndrzej Pietrasiewicz
409c94e289fSArnd Bergmann static struct usb_gadget_driver dbgp_driver = {
4108443f2d2SAndrzej Pietrasiewicz .function = "dbgp",
4118443f2d2SAndrzej Pietrasiewicz .max_speed = USB_SPEED_HIGH,
4128443f2d2SAndrzej Pietrasiewicz .bind = dbgp_bind,
4138443f2d2SAndrzej Pietrasiewicz .unbind = dbgp_unbind,
4148443f2d2SAndrzej Pietrasiewicz .setup = dbgp_setup,
415e45cfa20SPeter Chen .reset = dbgp_disconnect,
4168443f2d2SAndrzej Pietrasiewicz .disconnect = dbgp_disconnect,
4178443f2d2SAndrzej Pietrasiewicz .driver = {
4188443f2d2SAndrzej Pietrasiewicz .owner = THIS_MODULE,
4198443f2d2SAndrzej Pietrasiewicz .name = "dbgp"
4208443f2d2SAndrzej Pietrasiewicz },
4218443f2d2SAndrzej Pietrasiewicz };
4228443f2d2SAndrzej Pietrasiewicz
dbgp_init(void)4238443f2d2SAndrzej Pietrasiewicz static int __init dbgp_init(void)
4248443f2d2SAndrzej Pietrasiewicz {
425*af1969a2SAlan Stern return usb_gadget_register_driver(&dbgp_driver);
4268443f2d2SAndrzej Pietrasiewicz }
4278443f2d2SAndrzej Pietrasiewicz
dbgp_exit(void)4288443f2d2SAndrzej Pietrasiewicz static void __exit dbgp_exit(void)
4298443f2d2SAndrzej Pietrasiewicz {
4308443f2d2SAndrzej Pietrasiewicz usb_gadget_unregister_driver(&dbgp_driver);
4318443f2d2SAndrzej Pietrasiewicz #ifdef CONFIG_USB_G_DBGP_SERIAL
4328443f2d2SAndrzej Pietrasiewicz gserial_free_line(tty_line);
4338443f2d2SAndrzej Pietrasiewicz #endif
4348443f2d2SAndrzej Pietrasiewicz }
4358443f2d2SAndrzej Pietrasiewicz
4368443f2d2SAndrzej Pietrasiewicz MODULE_AUTHOR("Stephane Duverger");
4378443f2d2SAndrzej Pietrasiewicz MODULE_LICENSE("GPL");
4388443f2d2SAndrzej Pietrasiewicz module_init(dbgp_init);
4398443f2d2SAndrzej Pietrasiewicz module_exit(dbgp_exit);
440