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
