Is there a tutorial out there or a list of what I need to do to get a really teeny driver up and going? g_zero seems to be rather large for what it does...
No tutorial, at least not now -- beyond the overview in the 2.6 kerneldoc (Documentation/DocBook/gadget.pdf). You'll need to understand chunks of the USB spec too. Provide the usb_gadget_driver methods, and work up from there.
Actually I think g_zero is quite small for a driver that handles several different kinds of hardware, supports two different configurations, and passes the usb-if tests at both high and full speeds ... more than you may be thinking! :)
Try trimming things out of zero.c ... easier to prune out things you don't need (like second config) than to come up with a really minimal driver (like the one below) and then morph it back into something that's useful and portable.
- Dave
#include <linux/config.h> #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/uts.h> #include <linux/version.h> #include <linux/device.h> #include <linux/moduleparam.h>
#include <asm/byteorder.h> #include <asm/io.h> #include <asm/irq.h> #include <asm/system.h> #include <asm/unaligned.h>
#include <linux/usb_ch9.h> #include <linux/usb_gadget.h>
/* This driver is OVERSIMPLIFIED and completely unrealistic, * because it doesn't support: * * - anything except enumeration to a nop configuration * - hardware other than the pxa2xx udc * - error checking/handling; fault logging; diagnostics; ... * - endpoints other then ep0 * - string descriptors * - non-spotty usb-if conformance * - ... any useful functionality * * DO NOT BASE REAL DRIVERS ON THIS CODE. */
#define USB_BUFSIZ 256
struct simple_dev { struct usb_request *req; };
/* these descriptors embed pxa-specific knowledge */ static const 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, .bMaxPacketSize0 = 16,
.bNumConfigurations = 1, };
static const struct usb_config_descriptor the_config = { .bLength = sizeof the_config, .bDescriptorType = USB_DT_CONFIG,
.bNumInterfaces = 1, .bConfigurationValue = 1, .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, .bMaxPower = (100 + 1) / 2, };
static const struct usb_interface_descriptor the_intf = { .bLength = sizeof the_intf, .bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2, .bInterfaceClass = USB_CLASS_VENDOR_SPEC, };
static int config_buf (enum usb_device_speed speed, u8 *buf, u8 type, unsigned index) { const unsigned config_len = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE;
/* config */ memcpy (buf, &the_config, USB_DT_CONFIG_SIZE); buf [1] = type; ((struct usb_config_descriptor *) buf)->wTotalLength = __constant_cpu_to_le16 (config_len); buf += USB_DT_CONFIG_SIZE;
/* one interface */ memcpy (buf, &the_intf, USB_DT_INTERFACE_SIZE); buf += USB_DT_INTERFACE_SIZE;
/* no endpoints */
return config_len; }
static void simple_setup_complete (struct usb_ep *ep, struct usb_request *req) { }
static int simple_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) { struct simple_dev *dev = get_gadget_data (gadget); struct usb_request *req = dev->req; int value = -EOPNOTSUPP;
/* this takes shortcuts and embeds pxa-specific knowledge */
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR: switch (ctrl->wValue >> 8) {
case USB_DT_DEVICE: value = min (ctrl->wLength, (u16) sizeof device_desc); memcpy (req->buf, &device_desc, value); break; case USB_DT_CONFIG: value = config_buf (gadget->speed, req->buf, ctrl->wValue >> 8, ctrl->wValue & 0xff); if (value >= 0) value = min (ctrl->wLength, (u16) value); break; } break;
/* our set_{config,interface} is a NOP; * pxa hw handles get_config and get_interface */ case USB_REQ_SET_CONFIGURATION: case USB_REQ_SET_INTERFACE: value = 0; break; }
if (value >= 0) { req->length = value; (void) usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); } return value; }
static void simple_disconnect (struct usb_gadget *gadget) { }
static void simple_unbind (struct usb_gadget *gadget) { struct simple_dev *dev = get_gadget_data (gadget); struct usb_request *req = dev->req;
if (req) { if (req->buf) usb_ep_free_buffer (gadget->ep0, req->buf, req->dma, req->length); usb_ep_free_request (gadget->ep0, req); } kfree (dev); set_gadget_data (gadget, 0); }
static int simple_bind (struct usb_gadget *gadget) { struct simple_dev *dev;
dev = kmalloc (sizeof *dev, SLAB_KERNEL); if (!dev) return -ENOMEM; memset (dev, 0, sizeof *dev); set_gadget_data (gadget, dev);
dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); if (!dev->req) goto enomem; dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, &dev->req->dma, GFP_KERNEL); if (!dev->req->buf) goto enomem; dev->req->complete = simple_setup_complete; gadget->ep0->driver_data = dev;
return 0;
enomem: simple_unbind (gadget); return -ENOMEM; }
static struct usb_gadget_driver simple_driver = { .speed = USB_SPEED_FULL, .function = "oversimplified example", .bind = simple_bind, .unbind = simple_unbind,
.setup = simple_setup, .disconnect = simple_disconnect,
.driver = { .name = "simple", }, };
static int __init init (void) { return usb_gadget_register_driver (&simple_driver); } module_init (init);
static void __exit cleanup (void) { usb_gadget_unregister_driver (&simple_driver); } module_exit (cleanup);
------------------------------------------------------- This sf.net email is sponsored by:ThinkGeek Welcome to geek heaven. http://thinkgeek.com/sf _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel