From: Ragner Magalhaes <[EMAIL PROTECTED]>
Implements support for a gadget function with composite support
to work as a single function.
Signed-off-by: Ragner Magalhaes <[EMAIL PROTECTED]>
---
drivers/usb/gadget/gadget.c | 283 +++++++++++++++++++++++++++++++++++++++++++
1 files changed, 283 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/gadget/gadget.c b/drivers/usb/gadget/gadget.c
new file mode 100644
index 0000000..9a4a0e0
--- /dev/null
+++ b/drivers/usb/gadget/gadget.c
@@ -0,0 +1,283 @@
+/*
+ * 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 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->zero = 0;
+ req->length = 0;
+ value = dev->func->setup (gadget, ctrl);
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0 && value != DELAYED_STATUS) {
+ value = min((u16)value, w_length);
+ req->length = value;
+ req->zero = value < w_length;
+
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value != 0 && value != -ESHUTDOWN) {
+ WARN(dev, "error in submission: %s --> %d\n",
+ (ctrl->bRequestType & USB_DIR_IN ?
+ "ep0-in" : "ep0-out"), value);
+ req->status = 0;
+ }
+ } 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);
+
+ gadget->ep0->driver_data = dev;
+ 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->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);
+}
+
-------------------------------------------------------------------------
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