Hello, This is a patch that implements an USB EHCI Debug Device using the Gadget API. It does make use of "u_serial" to provide userland access. The code is small and is inspired by other gadgets. As i don't have a significant experience in kernel development i think it should be rewritten (using composite ?!). The effective code is really small, but the main problem is that i was constrained to patch into the usb controller code to make it forward the USB_DEVICE_DEBUG_MODE feature to the gadget driver. So unfortunately, every controller (i only tested my code on a Nokia N900 which uses "musb" controller) will potentially need a patch if their ep0 handler does not forward the feature request to the gadget driver ... if i'm correct. I provide a patch for a vanilla 2.6.28.10 kernel, although it has been tested (not intensively) on a 2.6.28-omap1 kernel. The Debug Port on the host side, was feeded by a linux kernel using "earlyprintk=dbgp". The current implementation is exclusive and does not allow the device to request for 500ma on the host port, which will allow charging while being used. I'm not sure if it will be useful, but if you have a devboard or a smartphone with a usb device port (or OTG) it will prevent you from buying a $80 debug device to be able to debug on your recent workstation without serial port :) PATCH - 1 - musb patch: --- linux-2.6.28.10/drivers/usb/musb/musb_gadget_ep0.c 2009-05-02 20:54:43.000000000 +0200 +++ linux-2.6.28.10-g_dbgp/drivers/usb/musb/musb_gadget_ep0.c 2010-06-22 15:02:43.000000000 +0200 @@ -353,6 +353,9 @@ __acquires(musb->lock) musb->g.a_alt_hnp_support = 1; break; #endif + case USB_DEVICE_DEBUG_MODE: + handled = 0; + break; stall: default: handled = -EINVAL; PATCH - 2 - debug device implementation patch: diff -uprN linux-2.6.28.10/drivers/usb/gadget/dbgp.c linux-2.6.28.10-g_dbgp/drivers/usb/gadget/dbgp.c --- linux-2.6.28.10/drivers/usb/gadget/dbgp.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.28.10-g_dbgp/drivers/usb/gadget/dbgp.c 2010-06-22 17:03:53.000000000 +0200 @@ -0,0 +1,281 @@ +/* + * dbgp.c -- EHCI Debug Port device gadget + * + * Copyright (C) 2010 Stephane Duverger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/* See comments in "zero.c" */ +#include "epautoconf.c" +#include "u_serial.c" + +/* We do not have product id for this gadget */ +#define DRIVER_VENDOR_ID 0x0525 /* NetChip */ +#define DRIVER_PRODUCT_ID 0xc0de /* undefined */ + +#define USB_DEBUG_MAX_PACKET_SIZE 8 +#define DBGP_REQ_EP0_LEN 128 +#define DBGP_REQ_LEN 512 + +static struct dbgp { + struct usb_gadget *gadget; + struct gserial *serial; + struct usb_request *req; + struct usb_ep *i_ep; + struct usb_ep *o_ep; +} dbgp; + +static struct usb_device_descriptor device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = __constant_cpu_to_le16(0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID), + .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID), + .bNumConfigurations = 1, +}; + +static struct usb_debug_descriptor dbg_desc = { + .bLength = sizeof dbg_desc, + .bDescriptorType = USB_DT_DEBUG, +}; + +static struct usb_endpoint_descriptor i_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bEndpointAddress = USB_DIR_IN, +}; + +static struct usb_endpoint_descriptor o_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .bEndpointAddress = USB_DIR_OUT, +}; + +static void dbgp_disconnect(struct usb_gadget *gadget) +{ + gserial_disconnect(dbgp.serial); +} + +static void dbgp_unbind(struct usb_gadget *gadget) +{ + if (dbgp.serial) + kfree(dbgp.serial); + + if (dbgp.req) { + if (dbgp.req->buf) + kfree(dbgp.req->buf); + usb_ep_free_request(gadget->ep0, dbgp.req); + } + + gadget->ep0->driver_data = NULL; +} + +static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) +{ + int stp; + + usb_ep_autoconfig_reset(gadget); + + dbgp.i_ep = usb_ep_autoconfig(gadget, &i_desc); + if (! dbgp.i_ep) { + stp = 1; + goto fail_1; + } + + dbgp.i_ep->driver_data = gadget; + i_desc.wMaxPacketSize = __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); + + dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc); + if (! dbgp.o_ep) { + dbgp.i_ep->driver_data = NULL; + stp = 2; + goto fail_2; + } + + dbgp.o_ep->driver_data = gadget; + o_desc.wMaxPacketSize = __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); + + dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f; + dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f; + + /* reflect to serial */ + dbgp.serial->in = dbgp.i_ep; + dbgp.serial->out = dbgp.o_ep; + + dbgp.serial->in_desc = &i_desc; + dbgp.serial->out_desc = &o_desc; + + if (gserial_setup(gadget, 1) < 0) { + stp = 3; + goto fail_3; + } + + return 0; + +fail_3: + dbgp.o_ep->driver_data = NULL; +fail_2: + dbgp.i_ep->driver_data = NULL; +fail_1: + dev_err(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp); + return -ENODEV; +} + +static int __init dbgp_bind(struct usb_gadget *gadget) +{ + int err, stp; + + dbgp.gadget = gadget; + + /* controle endpoint */ + dbgp.req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL); + if (! dbgp.req) { + err = -ENOMEM; + stp = 1; + goto fail; + } + + dbgp.req->buf = kmalloc(DBGP_REQ_EP0_LEN, GFP_KERNEL); + if (! dbgp.req->buf) { + err = -ENOMEM; + stp = 2; + goto fail; + } + + dbgp.req->length = DBGP_REQ_EP0_LEN; + gadget->ep0->driver_data = gadget; + + /* serial interface */ + dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); + if (!dbgp.serial) { + stp = 3; + err = -ENOMEM; + goto fail; + } + + /* in/out ep */ + err = dbgp_configure_endpoints(gadget); + if (err < 0) { + stp = 4; + goto fail; + } + + dev_info(&dbgp.gadget->dev, "bind: success\n"); + return 0; + +fail: + dev_err(&gadget->dev, "bind: failure (%d:%d)\n", stp, err); + dbgp_unbind(gadget); + return err; +} + +static void dbgp_setup_complete(struct usb_ep *ep, struct usb_request *req) +{ +/* dev_info(&dbgp.gadget->dev, "setup complete: %d, %d/%d\n", + req->status, req->actual, req->length); +*/ +} + +static int dbgp_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct usb_request *req = dbgp.req; + u8 request = ctrl->bRequest; + u16 value = le16_to_cpu(ctrl->wValue); + u16 length = le16_to_cpu(ctrl->wLength); + int err = 0; + void *data; + u16 len; + + gadget->ep0->driver_data = gadget; /* claim */ + + if (request == USB_REQ_GET_DESCRIPTOR) { + switch (value>>8) { + case USB_DT_DEVICE: + dev_info(&dbgp.gadget->dev, "setup: device\n"); + len = sizeof device_desc; + data = &device_desc; + break; + case USB_DT_DEBUG: + dev_info(&dbgp.gadget->dev, "setup: debug\n"); + len = sizeof dbg_desc; + data = &dbg_desc; + break; + default: + goto fail; + } + } + else if (request == USB_REQ_SET_FEATURE && value == USB_DEVICE_DEBUG_MODE) { + len = 0; + data = NULL; + dev_info(&dbgp.gadget->dev, "setup: feature\n"); + if ((err=gserial_connect(dbgp.serial, 0)) < 0) + goto fail; + } + else + goto fail; + + if (len >= 0) { + req->length = min(length, len); + req->zero = len < req->length; + if (data && req->length) + memcpy(req->buf, data, req->length); + + req->complete = dbgp_setup_complete; + return usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); + } + +fail: + dev_err(&dbgp.gadget->dev, "setup: failure req %x v %x\n", request, value); + return err; +} + +static struct usb_gadget_driver dbgp_driver = { + .function = "dbgp", + .speed = USB_SPEED_HIGH, + .bind = dbgp_bind, + .unbind = dbgp_unbind, + .setup = dbgp_setup, + .disconnect = dbgp_disconnect, + .driver = { + .owner = THIS_MODULE, + .name = "dbgp" + }, +}; + +static int __init dbgp_init(void) +{ + return usb_gadget_register_driver(&dbgp_driver); +} + +static void __exit dbgp_exit(void) +{ + usb_gadget_unregister_driver(&dbgp_driver); + gserial_cleanup(); +} + +MODULE_AUTHOR("Stephane Duverger"); +MODULE_LICENSE("GPL"); +module_init(dbgp_init); +module_exit(dbgp_exit); diff -uprN linux-2.6.28.10/drivers/usb/gadget/Kconfig linux-2.6.28.10-g_dbgp/drivers/usb/gadget/Kconfig --- linux-2.6.28.10/drivers/usb/gadget/Kconfig 2009-05-02 20:54:43.000000000 +0200 +++ linux-2.6.28.10-g_dbgp/drivers/usb/gadget/Kconfig 2010-06-22 14:54:02.000000000 +0200 @@ -673,6 +673,15 @@ config USB_CDC_COMPOSITE Say "y" to link the driver statically, or "m" to build a dynamically linked module. +config USB_G_DBGP + tristate "EHCI Debug Device gadget" + help + This gadget emulates an EHCI Debug device. This is useful when you want + to interact with an EHCI Debug Port. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "g_dbgp". + # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. diff -uprN linux-2.6.28.10/drivers/usb/gadget/Makefile linux-2.6.28.10-g_dbgp/drivers/usb/gadget/Makefile --- linux-2.6.28.10/drivers/usb/gadget/Makefile 2009-05-02 20:54:43.000000000 +0200 +++ linux-2.6.28.10-g_dbgp/drivers/usb/gadget/Makefile 2010-06-22 14:54:47.000000000 +0200 @@ -31,6 +31,7 @@ gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o g_printer-objs := printer.o g_cdc-objs := cdc2.o +g_dbgp-objs += dbgp.o obj-$(CONFIG_USB_ZERO) += g_zero.o obj-$(CONFIG_USB_ETH) += g_ether.o @@ -40,4 +41,4 @@ obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_G_PRINTER) += g_printer.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o - +obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html