学习Gadget比较有效的办法是掌握基本架构后,认真研读例程。其实不单Gadget如此,其他Linux驱动或子系统都是如此。另外Linux下的外设驱动通常有分层的概念,有带有面向对象的思想,因此研读代码是比较有效的领悟办法。
一般一个Linux的USB设备驱动,包括两大部分,一是CPU USB控制器部分的驱动,驱动文件名往往是xxx_udc.c,此部分驱动很多是与硬件CPU相关,包含寄存器设置、DMA设置,同时此部分也柔和了USB Gadget架构的东西,通常此部分代码是比较枯燥且难于理解的,每个CPU平台,此部分代码差异很大,但幸好此部分代码一般厂商会提供。二是gadget功能驱动,如果说USB控制器驱动是肉体,则此部分是灵魂,USB要在通信中是属于什么类型设备(HID?键盘?U盘?),怎样传输等等都是在此部分驱动中实现的。
Linux内核源码driver/usb/gadget/zero.c是一个很好的gadget功能驱动框架,本来内核开发者是想通过这个驱动框架来来方便第三方USB控制器驱动开发者测试UDC驱动的。
先上一个例程,这个例程是华清远见刘洪涛老师在阐述USB驱动部分写的一个例程,尽管例程尚有很多可优化、冗余的东西,尽管例程中描述注册字符设备驱动的方式是陈旧的方式,但是这个例程还是精彩阐述的如何开发一个gadget功能驱动。
在放这个例程前,为方便阅读,我更改部分代码格式。
-
-
-
-
-
-
-
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include "gadget_chips.h"
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include
-
#include /* size_t */
-
#include /* error codes */
-
#include
-
#include
-
#include
-
-
-
static const char shortname[] = "zero";
-
static const char loopback[] = "loop input to output";
-
static const char longname[] = "Gadget Zero";
-
static const char source_sink[] = "source and sink data";
-
#define STRING_MANUFACTURER 25
-
#define STRING_PRODUCT 42
-
#define STRING_SERIAL 101
-
#define STRING_SOURCE_SINK 250
-
#define STRING_LOOPBACK 251
-
-
-
-
#define DRIVER_VENDOR_NUM 0x5345 /* NetChip */
-
#define DRIVER_PRODUCT_NUM 0x1234 /* Linux-USB "Gadget Zero" */
-
-
static int usb_zero_major = 251;
-
-
static const char *EP_OUT_NAME;
-
-
-
-
#define USB_BUFSIZ 256
-
struct zero_dev
-
{
-
-
spinlock_t lock;
-
struct usb_gadget *gadget;
-
struct usb_request *req;
-
struct usb_ep *out_ep;
-
struct cdev cdev;
-
unsigned char data[128];
-
unsigned int data_size;
-
wait_queue_head_t bulkrq;
-
};
-
#define CONFIG_LOOPBACK 2
-
static struct usb_device_descriptor device_desc =
-
{
-
-
.bLength = sizeof device_desc,
-
.bDescriptorType = USB_DT_DEVICE,
-
.bcdUSB = __constant_cpu_to_le16(0x0110),
-
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
-
.idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
-
.idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
-
.iManufacturer = STRING_MANUFACTURER,
-
.iProduct = STRING_PRODUCT,
-
.iSerialNumber = STRING_SERIAL,
-
.bNumConfigurations = 1,
-
};
-
static struct usb_endpoint_descriptor fs_sink_desc =
-
{
-
-
.bLength = USB_DT_ENDPOINT_SIZE,
-
.bDescriptorType = USB_DT_ENDPOINT,
-
.bEndpointAddress = USB_DIR_OUT,
-
.bmAttributes = USB_ENDPOINT_XFER_BULK,
-
};
-
-
static struct usb_config_descriptor loopback_config =
-
{
-
-
.bLength = sizeof loopback_config,
-
.bDescriptorType = USB_DT_CONFIG,
-
-
.bNumInterfaces = 1,
-
.bConfigurationValue = CONFIG_LOOPBACK,
-
.iConfiguration = STRING_LOOPBACK,
-
.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
-
.bMaxPower = 1,
-
};
-
-
static const struct usb_interface_descriptor loopback_intf =
-
{
-
-
.bLength = sizeof loopback_intf,
-
.bDescriptorType = USB_DT_INTERFACE,
-
.bNumEndpoints = 1,
-
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
-
.iInterface = STRING_LOOPBACK,
-
};
-
-
-
#define STRING_MANUFACTURER 25
-
#define STRING_PRODUCT 42
-
#define STRING_SERIAL 101
-
#define STRING_SOURCE_SINK 250
-
#define STRING_LOOPBACK 251
-
static char manufacturer[50];
-
-
static char serial[] = "0123456789.0123456789.0123456789";
-
static struct usb_string strings[] =
-
{
-
-
{ STRING_MANUFACTURER, manufacturer, },
-
{ STRING_PRODUCT, longname, },
-
{ STRING_SERIAL, serial, },
-
{ STRING_LOOPBACK, loopback, },
-
{ STRING_SOURCE_SINK, source_sink, },
-
{ }
-
};
-
-
static struct usb_gadget_strings stringtab =
-
{
-
.language = 0x0409,
-
.strings = strings,
-
};
-
-
static const struct usb_descriptor_header *fs_loopback_function[] =
-
{
-
(struct usb_descriptor_header *) &loopback_intf,
-
(struct usb_descriptor_header *) &fs_sink_desc,
-
NULL,
-
};
-
-
-
static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
-
{
-
kfree(req->buf);
-
usb_ep_free_request(ep, req);
-
}
-
-
static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
-
{
-
struct usb_request *req;
-
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
-
if (req)
-
{
-
req->length = length;
-
req->buf = kmalloc(length, GFP_ATOMIC);
-
if (!req->buf)
-
{
-
usb_ep_free_request(ep, req);
-
req = NULL;
-
}
-
}
-
return req;
-
}
-
-
static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
-
{
-
struct zero_dev *dev = ep->driver_data;
-
int status = req->status;
-
switch (status)
-
{
-
case 0:
-
if (ep == dev->out_ep)
-
{
-
memcpy(dev->data, req->buf, req->actual);
-
dev->data_size=req->actual;
-
}
-
break;
-
-
case -ECONNABORTED:
-
case -ECONNRESET:
-
case -ESHUTDOWN:
-
printk("%s gone (%d), %d/%d/n", ep->name, status,req->actual, req->length);
-
case -EOVERFLOW:
-
-
-
-
-
default:
-
#if 1
-
printk("%s complete --> %d, %d/%d/n", ep->name,
-
status, req->actual, req->length);
-
#endif
-
case -EREMOTEIO:
-
break;
-
}
-
free_ep_req(ep, req);
-
wake_up_interruptible (&dev->bulkrq);
-
}
-
-
static struct usb_request *source_sink_start_ep(struct usb_ep *ep)
-
{
-
struct usb_request *req;
-
int status;
-
-
req = alloc_ep_req(ep, 128);
-
if (!req)
-
return NULL;
-
memset(req->buf, 0, req->length);
-
req->complete = source_sink_complete;
-
status = usb_ep_queue(ep, req, GFP_ATOMIC);
-
if (status)
-
{
-
struct zero_dev *dev = ep->driver_data;
-
printk("start %s --> %d/n", ep->name, status);
-
free_ep_req(ep, req);
-
req = NULL;
-
}
-
return req;
-
}
-
-
static int usb_zero_open (struct inode *inode, struct file *file)
-
{
-
struct zero_dev *dev = container_of (inode->i_cdev, struct zero_dev, cdev);
-
file->private_data = dev;
-
init_waitqueue_head (&dev->bulkrq);
-
return 0;
-
}
-
-
static int usb_zero_release (struct inode *inode, struct file *file)
-
{
-
return 0;
-
}
-
-
ssize_t usb_zero_read (struct file * file, const char __user * buf, size_t count,loff_t * f_pos)
-
{
-
struct zero_dev *dev =file->private_data;
-
struct usb_request *req;
-
int status;
-
struct usb_ep *ep;
-
struct usb_gadget *gadget = dev->gadget;
-
ssize_t ret = 0;
-
int result;
-
ep=dev->out_ep;
-
source_sink_start_ep(ep);
-
if (count < 0)
-
return -EINVAL;
-
interruptible_sleep_on (&dev->bulkrq);
-
if (copy_to_user (buf,dev->data,dev->data_size))
-
{
-
ret = -EFAULT;
-
}
-
else
-
{
-
ret = dev->data_size;
-
}
-
return ret;
-
}
-
-
struct file_operations usb_zero_fops =
-
{
-
.owner = THIS_MODULE,
-
.read = usb_zero_read,
-
.open = usb_zero_open,
-
.release = usb_zero_release,
-
};
-
-
static void usb_zero_setup_cdev (struct zero_dev *dev, int minor)
-
{
-
int err, devno = MKDEV (usb_zero_major, minor);
-
cdev_init(&dev->cdev, &usb_zero_fops);
-
dev->cdev.owner = THIS_MODULE;
-
err = cdev_add (&dev->cdev, devno, 1);
-
if (err)
-
printk ("Error adding usb_rcv/n");
-
}
-
-
static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req)
-
{
-
if (req->status || req->actual != req->length)
-
printk("setup complete --> %d, %d/%d/n",req->status, req->actual, req->length);
-
}
-
static void zero_reset_config(struct zero_dev *dev)
-
{
-
usb_ep_disable(dev->out_ep);
-
dev->out_ep = NULL;
-
}
-
-
static void zero_disconnect(struct usb_gadget *gadget)
-
{
-
struct zero_dev *dev = get_gadget_data(gadget);
-
unsigned long flags;
-
unregister_chrdev_region (MKDEV (usb_zero_major, 0), 1);
-
cdev_del (&(dev->cdev));
-
zero_reset_config(dev);
-
printk("in %s/n",__FUNCTION__);
-
}
-
-
static int config_buf(struct usb_gadget *gadget,u8 *buf, u8 type, unsigned index)
-
{
-
-
int len;
-
const struct usb_descriptor_header **function;
-
int hs = 0;
-
function =fs_loopback_function;
-
len = usb_gadget_config_buf(&loopback_config,buf, USB_BUFSIZ, function);
-
if (len < 0)
-
return len;
-
((struct usb_config_descriptor *) buf)->bDescriptorType = type;
-
return len;
-
}
-
-
static int set_loopback_config(struct zero_dev *dev)
-
{
-
int result = 0;
-
struct usb_ep *ep;
-
struct usb_gadget *gadget = dev->gadget;
-
ep=dev->out_ep;
-
const struct usb_endpoint_descriptor *d;
-
d = &fs_sink_desc;
-
result = usb_ep_enable(ep, d);
-
-
if (result == 0)
-
{
-
printk("connected/n");
-
}
-
else
-
printk("can't enable %s, result %d/n", ep->name, result);
-
return result;
-
}
-
-
static int zero_set_config(struct zero_dev *dev, unsigned number)
-
{
-
int result = 0;
-
struct usb_gadget *gadget = dev->gadget;
-
result = set_loopback_config(dev);
-
if (result)
-
zero_reset_config(dev);
-
else
-
{
-
char *speed;
-
-
switch (gadget->speed)
-
{
-
case USB_SPEED_LOW: speed = "low"; break;
-
case USB_SPEED_FULL: speed = "full"; break;
-
case USB_SPEED_HIGH: speed = "high"; break;
-
default: speed = " "; break;
-
}
-
}
-
return result;
-
}
-
-
-
-
-
static int zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
-
{
-
struct zero_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;
-
-
switch (ctrl->bRequest)
-
{
-
case USB_REQ_GET_DESCRIPTOR:
-
if (ctrl->bRequestType != USB_DIR_IN)
-
goto unknown;
-
switch (w_value >> 8)
-
{
-
case USB_DT_DEVICE:
-
value = min(w_length, (u16) sizeof device_desc);
-
memcpy(req->buf, &device_desc, value);
-
break;
-
case USB_DT_CONFIG:
-
value = config_buf(gadget, req->buf,
-
w_value >> 8,
-
w_value & 0xff);
-
if (value >= 0)
-
value = min(w_length, (u16) value);
-
break;
-
case USB_DT_STRING:
-
value = usb_gadget_get_string(&stringtab,w_value & 0xff, req->buf);
-
if (value >= 0)
-
value = min(w_length, (u16) value);
-
break;
-
}
-
break;
-
-
case USB_REQ_SET_CONFIGURATION:
-
if (ctrl->bRequestType != 0)
-
goto unknown;
-
spin_lock(&dev->lock);
-
value = zero_set_config(dev, w_value);
-
spin_unlock(&dev->lock);
-
break;
-
default:
-
unknown:
-
printk(
-
"unknown control req%02x.%02x v%04x i%04x l%d/n",
-
ctrl->bRequestType, ctrl->bRequest,
-
w_value, w_index, w_length);
-
}
-
-
if (value >= 0)
-
{
-
req->length = value;
-
req->zero = value < w_length;
-
value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
-
if (value < 0)
-
{
-
printk("ep_queue --> %d/n", value);
-
req->status = 0;
-
zero_setup_complete(gadget->ep0, req);
-
}
-
}
-
-
return value;
-
}
-
static void zero_unbind(struct usb_gadget *gadget)
-
{
-
struct zero_dev *dev = get_gadget_data(gadget);
-
printk("unbind/n");
-
unregister_chrdev_region (MKDEV (usb_zero_major, 0), 1);
-
cdev_del (&(dev->cdev));
-
-
if (dev->req)
-
{
-
dev->req->length = USB_BUFSIZ;
-
free_ep_req(gadget->ep0, dev->req);
-
}
-
kfree(dev);
-
set_gadget_data(gadget, NULL);
-
}
-
-
static int __init zero_bind(struct usb_gadget *gadget)
-
{
-
struct zero_dev *dev;
-
struct usb_ep *ep;
-
int gcnum;
-
usb_ep_autoconfig_reset(gadget);
-
ep = usb_ep_autoconfig(gadget, &fs_sink_desc);
-
if (!ep)
-
goto enomem;
-
EP_OUT_NAME = ep->name;
-
gcnum = usb_gadget_controller_number(gadget);
-
if (gcnum >= 0)
-
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
-
else
-
{
-
pr_warning("%s: controller '%s' not recognized/n",shortname, gadget->name);
-
device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
-
}
-
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-
if (!dev)
-
return -ENOMEM;
-
spin_lock_init(&dev->lock);
-
dev->gadget = gadget;
-
set_gadget_data(gadget, dev);
-
dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
-
if (!dev->req)
-
goto enomem;
-
dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
-
if (!dev->req->buf)
-
goto enomem;
-
dev->req->complete = zero_setup_complete;
-
dev->out_ep=ep;
-
printk("name=%s/n",dev->out_ep->name);
-
ep->driver_data=dev;
-
device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
-
usb_gadget_set_selfpowered(gadget);
-
gadget->ep0->driver_data = dev;
-
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",init_utsname()->sysname, init_utsname()->release,gadget->name);
-
-
dev_t usb_zero_dev = MKDEV (usb_zero_major, 0);
-
int result = register_chrdev_region (usb_zero_dev, 1, "usb_zero");
-
if (result < 0)
-
{
-
printk (KERN_NOTICE "Unable to get usb_transfer region, error %d/n",result);
-
return 0;
-
}
-
usb_zero_setup_cdev (dev, 0);
-
return 0;
-
enomem:
-
zero_unbind(gadget);
-
return -ENOMEM;
-
}
-
-
static struct usb_gadget_driver zero_driver =
-
{
-
-
#ifdef CONFIG_USB_GADGET_DUALSPEED
-
.speed = USB_SPEED_HIGH,
-
#else
-
.speed = USB_SPEED_FULL,
-
#endif
-
.function = (char *) longname,
-
.bind = zero_bind,
-
.unbind = __exit_p(zero_unbind),
-
.setup = zero_setup,
-
.disconnect = zero_disconnect,
-
-
-
.driver = {
-
.name = (char *) shortname,
-
.owner = THIS_MODULE,
-
},
-
};
-
MODULE_AUTHOR("David Brownell");
-
MODULE_LICENSE("GPL");
-
static int __init init(void)
-
{
-
return usb_gadget_register_driver(&zero_driver);
-
}
-
module_init(init);
-
-
static void __exit cleanup(void)
-
{
-
usb_gadget_unregister_driver(&zero_driver);
-
}
-
module_exit(cleanup);
-
阅读(2280) | 评论(0) | 转发(1) |