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/ _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel