[PATCH 1/6] USB gadget driver

Ragner Magalhaes <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
Implements support for a gadget function with composite support
to work as a single function.

Signed-off-by: Ragner Magalhaes <[email protected]>

:000000 100644 0000000... e6551d9... A	drivers/usb/gadget/gadget.c

diff --git a/drivers/usb/gadget/gadget.c b/drivers/usb/gadget/gadget.c
new file mode 100644
index 0000000..e6551d9
--- /dev/null
+++ b/drivers/usb/gadget/gadget.c
@@ -0,0 +1,293 @@
+/*
+ * gadget.c -- USB gadget driver.
+ *
+ * Copyright (C) 2007 Instituto Nokia de Tecnologia - INdT
+ * Developed for INdT by Ragner Magalhaes
+ * Ragner Magalhaes <[email protected]>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * Implements support for one gadget device with a single function.
+ */
+
+#if 0
+#define DEBUG 1
+#define VERBOSE 1
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/composite.h>
+
+#include "gadget_chips.h"
+
+/*-------------------------------------------------------------------------*/
+
+#define xprintk(d,level,fmt,args...) \
+	dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+	xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+	do { } while(0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG	DBG
+#else
+#define VDBG(dev,fmt,args...) \
+	do { } while(0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+	xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+	xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+	xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ		256
+
+
+struct usb_function	*func;
+
+/*-------------------------------------------------------------------------*/
+
+/* An impossibly large value as file_storage */
+#define DELAYED_STATUS		(USB_BUFSIZ + 999)
+
+void usb_func_ep_reset (struct usb_gadget *g, struct usb_function *f)
+{
+	struct usb_ep	*ep;
+
+	list_for_each_entry (ep, &g->ep_list, ep_list) {
+		if (ep->driver_data == f->driver_data) {
+			usb_ep_disable (ep);
+			ep->driver_data = NULL;
+		}
+	}
+}
+
+static void
+free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->buf)
+		kfree (req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+gadget_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	if (req->status || req->actual != req->length)
+		DBG((struct usb_gadget_dev *)ep->driver_data,
+				"setup complete --> %d, %u/%u\n",
+				req->status, req->actual, req->length);
+}
+
+static int
+gadget_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+	struct usb_gadget_dev		*dev = get_gadget_data(gadget);
+	struct usb_request		*req = dev->req;
+	int				value = -EOPNOTSUPP;
+	u16				w_index = le16_to_cpu(ctrl->wIndex);
+	u16				w_value = le16_to_cpu(ctrl->wValue);
+	u16				w_length = le16_to_cpu(ctrl->wLength);
+
+	req->complete = gadget_setup_complete;
+	req->zero = 0;
+	spin_lock(&dev->lock);
+	value = dev->func->setup (gadget, ctrl);
+	spin_unlock(&dev->lock);
+
+	/* respond with data transfer before status phase? */
+	if (value >= 0 && value != DELAYED_STATUS) {
+		req->length = value;
+		req->zero = value < w_length;
+		value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			DBG(dev, "ep_queue --> %d\n", value);
+			req->status = 0;
+			gadget_setup_complete(gadget->ep0, req);
+		}
+	} else {
+		DBG(dev, "setup req%02x.%02x v%04x i%04x l%d --> %d\n",
+				ctrl->bRequestType, ctrl->bRequest,
+				w_value, w_index, w_length, value);
+		/* to eliminate compile warnings */
+		w_index = w_value = 0;
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+static void
+gadget_disconnect(struct usb_gadget *gadget)
+{
+	struct usb_gadget_dev		*dev = get_gadget_data(gadget);
+	unsigned long			flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->func->disconnect(gadget);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void /* __init_or_exit */
+gadget_unbind(struct usb_gadget *gadget)
+{
+	struct usb_gadget_dev		*dev = get_gadget_data(gadget);
+
+	/* we've already been disconnected ... no i/o is active */
+	if (dev->req) {
+		dev->req->length = USB_BUFSIZ;
+		free_ep_req(gadget->ep0, dev->req);
+		dev->req = NULL;
+	}
+	if (dev->func)
+		dev->func->unbind(gadget);
+
+	usb_func_ep_reset (gadget, dev->func);
+	kfree(dev);
+	set_gadget_data(gadget, NULL);
+}
+
+static int __devinit
+gadget_bind(struct usb_gadget *gadget)
+{
+	struct usb_gadget_dev		*dev;
+	int				status = -ENOMEM;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return status;
+
+	spin_lock_init(&dev->lock);
+	dev->gadget = gadget;
+	set_gadget_data(gadget, dev);
+
+	/* preallocate control response and buffer */
+	dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+	if (!dev->req)
+		goto fail;
+	dev->req->buf = kmalloc (USB_BUFSIZ, GFP_KERNEL);
+	if (!dev->req->buf)
+		goto fail;
+
+	dev->req->complete = gadget_setup_complete;
+	gadget->ep0->driver_data = dev;
+
+	dev->func = func;
+
+	usb_ep_autoconfig_reset(gadget);
+
+	if (!func->bind) {
+		status = -EINVAL;
+		goto fail;
+	}
+	status = func->bind (gadget);
+	if (status < 0)
+		goto fail;
+
+	return 0;
+fail:
+	gadget_unbind(gadget);
+	return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+gadget_suspend(struct usb_gadget *gadget)
+{
+	struct usb_gadget_dev		*dev = get_gadget_data(gadget);
+
+	dev->func->suspend(gadget);
+}
+
+static void
+gadget_resume(struct usb_gadget *gadget)
+{
+	struct usb_gadget_dev		*dev = get_gadget_data (gadget);
+
+	dev->func->resume (gadget);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver gadget_drv = {
+#ifdef CONFIG_USB_GADGET_DUALSPEED
+	.speed		= USB_SPEED_HIGH,
+#else
+	.speed		= USB_SPEED_FULL,
+#endif
+
+	.bind		= gadget_bind,
+	.unbind		= __exit_p(gadget_unbind),
+
+	.setup		= gadget_setup,
+	.disconnect	= gadget_disconnect,
+
+	.suspend	= gadget_suspend,
+	.resume		= gadget_resume,
+
+	.driver	= {
+		.owner	= THIS_MODULE,
+	},
+};
+
+/* register single function */
+int usb_func_register(struct usb_function *f)
+{
+	if (!f)
+		return -ENODEV;
+	else if (func)
+		return -EBUSY;
+
+	if (!f->name)
+		f->name = "gadget";
+	gadget_drv.function =  (char *) f->name;
+	gadget_drv.driver.name = f->name;
+
+	func = f;
+
+	return usb_gadget_register_driver (&gadget_drv);
+}
+
+void usb_func_unregister(struct usb_function *f)
+{
+	usb_gadget_unregister_driver (&gadget_drv);
+}
+
-- 
1.5.0.4.501.gb08b




-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.