一、Linux USB Gadget Driver功能
为了与主机端驱动设备的USB Device Driver概念进行区别,将在外围器件中运行的驱动程序称为USB Gadget Driver。其中,Host端驱动设备的驱动程序是master或者client driver,设备端gadget driver是slave或者function driver。
Gadget Driver和USB Host端驱动程序类似,都是使用请求队列来对I/O包进行缓冲,这些请求可以被提交和取消。它们的结构、消息和常量的定义也和USB技术规范第九章的内容一致。同时也是通过bind和unbind将driver与device建立关系。
二、Linux USB Gadget Driver核心数据结构
1. USB_Gadget对象
struct usb_gadget {
/* readonly to gadget driver */
const struct usb_gadget_ops *ops; //Gadget设备操作函数集
struct usb_ep *ep0; //控制端点,只对setup包响应
struct list_head ep_list;//将设备的所有端点连成链表,ep0不在其中
enum usb_device_speed speed;//高速、全速和低速
unsigned is_dualspeed:1; //是否同时支持高速和全速
unsigned is_otg:1; //是否支持OTG(On-To-Go)
unsigned is_a_peripheral:1;
unsigned b_hnp_enable:1;
unsigned a_hnp_support:1;
unsigned a_alt_hnp_support:1;
const char *name; //器件名称
struct device dev; //内核设备模型使用
};
2. Gadget器件操作函数集
操作UDC硬件的API,但操作端点的函数由端点操作函数集完成
struct usb_gadget_ops {
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
int (*vbus_session) (struct usb_gadget *, int is_active);
int (*vbus_draw) (struct usb_gadget *, unsigned mA);
int (*pullup) (struct usb_gadget *, int is_on);
int (*ioctl)(struct usb_gadget *, unsigned code, unsigned long param);
};
3. USB Gadget driver对象
struct usb_gadget_driver {
char *function; //驱动名称
enum usb_device_speed speed; //USB设备速度类型
int (*bind)(struct usb_gadget *); //将驱动和设备绑定,一般在驱动注册时调用
void (*unbind)(struct usb_gadget *);//卸载驱动时调用,rmmod时调用
int (*setup)(struct usb_gadget *, const struct usb_ctrlrequest *); //处理ep0的控制请求,在中断中调用,不能睡眠
void (*disconnect)(struct usb_gadget *); //可能在中断中调用不能睡眠
void (*suspend)(struct usb_gadget *); //电源管理模式相关,设备挂起
void (*resume)(struct usb_gadget *);//电源管理模式相关,设备恢复
/* FIXME support safe rmmod */
struct device_driver driver; //内核设备管理使用
};
4. 描述一个I/O请求
struct usb_request {
void *buf; //数据缓存区
unsigned length; //数据长度
dma_addr_t dma; //与buf关联的DMA地址,DMA传输时使用
unsigned no_interrupt:1;//当为true时,表示没有完成函数,则通过中断通知传输完成,这个由DMA控制器直接控制
unsigned zero:1; //当输出的最后一个数据包不够长度是是否填充0
unsigned short_not_ok:1; //当接收的数据不够指定长度时,是否报错
void (*complete)(struct usb_ep *ep, struct usb_request *req);//请求完成函数
void *context;//被completion回调函数使用
struct list_head list; //被Gadget Driver使用,插入队列
int status;//返回完成结果,0表示成功
unsigned actual;//实际传输的数据长度
};
5. 端点
struct usb_ep {
void *driver_data; //端点私有数据
const char *name; //端点名称
const struct usb_ep_ops *ops; //端点操作函数集
struct list_head ep_list; //Gadget设备建立所有端点的链表
unsigned maxpacket:16;//这个端点使用的最大包长度
};
6. 端点操作函数集
struct usb_ep_ops {
int (*enable) (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc);
int (*disable) (struct usb_ep *ep);
struct usb_request *(*alloc_request) (struct usb_ep *ep, gfp_t gfp_flags);
void (*free_request) (struct usb_ep *ep, struct usb_request *req);
int (*queue) (struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);
int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
int (*set_halt) (struct usb_ep *ep, int value);
int (*set_wedge) (struct usb_ep *ep);
int (*fifo_status) (struct usb_ep *ep);
void (*fifo_flush) (struct usb_ep *ep);
};
7. 字符串结构
struct usb_gadget_strings {
u16 language; /* 0x0409 for en-us */
struct usb_string *strings;
};
struct usb_string {
u8 id; //索引
const char *s;
};
8. UDC驱动程序需要实现的上层调用接口
int usb_gadget_register_driver(struct usb_gadget_driver *driver);
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
三、UDC驱动程序
1. UDC层主要数据结构,以S3C2410为例,在driver/usb/gadget/s3c2410_udc.c和s3c2410_udc.h文件中。
下面的结构基本上每个UDC驱动程序都会实现,但具体实现的细节又不太相同。但万变不离其宗,宗就是上面介绍的基本gadget驱动数据结构,基本上UDC驱动程序自己实现的数据结构都是都这些基本数据结构的二次封装。
a. 设备结构
struct s3c2410_udc {
spinlock_t lock;
struct s3c2410_ep ep[S3C2410_ENDPOINTS];
int address;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct s3c2410_request fifo_req;
u8 fifo_buf[EP_FIFO_SIZE];
u16 devstatus;
u32 port_status;
int ep0state;
unsigned got_irq : 1;
unsigned req_std : 1;
unsigned req_config : 1;
unsigned req_pending : 1;
u8 vbus;
struct dentry *regs_info;
};
程序中对这个结构的初始化:
static struct s3c2410_udc memory = {
.gadget = {
.ops = &s3c2410_ops,
.ep0 = &memory.ep[0].ep,
.name = gadget_name,
.dev = {
.init_name = "gadget",
},
},
/* control endpoint */
.ep[0] = { //struct s3c2410_ep
.num = 0,
.ep = {//struct usb_ep
.name = ep0name,
.ops = &s3c2410_ep_ops,
.maxpacket = EP0_FIFO_SIZE,
},
.dev = &memory,
},
/* first group of endpoints */
.ep[1] = {
.num = 1,
.ep = {
.name = "ep1-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[2] = {
.num = 2,
.ep = {
.name = "ep2-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[3] = {
.num = 3,
.ep = {
.name = "ep3-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 3,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
.ep[4] = {
.num = 4,
.ep = {
.name = "ep4-bulk",
.ops = &s3c2410_ep_ops,
.maxpacket = EP_FIFO_SIZE,
},
.dev = &memory,
.fifo_size = EP_FIFO_SIZE,
.bEndpointAddress = 4,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
}
};
不同的UDC,自定义的数据结构不同。但一般都有这样一个数据结构来表示UDC设备,对usb_gadget设备对象进行封装,并包含设备的所有端点。
b. 端点结构
struct s3c2410_ep {
struct list_head queue;
unsigned long last_io; /* jiffies timestamp */
struct usb_gadget *gadget;
struct s3c2410_udc *dev;
const struct usb_endpoint_descriptor *desc;
struct usb_ep ep; //封装的struct usb_ep结构
u8 num;
unsigned short fifo_size;
u8 bEndpointAddress;
u8 bmAttributes;
unsigned halted : 1;
unsigned already_seen : 1;
unsigned setup_stage : 1;
};
对usb_ep结构进行封装,并有一个queue队列来对该端口上的request进行排队。
c. Request结构
struct s3c2410_request {
struct list_head queue; /* ep's requests */
struct usb_request req;
};
对usb_request进行封装,queue变量进行队列排队。
USB设备驱动程序-USB Gadget Driver(二)
装载自:
http://blog.chinaunix.net/uid-14518381-id-3921457.html
1. UDC驱动是作为platform driver向platform子系统注册的,因此UDC驱动首先就需要实现struct platform_driver结构中的函数成员:
struct platform_driver {
int (*probe)(struct platform_device *); //驱动和设备绑定
int (*remove)(struct platform_device *); //支持热插拔的设备移除
void (*shutdown)(struct platform_device *); //设备关闭
int (*suspend)(struct platform_device *, pm_message_t state); //电源管理相关,挂起设备
int (*resume)(struct platform_device *); //电源管理相关,恢复设备
struct device_driver driver;
struct platform_device_id *id_table; //驱动和设备匹配信息
};
在下面的源码分析中以s3c2410_udc.c文件为例:
static struct platform_driver udc_driver_2410 = {
.driver = {
.name = "s3c2410-usbgadget",
.owner = THIS_MODULE,
},
.probe = s3c2410_udc_probe,
.remove = s3c2410_udc_remove,
.suspend = s3c2410_udc_suspend,
.resume = s3c2410_udc_resume,
};
其中以s3c2410_udc_probe和s3c2410_udc_remove函数最为重要,s3c2410_udc_probe函数实现驱动和设备的匹配绑定,并分配资源;而s3c2410_udc_remove函数实现资源的释放。
static int s3c2410_udc_probe(struct platform_device *pdev)
{
struct s3c2410_udc *udc = &memory; //s3c2410的UDC设备,在其中对usb_gadget设备对象和端点等进行了初始化
struct device *dev = &pdev->dev;
int retval;
int irq;
//获取总线时钟并使能
usb_bus_clock = clk_get(NULL, "usb-bus-gadget");
clk_enable(usb_bus_clock);
//获取设备时钟并使能
udc_clock = clk_get(NULL, "usb-device");
clk_enable(udc_clock);
mdelay(10);
spin_lock_init (&udc->lock); //初始化设备的自旋锁
udc_info = pdev->dev.platform_data;
rsrc_start = S3C2410_PA_USBDEV; //s3c2410 UDC端口起始地址
rsrc_len = S3C24XX_SZ_USBDEV; //s3c2410端口地址长度
if (!request_mem_region(rsrc_start, rsrc_len, gadget_name)) //申请端口资源
return -EBUSY;
base_addr = ioremap(rsrc_start, rsrc_len); //端口映射
if (!base_addr) {
retval = -ENOMEM;
goto err_mem;
}
device_initialize(&udc->gadget.dev); //初始化device设备对象
udc->gadget.dev.parent = &pdev->dev; //当前UDC设备的父设备对象是platform_device
udc->gadget.dev.dma_mask = pdev->dev.dma_mask;
the_controller = udc;
platform_set_drvdata(pdev, udc); //驱动和设备绑定,在platform_device结构中保存udc设备对象
/*重新初始化设备*/
s3c2410_udc_disable(udc);
s3c2410_udc_reinit(udc);
/* irq setup after old hardware state is cleaned up */
/*申请中断,并绑定中断函数,中断函数是UDC功能驱动的入口函数*/
retval = request_irq(IRQ_USBD, s3c2410_udc_irq, IRQF_DISABLED, gadget_name, udc);
if (udc_info && udc_info->vbus_pin > 0) {
retval = gpio_request(udc_info->vbus_pin, "udc vbus");
irq = gpio_to_irq(udc_info->vbus_pin);
retval = request_irq(irq, s3c2410_udc_vbus_irq, IRQF_DISABLED | IRQF_TRIGGER_RISING| IRQF_TRIGGER_FALLING | IRQF_SHARED,gadget_name, udc);
}
else {
udc->vbus = 1;
}
if (s3c2410_udc_debugfs_root) { //创建虚拟文件debugfs
udc->regs_info = debugfs_create_file("registers", S_IRUGO, s3c2410_udc_debugfs_root,udc, &s3c2410_udc_debugfs_fops);
if (!udc->regs_info)
dev_warn(dev, "debugfs file creation failed\n");
}
dev_dbg(dev, "probe ok\n");
return 0;
err_gpio_claim:
if (udc_info && udc_info->vbus_pin > 0)
gpio_free(udc_info->vbus_pin);
err_int:
free_irq(IRQ_USBD, udc);
err_map:
iounmap(base_addr);
err_mem:
release_mem_region(rsrc_start, rsrc_len);
return retval;
}
从s3c2410_udc_probe函数可以看出,probe函数主要完成的就是将platform_device设备对象和UDC设备对象建立关系,UDC设备和驱动的一些初始化工作,并申请驱动所需的资源,若端口区间、中断号等,并将中断函数和中断号绑定。这个中断处理函数非常重要,对这个设备的所有操作都将从中断函数入口。
static int s3c2410_udc_remove(struct platform_device *pdev)
{
struct s3c2410_udc *udc = platform_get_drvdata(pdev); //获取UDC设备对象,在probe函数中绑定的
unsigned int irq;
if (udc->driver) //设备的驱动usb_gadget_driver对象,说明设备正在使用
return -EBUSY;
debugfs_remove(udc->regs_info); //移除debugfs文件系统中建立的文件
if (udc_info && udc_info->vbus_pin > 0) {
irq = gpio_to_irq(udc_info->vbus_pin);
free_irq(irq, udc);
}
free_irq(IRQ_USBD, udc); //释放中断
/*释放端口资源*/
iounmap(base_addr);
release_mem_region(rsrc_start, rsrc_len);
/*解除绑定*/
platform_set_drvdata(pdev, NULL);
/*释放时钟*/
if (!IS_ERR(udc_clock) && udc_clock != NULL) {
clk_disable(udc_clock);
clk_put(udc_clock);
udc_clock = NULL;
}
if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
clk_disable(usb_bus_clock);
clk_put(usb_bus_clock);
usb_bus_clock = NULL;
}
dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);
return 0;
}
可以看出,remove函数基本上是probe函数的逆操作,将probe函数中申请的资源释放掉。
2. UDC驱动程序还需要为上层实现usb_gadget_register_driver和usb_gadget_unregister_driver两个gadget driver注册接口,这两个函数将实现gadget driver和udc driver绑定。
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
struct s3c2410_udc *udc = the_controller; //UDC设备对象
int retval;
/* Sanity checks */
if (!udc)
return -ENODEV;
/*UDC设备只能绑定一个gadget driver对象*/
if (udc->driver)
return -EBUSY;
/*检查gadget driver是否实现了绑定函数、setup函数。同时低速设备也不支持gadget driver*/
if (!driver->bind || !driver->setup|| driver->speed < USB_SPEED_FULL) {
printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",driver->bind, driver->setup, driver->speed);
return -EINVAL;
}
//支持卸载的话,还需要实现unbind函数
#if defined(MODULE)
if (!driver->unbind) {
printk(KERN_ERR "Invalid driver: no unbind method\n");
return -EINVAL;
}
#endif
/* Hook the driver */
/*将gadget driver和udc设备绑定*/
udc->driver = driver;
udc->gadget.dev.driver = &driver->driver;
/* Bind the driver */
if ((retval = device_add(&udc->gadget.dev)) != 0) { //完成gadget设备在内核中的注册
printk(KERN_ERR "Error in device_add() : %d\n",retval);
goto register_error;
}
if ((retval = driver->bind (&udc->gadget)) != 0) {//gadget驱动绑定函数
device_del(&udc->gadget.dev);
goto register_error;
}
/* Enable udc */
//使能UDC设备
s3c2410_udc_enable(udc);
return 0;
register_error:
udc->driver = NULL;
udc->gadget.dev.driver = NULL;
return retval;
}
/*gadget 驱动注销函数*/
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
struct s3c2410_udc *udc = the_controller;
if (!udc)
return -ENODEV;
/*驱动必须和注册时的驱动是一致的,同时实现了unbind函数*/
if (!driver || driver != udc->driver || !driver->unbind)
return -EINVAL;
dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", driver->driver.name);
/*调用gadget driver实现的unbind函数*/
driver->unbind(&udc->gadget);
device_del(&udc->gadget.dev); //和register函数中的device_add对应
udc->driver = NULL;//这个很重要,这里说明gadget驱动注销之后,才能移除udc设备
/* Disable udc */
/*关闭设备*/
s3c2410_udc_disable(udc);
return 0;
}
3. 中断函数
中断处理函数是UDC驱动层的核心函数,由于UDC是从设备,主机端是控制端,所有的操作都是主机端发起的,所以中断处理函数是UDC驱动层的核心函数。
static irqreturn_t s3c2410_udc_irq(int dummy, void *_dev)
{
struct s3c2410_udc *dev = _dev; //UDC设备对象
int usb_status;
int usbd_status;
int pwr_reg;
int ep0csr;
int i;
u32 idx;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
/* Driver connected ? */
if (!dev->driver) { //还没有和驱动绑定,清除中断
/* Clear interrupts */
udc_write(udc_read(S3C2410_UDC_USB_INT_REG), S3C2410_UDC_USB_INT_REG);
udc_write(udc_read(S3C2410_UDC_EP_INT_REG),S3C2410_UDC_EP_INT_REG);
}
/* Save index */
idx = udc_read(S3C2410_UDC_INDEX_REG); //这是哪个端点产生中断的索引号
/* Read status registers */
usb_status = udc_read(S3C2410_UDC_USB_INT_REG); //UDC设备状态
usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); //UDC产生中断的端点状态
pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", usb_status, usbd_status, pwr_reg, ep0csr);
/*
开始中断的实际处理,这里的中断只有两种类型:
1. UDC设备中断: 重置、挂起和恢复
2. 端点中断
*/
/* UDC设备RESET操作处理 */
if (usb_status & S3C2410_UDC_USBINT_RESET) { //Reset中断
/* two kind of reset :
* - reset start -> pwr reg = 8
* - reset end -> pwr reg = 0
**/
dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", ep0csr, pwr_reg);
dev->gadget.speed = USB_SPEED_UNKNOWN;
udc_write(0x00, S3C2410_UDC_INDEX_REG);
udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, S3C2410_UDC_MAXP_REG);
dev->address = 0;
dev->ep0state = EP0_IDLE;
dev->gadget.speed = USB_SPEED_FULL;
/* clear interrupt */
udc_write(S3C2410_UDC_USBINT_RESET, S3C2410_UDC_USB_INT_REG);
udc_write(idx, S3C2410_UDC_INDEX_REG);
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
/* UDC设备RESUME操作处理 */
if (usb_status & S3C2410_UDC_USBINT_RESUME) { //Resume中断
dprintk(DEBUG_NORMAL, "USB resume\n");
/* clear interrupt */
udc_write(S3C2410_UDC_USBINT_RESUME, S3C2410_UDC_USB_INT_REG);
if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->resume)//调用resume函数
dev->driver->resume(&dev->gadget);
}
/* UDC设备SUSPEND操作处理 */
if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { //Suspend中断
dprintk(DEBUG_NORMAL, "USB suspend\n");
/* clear interrupt */
udc_write(S3C2410_UDC_USBINT_SUSPEND, S3C2410_UDC_USB_INT_REG);
if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver&& dev->driver->suspend)//调用suspend函数
dev->driver->suspend(&dev->gadget);
dev->ep0state = EP0_IDLE;
}
/* 下面就是端点中断得处理 */
/* 首先是控制端点(端点0)的中断处理*/
/* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready
* generate an interrupt
*/
if (usbd_status & S3C2410_UDC_INT_EP0) { //端点0的中断
dprintk(DEBUG_VERBOSE, "USB ep0 irq\n");
/* Clear the interrupt bit by setting it to 1 */
udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG);
s3c2410_udc_handle_ep0(dev); //处理端点0
}
/* 其他端点,就是数据传输的处理*/
for (i = 1; i < S3C2410_ENDPOINTS; i++) { //遍历所有端点,找出中断的端点
u32 tmp = 1 << i;
if (usbd_status & tmp) {
dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i);
/* Clear the interrupt bit by setting it to 1 */
udc_write(tmp, S3C2410_UDC_EP_INT_REG);
s3c2410_udc_handle_ep(&dev->ep[i]); //处理对应端点
}
}
dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", IRQ_USBD);
/* Restore old index */
udc_write(idx, S3C2410_UDC_INDEX_REG);
spin_unlock_irqrestore(&dev->lock, flags);
return IRQ_HANDLED;
}
4. 端点操作函数
端点操作函数是UDC驱动的基础,因为大部分的动作其实都是和端点相关的,如数据传输等。
首先来看中断函数中涉及的两个函数,一个是端点0的处理函数,一个是其他端点的处理函数。
static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
{
u32 ep0csr;
struct s3c2410_ep *ep = &dev->ep[0];
struct s3c2410_request *req;
struct usb_ctrlrequest crq;
if (list_empty(&ep->queue))
req = NULL;
else
req = list_entry(ep->queue.next, struct s3c2410_request, queue);
/* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to
* S3C2410_UDC_EP0_CSR_REG when index is zero */
udc_write(0, S3C2410_UDC_INDEX_REG);
ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", ep0csr, ep0states[dev->ep0state]);
/* clear stall status */
if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { //清除STALL状态
s3c2410_udc_nuke(dev, ep, -EPIPE);
dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n");
s3c2410_udc_clear_ep0_sst(base_addr);
dev->ep0state = EP0_IDLE;
return;
}
/* clear setup end */
if (ep0csr & S3C2410_UDC_EP0_CSR_SE) {
dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n");
s3c2410_udc_nuke(dev, ep, 0);
s3c2410_udc_clear_ep0_se(base_addr);
dev->ep0state = EP0_IDLE;
}
/*端点0的状态处理*/
switch (dev->ep0state) {
case EP0_IDLE:
s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); //在这个函数中会调用上层提供的gadget_driver中实现的setup函数来处理控制数据包
break;
case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ //向主机发送数据
dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
s3c2410_udc_write_fifo(ep, req); //写UDC FIFO
}
break;
case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ //从主机接收数据
dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {
s3c2410_udc_read_fifo(ep,req);
}
break;
case EP0_END_XFER:
dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n");
dev->ep0state = EP0_IDLE;
break;
case EP0_STALL:
dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n");
dev->ep0state = EP0_IDLE;
break;
}
}
/*
* handle_ep - Manage I/O endpoints
其他端点的处理函数,主要是数据发送和接收
*/
static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
{
struct s3c2410_request *req;
int is_in = ep->bEndpointAddress & USB_DIR_IN;
u32 ep_csr1;
u32 idx;
if (likely (!list_empty(&ep->queue))) //取出申请
req = list_entry(ep->queue.next, struct s3c2410_request, queue);
else
req = NULL;
idx = ep->bEndpointAddress & 0x7F; //端点地址
if (is_in) { //向主机发送数据
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG);
dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", idx, ep_csr1, req ? 1 : 0);
if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) {
dprintk(DEBUG_VERBOSE, "st\n");
udc_write(idx, S3C2410_UDC_INDEX_REG);
udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, S3C2410_UDC_IN_CSR1_REG);
return;
}
if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
s3c2410_udc_write_fifo(ep,req);
}
}
else //从主机接收数据
{
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1);
if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) {
udc_write(idx, S3C2410_UDC_INDEX_REG);
udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, S3C2410_UDC_OUT_CSR1_REG);
return;
}
if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
s3c2410_udc_read_fifo(ep,req);
}
}
}
端点操作函数集:端点的基本操作函数
static const struct usb_ep_ops s3c2410_ep_ops = {
.enable = s3c2410_udc_ep_enable, //端点使能
.disable = s3c2410_udc_ep_disable, //关闭端点
.alloc_request = s3c2410_udc_alloc_request, //分配一个请求
.free_request = s3c2410_udc_free_request, //释放请求
.queue = s3c2410_udc_queue, //向端点提交一个请求
.dequeue = s3c2410_udc_dequeue, //从端点请求队列中删除一个请求
.set_halt = s3c2410_udc_set_halt,
};
主要分析queue这个函数,因为上层主要和这个函数打交道,接收或发送数据都需要对应的端点队列提交请求
static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
{
struct s3c2410_request *req = to_s3c2410_req(_req);
struct s3c2410_ep *ep = to_s3c2410_ep(_ep);
struct s3c2410_udc *dev;
u32 ep_csr = 0;
int fifo_count = 0;
unsigned long flags;
if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__);
return -EINVAL;
}
dev = ep->dev;
if (unlikely (!dev->driver|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
return -ESHUTDOWN;
}
local_irq_save (flags);
if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) {
if (!_req)
dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
else {
dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", __func__, !_req->complete,!_req->buf, !list_empty(&req->queue));
}
local_irq_restore(flags);
return -EINVAL;
}
_req->status = -EINPROGRESS;
_req->actual = 0;
dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", __func__, ep->bEndpointAddress, _req->length);
if (ep->bEndpointAddress) { //设置端点号
udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ? S3C2410_UDC_IN_CSR1_REG: S3C2410_UDC_OUT_CSR1_REG);
fifo_count = s3c2410_udc_fifo_count_out();
}
else
{
udc_write(0, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
fifo_count = s3c2410_udc_fifo_count_out();
}
/* kickstart this i/o queue? */
if (list_empty(&ep->queue) && !ep->halted) { //该端点队列为空,且没有关闭,则直接完成申请
if (ep->bEndpointAddress == 0 /* ep0 */) { //端点0
switch (dev->ep0state) {
case EP0_IN_DATA_PHASE:
if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) &&s3c2410_udc_write_fifo(ep,req)) {
dev->ep0state = EP0_IDLE;
req = NULL;
}
break;
case EP0_OUT_DATA_PHASE:
if ((!_req->length)|| ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& s3c2410_udc_read_fifo(ep,req))) {
dev->ep0state = EP0_IDLE;
req = NULL;
}
break;
default:
local_irq_restore(flags);
return -EL2HLT;
}
}
else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) &&s3c2410_udc_write_fifo(ep, req)) {
req = NULL;
}
else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY)&& fifo_count&& s3c2410_udc_read_fifo(ep, req)) {
req = NULL;
}
}
/* pio or dma irq handler advances the queue. */
if (likely (req != 0))
list_add_tail(&req->queue, &ep->queue); //加入队列,等待中断处理
local_irq_restore(flags);
dprintk(DEBUG_VERBOSE, "%s ok\n", __func__);
return 0;
}
UDC驱动中还有一个重要函数,在数据传输或接收完成,即一个请求完成之后,会调用这个请求的完成函数来通知上层驱动。
static void s3c2410_udc_done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status)
{
unsigned halted = ep->halted;
list_del_init(&req->queue); //将这个请求从端点请求队列中删除
if (likely (req->req.status == -EINPROGRESS))
req->req.status = status; //返回完成状态
else
status = req->req.status;
ep->halted = 1;
req->req.complete(&ep->ep, &req->req);
ep->halted = halted;
}
总结:
UDC设备驱动层的源码就分析得差不多了,其他很多函数都是操作寄存器,与UDC设备密切相关,但总的来说完成的功能都是一致的。可以发现,在UDC设备驱动层主要需要做以下几个工作:
1. 对usb_gadget、usb_ep、usb_request三个标准数据结构进行封装,根据自己UDC的一些设备特性,设计对应的自己的数据结构;
2. 实现platform_driver数据结构中的函数,将UDC设备驱动向platform系统进行注册;
3. 实现usb_gadget_ops函数集,这些函数主要是操作UDC设备的一些特性(针对设备);
4. 实现usb_ep_ops函数集,这些函数主要是操作端点的功能,如请求分配和提交等(针对端点);
5. 实现UDC设备的中断处理函数,这个函数基本上就是UDC设备驱动的核心;
6.实现上层功能驱动注册接口函数:
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
USB设备驱动程序-USB Gadget Driver(三)
Gadget设备层
这一层是可选的,介于UDC驱动层和Gadget功能层之间。主要源码在composite.c和composite.h文件中,设备层其实和硬件无关,主要实现一些通用性的代码,减少gadget功能层的代码重复工作。Gadget设备层其中承上启下的作用,联系Gadget功能层和UDC驱动层。
将composite源码独立出来,还为复合设备的实现提供了一个通用性的框架。复合设备是指在一个配置描述符中支持多个功能,或者支持多个配置的设备中,每个配置都有一个不同的功能。如一个设备同时支持网络和存储,一个设备同时支持键盘和鼠标功能等。
Gadget设备层的主要数据结构:
1. Function
struct usb_function { //描述一个配置的一个功能
const char *name; //功能名称
struct usb_gadget_strings **strings; //string数组,通过bind中分配的id访问
struct usb_descriptor_header **descriptors; //全速和低速的描述符表,用于bind中分配的接口描述符和string描述符
struct usb_descriptor_header **hs_descriptors; //高速描述符表
struct usb_configuration *config;//调用usb_add_function()函数赋值,是这个功能关联的配置描述符
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching.
* Related: unbind() may kfree() but bind() won't...
*/
/* configuration management: bind/unbind */
/*在Gadget注册时,分配资源*/
int (*bind)(struct usb_configuration *, struct usb_function *);
/*unbind的逆操作*/
void (*unbind)(struct usb_configuration *, struct usb_function *);
/* runtime state management */
/*重新配置altsetting,*/
int (*set_alt)(struct usb_function *,unsigned interface, unsigned alt);
/*获取当前altsetting*/
int (*get_alt)(struct usb_function *, unsigned interface);
/*关闭功能*/
void (*disable)(struct usb_function *);
/*接口相关的控制处理*/
int (*setup)(struct usb_function *, const struct usb_ctrlrequest *);
/*电源管理相关的挂起和恢复功能*/
void (*suspend)(struct usb_function *);
void (*resume)(struct usb_function *);
/* private: */
/* internals */
struct list_head list;
DECLARE_BITMAP(endpoints, 32);
};
2. Config
struct usb_configuration { //表示一个Gadget配置
const char *label; //配置名称
struct usb_gadget_strings **strings; //字符串表
const struct usb_descriptor_header **descriptors; //功能描述符表
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching...
*/
/* configuration management: bind/unbind */
/*在usb_add_config函数中调用,分配资源等*/
int (*bind)(struct usb_configuration *);
void (*unbind)(struct usb_configuration *);
/*处理驱动框架不能处理的配置控制请求*/
int (*setup)(struct usb_configuration *, const struct usb_ctrlrequest *);
/* fields in the config descriptor */
/*用来赋值配置描述符*/
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
/*和composite设备关联,在usb_add_config函数中设置*/
struct usb_composite_dev *cdev;
/* private: */
/* internals */
struct list_head list;
struct list_head functions; //功能链表
u8 next_interface_id;
unsigned highspeed:1;
unsigned fullspeed:1;
struct usb_function *interface[MAX_CONFIG_INTERFACES];
};
3. Driver
struct usb_composite_driver {
const char *name; //驱动名称
const struct usb_device_descriptor *dev; //设备描述符
struct usb_gadget_strings **strings; //字符串表
/* REVISIT: bind() functions can be marked __init, which
* makes trouble for section mismatch analysis. See if
* we can't restructure things to avoid mismatching...
*/
int (*bind)(struct usb_composite_dev *);
int (*unbind)(struct usb_composite_dev *);
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
};
4. Dev
struct usb_composite_dev { //表示一个composite设备
struct usb_gadget *gadget; //关联的gadget
struct usb_request *req; //用于控制响应,提前分配的
unsigned bufsiz; //req中提前分配的buffer长度
struct usb_configuration *config; //当前配置
/* private: */
/* internals */
struct usb_device_descriptor desc;
struct list_head configs; //配置链表
struct usb_composite_driver *driver;
u8 next_string_id;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
*/
unsigned deactivations;
/* protects at least deactivation count */
spinlock_t lock;
};
其中重要的函数:
1.
static int __init composite_bind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev;
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL); //分配一个composite设备对象
if (!cdev)
return status;
spin_lock_init(&cdev->lock);
/*将udc gadget和composite设备建立关系*/
cdev->gadget = gadget;
set_gadget_data(gadget, cdev);
INIT_LIST_HEAD(&cdev->configs); //初始化配置链表
/* preallocate control response and buffer */
/*分配控制端点的请求数据结构和缓存区*/
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!cdev->req)
goto fail;
cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL); //分配控制请求的缓冲区
if (!cdev->req->buf)
goto fail;
cdev->req->complete = composite_setup_complete; //端的0的完成函数
gadget->ep0->driver_data = cdev;
cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
/*设置gadget的供电模式*/
usb_gadget_set_selfpowered(gadget);
/* interface and string IDs start at zero via kzalloc.
* we force endpoints to start unassigned; few controller
* drivers will zero ep->driver_data.
*/
/*将所有端点的driver_data成员清零*/
usb_ep_autoconfig_reset(cdev->gadget);
/* composite gadget needs to assign strings for whole device (like
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
/*调用功能层提供的bind函数,分配资源*/
status = composite->bind(cdev);
if (status < 0)
goto fail;
/*设置composite设备的设备描述符*/
cdev->desc = *composite->dev;
cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
/* standardized runtime overrides for device ID data */
/*设置composite设备的设备描述符信息*/
if (idVendor)
cdev->desc.idVendor = cpu_to_le16(idVendor);
if (idProduct)
cdev->desc.idProduct = cpu_to_le16(idProduct);
if (bcdDevice)
cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
/* strings can't be assigned before bind() allocates the
* releavnt identifiers
*/
/*设置字符串描述符*/
if (cdev->desc.iManufacturer && iManufacturer)
string_override(composite->strings, cdev->desc.iManufacturer, iManufacturer);
if (cdev->desc.iProduct && iProduct)
string_override(composite->strings, cdev->desc.iProduct, iProduct);
if (cdev->desc.iSerialNumber && iSerialNumber)
string_override(composite->strings, cdev->desc.iSerialNumber, iSerialNumber);
INFO(cdev, "%s ready\n", composite->name);
return 0;
fail:
composite_unbind(gadget);
return status;
}
Composite_bind函数主要完成UDC驱动层gadget设备和设备层composite设备关系的建立,分配控制端点0的请求数据结构,设置设备描述符,并调用功能层的bind函数分配功能层所需要的资源等工作。
2.
static void /* __init_or_exit */ composite_unbind(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
/* composite_disconnect() must already have been called
* by the underlying peripheral controller driver!
* so there's no i/o concurrency that could affect the
* state protected by cdev->lock.
*/
WARN_ON(cdev->config);
while ( !list_empty(&cdev->configs)) { //遍历设备的配置链表
struct usb_configuration *c;
c = list_first_entry(&cdev->configs, struct usb_configuration, list);
while (!list_empty(&c->functions)) { //遍历这个配置下的所有功能
struct usb_function *f;
f = list_first_entry(&c->functions, struct usb_function, list);
list_del(&f->list);
if (f->unbind) { //调用功能的unbind函数
DBG(cdev, "unbind function '%s'/%p\n", f->name, f);
f->unbind(c, f);
/* may free memory for "f" */
}
}
list_del(&c->list);
if (c->unbind) { //调用配置的unbind函数
DBG(cdev, "unbind config '%s'/%p\n", c->label, c);
c->unbind(c);
/* may free memory for "c" */
}
}
if (composite->unbind) //调用功能驱动的unbind函数
composite->unbind(cdev);
if (cdev->req) { //释放分配的控制请求的缓存区
kfree(cdev->req->buf);
usb_ep_free_request(gadget->ep0, cdev->req);
}
kfree(cdev); //释放composite设备
set_gadget_data(gadget, NULL);
composite = NULL;
}
Unbind函数完成bind函数中分配的资源,并遍历所有配置和各个配置下的功能,调用其对应得unbind函数来释放资源。
其他重要函数:
在配置中增加一个功能,每个配置初始化后都必须至少有一个功能。
int __init usb_add_function(struct usb_configuration *config, struct usb_function *function)
{
int value = -EINVAL;
DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",function->name, function,config->label, config);
if (!function->set_alt || !function->disable)
goto done;
/*设置功能所属配置,并将功能加入配置的链表中*/
function->config = config;
list_add_tail(&function->list, &config->functions);
/* REVISIT *require* function->bind? */
if (function->bind) { //调用功能的bind函数
value = function->bind(config, function); //分配这个功能所需要的资源
if (value < 0) {
list_del(&function->list);
function->config = NULL;
}
}
else
value = 0;
/* We allow configurations that don't work at both speeds.
* If we run into a lowspeed Linux system, treat it the same
* as full speed ... it's the function drivers that will need
* to avoid bulk and ISO transfers.
*/
if (!config->fullspeed && function->descriptors)
config->fullspeed = true;
if (!config->highspeed && function->hs_descriptors)
config->highspeed = true;
done:
if (value)
DBG(config->cdev, "adding '%s'/%p --> %d\n", function->name, function, value);
return value;
}
/*为设备增加一个配置,这个实现原理和在配置下增加一个功能类似*/
int __init usb_add_config(struct usb_composite_dev *cdev, struct usb_configuration *config)
{
int status = -EINVAL;
struct usb_configuration *c;
DBG(cdev, "adding config #%u '%s'/%p\n", config->bConfigurationValue, config->label, config);
if (!config->bConfigurationValue || !config->bind)
goto done;
/* Prevent duplicate configuration identifiers */
list_for_each_entry(c, &cdev->configs, list) { //若配置已经加入了,则不需要再加
if (c->bConfigurationValue == config->bConfigurationValue) {
status = -EBUSY;
goto done;
}
}
/*将这个配置加入composite设备*/
config->cdev = cdev;
list_add_tail(&config->list, &cdev->configs);
/*初始化这个配置的功能链表头*/
INIT_LIST_HEAD(&config->functions);
config->next_interface_id = 0;
status = config->bind(config); //完成这个配置的资源分配
if (status < 0) {
list_del(&config->list);
config->cdev = NULL;
}
else
{
unsigned i;
DBG(cdev, "cfg %d/%p speeds:%s%s\n", config->bConfigurationValue, config, config->highspeed ? " high" : "",
config->fullspeed? (gadget_is_dualspeed(cdev->gadget)? " full": " full/low"): "");
for (i = 0; i < MAX_CONFIG_INTERFACES; i++) { //查看这个配置下有哪有功能
struct usb_function *f = config->interface[i];
if (!f)
continue;
DBG(cdev, " interface %d = %s/%p\n",i, f->name, f);
}
}
/* set_alt(), or next config->bind(), sets up
* ep->driver_data as needed.
*/
//将gadget设备的所有端点的driver_data清零
usb_ep_autoconfig_reset(cdev->gadget);
done:
if (status)
DBG(cdev, "added config '%s'/%u --> %d\n", config->label, config->bConfigurationValue, status);
return status;
}
/*设置一个配置*/
static int set_config(struct usb_composite_dev *cdev, const struct usb_ctrlrequest *ctrl, unsigned number)
{
struct usb_gadget *gadget = cdev->gadget;
struct usb_configuration *c = NULL;
int result = -EINVAL;
unsigned power = gadget_is_otg(gadget) ? 8 : 100;
int tmp;
if (cdev->config)
reset_config(cdev); //清除设备目前使用的配置
if (number) { //根据配置值找到对应的配置
list_for_each_entry(c, &cdev->configs, list) {
if (c->bConfigurationValue == number) {
result = 0;
break;
}
}
if (result < 0) //没有找到这个配置
goto done;
}
else
result = 0;
INFO(cdev, "%s speed config #%d: %s\n", ({ 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;
} ; speed; }), number, c ? c->label : "unconfigured");
if (!c)
goto done;
cdev->config = c; //设置为设备使用的配置
/* Initialize all interfaces by setting them to altsetting zero. */
for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
struct usb_function *f = c->interface[tmp];
struct usb_descriptor_header **descriptors;
if (!f)
break;
/*
* Record which endpoints are used by the function. This is used
* to dispatch control requests targeted at that endpoint to the
* function's setup callback instead of the current
* configuration's setup callback.
*/
if (gadget->speed == USB_SPEED_HIGH)
descriptors = f->hs_descriptors;
else
descriptors = f->descriptors;
for (; *descriptors; ++descriptors) { //遍历功能的所有端点描述符
struct usb_endpoint_descriptor *ep;
int addr;
if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
continue;
ep = (struct usb_endpoint_descriptor *)*descriptors;
addr = ((ep->bEndpointAddress & 0x80) >> 3) | (ep->bEndpointAddress & 0x0f);
set_bit(addr, f->endpoints); //这个功能采用的端点号
}
result = f->set_alt(f, tmp, 0); //设置功能的默认值
if (result < 0) {
DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n", tmp, f->name, f, result);
reset_config(cdev);
goto done;
}
}
/* when we return, be sure our power usage is valid */
power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
done:
usb_gadget_vbus_draw(gadget, power);
return result;
}
/*这个setup函数完成了ep0所需要处理的而底层不能处理的功能,由于这一个层实现了这个函数,所以功能层就不需要关注于USB协议的这些事务性处理,而重点放在需要实现的功能上*/
static int composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget); //由gadget获取composite设备
struct usb_request *req = cdev->req; //控制请求
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u8 intf = w_index & 0xFF;
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
struct usb_function *f = NULL;
u8 endp;
/* partial re-init of the response message; the function or the
* gadget might need to intercept e.g. a control-OUT completion
* when we delegate to it.
*/
//初始化响应请求信息
req->zero = 0;
req->complete = composite_setup_complete;
req->length = USB_BUFSIZ;
gadget->ep0->driver_data = cdev;
switch (ctrl->bRequest) { //根据请求类型处理
/* we handle all standard USB descriptors */
case USB_REQ_GET_DESCRIPTOR: //返回描述符
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
switch (w_value >> 8) {
case USB_DT_DEVICE: //返回设备描述符
cdev->desc.bNumConfigurations = count_configs(cdev, USB_DT_DEVICE);
value = min(w_length, (u16) sizeof cdev->desc);
memcpy(req->buf, &cdev->desc, value);
break;
case USB_DT_DEVICE_QUALIFIER: //返回qualifier描述符
if (!gadget_is_dualspeed(gadget))
break;
device_qual(cdev);
value = min_t(int, w_length, sizeof(struct usb_qualifier_descriptor));
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (!gadget_is_dualspeed(gadget))
break;
/* FALLTHROUGH */
case USB_DT_CONFIG: //返回配置描述符
value = config_desc(cdev, w_value);
if (value >= 0)
value = min(w_length, (u16) value);
break;
case USB_DT_STRING: //返回字符串描述符
value = get_string(cdev, req->buf, w_index, w_value & 0xff);
if (value >= 0)
value = min(w_length, (u16) value);
break;
}
break;
/* any number of configs can work */
case USB_REQ_SET_CONFIGURATION: //设置配置
if (ctrl->bRequestType != 0)
goto unknown;
if (gadget_is_otg(gadget)) {
if (gadget->a_hnp_support)
DBG(cdev, "HNP available\n");
else if (gadget->a_alt_hnp_support)
DBG(cdev, "HNP on another port\n");
else
VDBG(cdev, "HNP inactive\n");
}
spin_lock(&cdev->lock);
value = set_config(cdev, ctrl, w_value);
spin_unlock(&cdev->lock);
break;
case USB_REQ_GET_CONFIGURATION: //返回当前配置
if (ctrl->bRequestType != USB_DIR_IN)
goto unknown;
if (cdev->config)
*(u8 *)req->buf = cdev->config->bConfigurationValue;
else
*(u8 *)req->buf = 0;
value = min(w_length, (u16) 1);
break;
/* function drivers must handle get/set altsetting; if there's
* no get() method, we know only altsetting zero works.
*/
case USB_REQ_SET_INTERFACE: //设置接口设置
if (ctrl->bRequestType != USB_RECIP_INTERFACE)
goto unknown;
if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
if (w_value && !f->set_alt)
break;
value = f->set_alt(f, w_index, w_value);
break;
case USB_REQ_GET_INTERFACE: //返回接口设置
if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
goto unknown;
if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
break;
f = cdev->config->interface[intf];
if (!f)
break;
/* lots of interfaces only need altsetting zero... */
value = f->get_alt ? f->get_alt(f, w_index) : 0;
if (value < 0)
break;
*((u8 *)req->buf) = value;
value = min(w_length, (u16) 1);
break;
default:
unknown:
VDBG(cdev, "non-core control req%02x.%02x v%04x i%04x l%d\n",ctrl->bRequestType, ctrl->bRequest,w_value, w_index, w_length);
/* functions always handle their interfaces and endpoints...
* punt other recipients (other, WUSB, ...) to the current
* configuration code.
*
* REVISIT it could make sense to let the composite device
* take such requests too, if that's ever needed: to work
* in config 0, etc.
*/
switch (ctrl->bRequestType & USB_RECIP_MASK) {
case USB_RECIP_INTERFACE:
f = cdev->config->interface[intf];
break;
case USB_RECIP_ENDPOINT:
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
list_for_each_entry(f, &cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
break;
}
if (&f->list == &cdev->config->functions)
f = NULL;
break;
}
if (f && f->setup)
value = f->setup(f, ctrl);
else {
struct usb_configuration *c;
c = cdev->config;
if (c && c->setup)
value = c->setup(c, ctrl);
}
goto done;
}
/* 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);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
composite_setup_complete(gadget->ep0, req);
}
}
done:
/* device either stalls (value < 0) or reports success */
return value;
}
USB设备驱动程序-USB Gadget Driver(四)
Gadget 功能层
Gadget功能层完成USB设备的具体功能,其表现的形式各不相同,如键盘、鼠标、存储和网卡等等。功能层不仅涉及到Gadget驱动相关的内容,还涉及到其功能相关的内核子系统。如存储还涉及到内核存储子系统,网卡还涉及到网络驱动子系统。因此,Gadget功能的代码非常复杂。这里以zero.c为例,这个模块只是简单地将接收的数据回显回去。
一、数据结构
首先需要实现usb_composite_driver函数集:
static struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
.bind = zero_bind,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
};
二、主要函数
这个模块的实现就是这么简单:
static int __init init(void)
{
return usb_composite_register(&zero_driver);
}
module_init(init);
static void __exit cleanup(void)
{
usb_composite_unregister(&zero_driver);
}
Bind函数是功能层需要实现与设备层关联的重要函数:
static int __init zero_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget; //Gadget设备
int id;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
/*分配字符串描述符的id,并赋值给设备描述符中字符串索引*/
id = usb_string_id(cdev);
strings_dev[STRING_MANUFACTURER_IDX].id = id;
device_desc.iManufacturer = id;
id = usb_string_id(cdev); i
strings_dev[STRING_PRODUCT_IDX].id = id;
device_desc.iProduct = id;
id = usb_string_id(cdev);
strings_dev[STRING_SERIAL_IDX].id = id;
device_desc.iSerialNumber = id;
/*设置挂起后,设备自动恢复的定时器*/
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
/*核心代码,实现功能*/
if (loopdefault) {
loopback_add(cdev, autoresume != 0); //数据简单回显功能
if (!gadget_is_sh(gadget))
sourcesink_add(cdev, autoresume != 0);
}
else
{
sourcesink_add(cdev, autoresume != 0);
if (!gadget_is_sh(gadget))
loopback_add(cdev, autoresume != 0);
}
/*初始化设备描述符*/
gcnum = usb_gadget_controller_number(gadget);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
else {
device_desc.bcdDevice = cpu_to_le16(0x9999);
}
return 0;
}
/*增加数据简单回显功能*/
int __init loopback_add(struct usb_composite_dev *cdev, bool autoresume)
{
int id;
/*获取字符串描述符id索引*/
id = usb_string_id(cdev);
strings_loopback[0].id = id;
loopback_intf.iInterface = id;
loopback_driver.iConfiguration = id;
/* support autoresume for remote wakeup testing */
if (autoresume)
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
/* support OTG systems */
if (gadget_is_otg(cdev->gadget)) {
loopback_driver.descriptors = otg_desc;
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
return usb_add_config(cdev, &loopback_driver); //增加一个配置
}
/*loopback配置*/
static struct usb_configuration loopback_driver = {
.label = "loopback",
.strings = loopback_strings,
.bind = loopback_bind_config,
.bConfigurationValue = 2,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};
将增加配置的usb_add_config函数中会调用其bind函数,即loopback_bind_config函数,来分配这个配置所需要的资源。
struct f_loopback {
struct usb_function function;
struct usb_ep *in_ep;
struct usb_ep *out_ep;
};
static int __init loopback_bind_config(struct usb_configuration *c)
{
struct f_loopback *loop;
int status;
loop = kzalloc(sizeof *loop, GFP_KERNEL); //分配一个loop结构
if (!loop)
return -ENOMEM;
/*初始化一个功能*/
loop->function.name = "loopback";
loop->function.descriptors = fs_loopback_descs;
loop->function.bind = loopback_bind;
loop->function.unbind = loopback_unbind;
loop->function.set_alt = loopback_set_alt;
loop->function.disable = loopback_disable;
status = usb_add_function(c, &loop->function); //加入这个功能
if (status)
kfree(loop);
return status;
}
在usb_add_function函数中,又会调用这个功能的bind函数,即loopback_bind函数:
static int __init loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_loopback *loop = func_to_loop(f);
int id;
/* allocate interface ID(s) */
id = usb_interface_id(c, f); //分配一个接口id
if (id < 0)
return id;
loopback_intf.bInterfaceNumber = id;
/* allocate endpoints */
/*返回一个输入端点*/
loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_source_desc);
if (!loop->in_ep) {
autoconf_fail:
ERROR(cdev, "%s: can't autoconfigure on %s\n", f->name, cdev->gadget->name);
return -ENODEV;
}
loop->in_ep->driver_data = cdev; /* claim */
/*返回一个输出端点,返回合适的端点*/
loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc);
if (!loop->out_ep)
goto autoconf_fail;
loop->out_ep->driver_data = cdev; /* claim */
/* support high speed hardware */
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_loop_source_desc.bEndpointAddress = fs_loop_source_desc.bEndpointAddress;
hs_loop_sink_desc.bEndpointAddress = fs_loop_sink_desc.bEndpointAddress;
f->hs_descriptors = hs_loopback_descs;
}
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n", gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",f->name, loop->in_ep->name, loop->out_ep->name);
return 0;
}
功能的实现
Loopback_set_alt函数将在设备层的setup函数中被调用,控制通信设置接口。
static int loopback_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_loopback *loop = func_to_loop(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt is zero */
if (loop->in_ep->driver_data)
disable_loopback(loop);
return enable_loopback(cdev, loop); //开启功能
}
static int enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
int result = 0;
const struct usb_endpoint_descriptor *src, *sink;
struct usb_ep *ep;
struct usb_request *req;
unsigned i;
/*选择端点描述符*/
src = ep_choose(cdev->gadget, &hs_loop_source_desc, &fs_loop_source_desc);
sink = ep_choose(cdev->gadget, &hs_loop_sink_desc, &fs_loop_sink_desc);
/* one endpoint writes data back IN to the host */
/*输入输出端点使能*/
ep = loop->in_ep;
result = usb_ep_enable(ep, src);
if (result < 0)
return result;
ep->driver_data = loop;
/* one endpoint just reads OUT packets */
ep = loop->out_ep;
result = usb_ep_enable(ep, sink);
if (result < 0) {
fail0:
ep = loop->in_ep;
usb_ep_disable(ep);
ep->driver_data = NULL;
return result;
}
ep->driver_data = loop;
/* allocate a bunch of read buffers and queue them all at once.
* we buffer at most 'qlen' transfers; fewer if any need more
* than 'buflen' bytes each.
*/
/*qlen=32,分配32个请求,将这个请求放入输出端点队列,等待接收数据*/
for (i = 0; i < qlen && result == 0; i++) {
req = alloc_ep_req(ep);
if (req) {
req->complete = loopback_complete;
result = usb_ep_queue(ep, req, GFP_ATOMIC);
if (result)
ERROR(cdev, "%s queue req --> %d\n", ep->name, result);
}
else {
usb_ep_disable(ep);
ep->driver_data = NULL;
result = -ENOMEM;
goto fail0;
}
}
DBG(cdev, "%s enabled\n", loop->function.name);
return result;
}
/*接收到数据之后,将调用这个完成函数*/
static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_loopback *loop = ep->driver_data;
struct usb_composite_dev *cdev = loop->function.config->cdev;
int status = req->status;
switch (status) {
case 0: /* normal completion? */
if (ep == loop->out_ep) { //将接收到的数据放入输入端点,返回给主机
/* loop this OUT packet back IN to the host */
req->zero = (req->actual < req->length);
req->length = req->actual;
status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
if (status == 0)
return;
/* "should never get here" */
ERROR(cdev, "can't loop %s to %s: %d\n", ep->name, loop->in_ep->name,status);
}
/* queue the buffer for some later OUT packet */
req->length = buflen; //将输入端点完成的申请,重新放入输出队列,等待接收新的数据
status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
if (status == 0)
return;
/* "should never get here" */
/* FALLTHROUGH */
default:
ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length);
/* FALLTHROUGH */
/* NOTE: since this driver doesn't maintain an explicit record
* of requests it submitted (just maintains qlen count), we
* rely on the hardware driver to clean up on disconnect or
* endpoint disable.
*/
case -ECONNABORTED: /* hardware forced ep reset */
case -ECONNRESET: /* request dequeued */
case -ESHUTDOWN: /* disconnect from host */
free_ep_req(ep, req);
return;
}
}
标签:gadget,struct,Gadget,Driver,udc,cdev,usb,ep,USB From: https://www.cnblogs.com/kn-zheng/p/17074897.html