Linux Gadget的一点研究之例程分析

来源:互联网 发布:淘宝网茶具套装价格 编辑:程序博客网 时间:2024/06/11 05:40

学习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功能驱动。

在放这个例程前,为方便阅读,我更改部分代码格式。

/*  * zero.c -- Gadget Zero, for simple USB development * All rights reserved. */ /* #define VERBOSE_DEBUG */#include <linux/kernel.h>#include <linux/utsname.h>#include <linux/device.h>#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>#include "gadget_chips.h"#include <linux/slab.h>#include <linux/module.h>#include <linux/init.h>#include <linux/usb/input.h>#include <linux/cdev.h>#include <asm/uaccess.h>#include <linux/fs.h>#include <linux/poll.h>#include <linux/types.h> /* size_t */#include <linux/errno.h> /* error codes */#include <asm/system.h>#include <asm/io.h>#include <linux/sched.h>/*-------------------------------------------------------------------------*/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 0x0525 /* NetChip *///#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */#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; /* sink *//*-------------------------------------------------------------------------*//* big enough to hold our biggest descriptor */#define USB_BUFSIZ 256struct zero_dev {//zero设备结构spinlock_t lock;struct usb_gadget *gadget;struct usb_request *req; /* for control responses */struct usb_ep *out_ep;struct cdev cdev;unsigned char data[128];unsigned int data_size;wait_queue_head_t bulkrq; };#define CONFIG_LOOPBACK 2static 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,/* compute wTotalLength on the fly */.bNumInterfaces = 1,.bConfigurationValue = CONFIG_LOOPBACK,.iConfiguration = STRING_LOOPBACK,.bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,.bMaxPower = 1, /* self-powered */ }; 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,};/* static strings, in UTF-8 */#define STRING_MANUFACTURER 25#define STRING_PRODUCT 42#define STRING_SERIAL 101#define STRING_SOURCE_SINK 250#define STRING_LOOPBACK 251static char manufacturer[50];/* default serial number takes at least two packets */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, },{ } /* end of list */};static struct usb_gadget_strings stringtab = {.language = 0x0409, /* en-us */.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: /* normal completion */if (ep == dev->out_ep) {memcpy(dev->data, req->buf, req->actual);//返回数据拷贝到req->buf中, //dev->data_size=req->length; dev->data_size=req->actual; //实际长度为req->actual;需要确认req –>short_not_ok为0。参考gadget.h中关于usb_request结构的注释} break;/* this endpoint is normally active while we're configured */case -ECONNABORTED: /* hardware forced ep reset */case -ECONNRESET: /* request dequeued */case -ESHUTDOWN: /* disconnect from host */printk("%s gone (%d), %d/%d/n", ep->name, status,req->actual, req->length);case -EOVERFLOW: /* buffer overrun on read means that * we didn't provide a big enough * buffer. */default:#if 1printk("%s complete --> %d, %d/%d/n", ep->name,status, req->actual, req->length);        #endifcase -EREMOTEIO: /* short read */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;//printk("in %s/n",__FUNCTION__);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)//配置端点0的请求完成处理{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 is_source_sink;int len;const struct usb_descriptor_header **function;int hs = 0;function =fs_loopback_function;//根据fs_loopback_function,得到长度,//此处len=配置(9)+1个接口(9)+1个端点(7)=25len = 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); //激活端点//printk("");if (result == 0) {            printk("connected/n"); //如果成功,打印“connected”} 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;}/* *zero_setup完成USB设置阶段和具体功能相关的交互部分 */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);/* usually this stores reply data in the pre-allocated ep0 buffer, * but config change events will reconfigure hardware. */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: //获取配置,注意:会根据fs_loopback_function读取到接口、端点描述符,注意通过config_buf完成读取数据及数量的统计。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);}/* respond with data transfer before status phase */if (value >= 0) {req->length = value;req->zero = value < w_length;value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);//通过端点0完成setupif (value < 0) {printk("ep_queue --> %d/n", value);req->status = 0;zero_setup_complete(gadget->ep0, req);}}  /* device either stalls (value < 0) or reports success */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));/* 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);}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; //记录端点(就是接收host端数据的端点)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 = { //gadget驱动的核心数据结构#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,    //.suspend = zero_suspend, //不考虑电源管理的功能     //.resume = zero_resume,    .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); //注册驱动,调用bind绑定到控制器 }module_init(init);static void __exit cleanup(void){usb_gadget_unregister_driver(&zero_driver); //注销驱动,通常会调用到unbind解除绑定, //在s3c2410_udc.c中调用的是disconnect方法 }module_exit(cleanup);

原创粉丝点击