PATCH: usb ehci debug port device gadget

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux