自娱自乐1之Linux UDC驱动(形式模板)

来源:互联网 发布:php冒泡排序代码 编辑:程序博客网 时间:2024/06/14 06:30


首先,我不是做驱动的开发人员。所以只能用自娱自乐来表示我的行为。

我不知道udc和gadget驱动是不是冷门的驱动,资料真是不多。我之前买了一本书,上面说到这些,就教你如何调试已写好的驱动。这样也可以写书,太坑了吧!我随便从网上搜搜都能写的比他好。难道现在的育人机构为了钱都变成了坑人机构。

我以前就希望把自己写过的驱动总结成一个模板,让人能直观的看出linux提供的接口要我们做什么甚至怎么做。虽然做这个比较难,但我还是成功的欺骗了自己,可以做到。

这是自娱自乐第一期,可能废话多了一点,请大家原谅。现在说这个模板。这个是一个未做实际应用的模板,只是编译通过,除了没实践,还缺少DMA和USB的请求类型处理样例。后期我会用它做一个驱动,不断的完善。现在这个应该在理论和实践之间的东西。

常用结构体(别人写的,不是linux-3.2.36,不过差不多)

========================================================USB UDC与gadget驱动=========================================================/* *linux内核中usb设备侧驱动程序分为3个层次:UDC驱动、Gadget API和Gadget驱动程序,UDC驱动程序直接访问硬件usb控制器OHCI/EHCI/UHCI,作为usb设备和主机间的底层通信,向上层 *提供与硬件相关操作的回调函数。当前Gadget API是对UDC驱动程序回调函数的简单包装。Gadget驱动程序具体控制系统作为usb设备时的相关功能的实现,使设备表现出“网络连接”、“打印机” *或“USB Mass Storage”等特性。 * *    这里的USB设备控制器(UDC)驱动指作为其他USB主机控制器外设的USB硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将USB设备依附于一个USB主机控制器上:比如当某运行linux的手机作为PC *的U盘时,手机的底层USB控制器行使USB设备控制器的功能,这时候运行在底层的是UDC驱动,而手机成为U盘,在UDC驱动之上仍然需要另一个驱动,对于USB大容量存储器为file storage驱动,这一 *驱动称为gadget驱动(总之是一个运行linux的系统的usb接口作为另一个linux系统的设备)。usb设备驱动调用usb核心提供的API,因此具体驱动与SOC无关。同样,usb gadget驱动调用通用的gadget API *因此具体gadget驱动也变得与SOC无关。 *UDC驱动和gadget驱动都位于内核的drivers/usb/gadget目录下,S3C2410对应的UDC驱动为s3c2410_udc.c。ether.c、f_serial.c、file_storage.c等文件实现了一些gadget驱动 */#include <linux/gadget.h>-----------------------------------------------------------struct usb_gadget------------------------------------------------------struct usb_gadget {   //描述USB设备控制器/* readonly to gadget driver */                  //针对gadget驱动只读const struct usb_gadget_ops*ops;                //访问硬件函数struct usb_ep*ep0;                          //端点0,setup使用struct list_headep_list;/* of usb_ep */    //其他端点列表enum usb_device_speedspeed;                   unsignedis_dualspeed:1;unsignedis_otg:1;unsignedis_a_peripheral:1;unsignedb_hnp_enable:1;                    //A-HOST使能了HNP支持unsigneda_hnp_support:1;                   //A-HOST支持HNPunsigneda_alt_hnp_support:1;const char*name;struct devicedev;};------------------------------------------------------struct usb_gadget_ops-------------------------------------------------------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);};-----------------------------------------------------struct usb_gadget_driver----------------------------------------------------struct usb_gadget_driver {  //描述gadget驱动char*function;                          //描述gadget功能的字符串enum usb_device_speedspeed;int(*bind)(struct usb_gadget *);         //当驱动和gadget绑定时调用void(*unbind)(struct usb_gadget *);int(*setup)(struct usb_gadget *,         //处理硬件驱动未处理的端点0请求const struct usb_ctrlrequest *);void(*disconnect)(struct usb_gadget *);void(*suspend)(struct usb_gadget *);void(*resume)(struct usb_gadget *);/* FIXME support safe rmmod */struct device_driverdriver;};-----------------------------------------------------struct usb_request----------------------------------------------------------struct usb_request {  //表示一个传输请求的usb_request(与从机端看到的urb相似)void*buf;unsignedlength;dma_addr_tdma;unsignedno_interrupt:1;unsignedzero:1;unsignedshort_not_ok:1;void(*complete)(struct usb_ep *ep,struct usb_request *req);void*context;struct list_headlist;intstatus;unsignedactual;};----------------------------------------------------------struct usb_ep---------------------------------------------------------struct usb_ep {   //描述一个端点void*driver_data;const char*name;const struct usb_ep_ops*ops;struct list_headep_list;unsignedmaxpacket:16;};------------------------------------------------------struct usb_ep_ops---------------------------------------------------------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);};--------------------------------------------------------------------------------------------------------------------------------/* *UDC和gadget驱动围绕上述数据结构及其成员函数而展开。  *在具体的UDC驱动中,需要封装usb_gadget和每个端点usb_ep,实现端点usb_ep_ops,完成usb_request。另外usb_gadget_register_driver和usb_gadget_unregister_driver这两个API需要由UDC *驱动提供,gadget驱动会调用它们。 */int usb_gadget_register_driver(struct usb_gadget_driver *driver); //注册,在加载模块中调用,该函数中会调用driver->bind()函数,将usb_gadget_driver与具体的gadget绑定int usb_gadget_unregister_driver(struct usb_gadget_driver *driver); //注销,在卸载模块中调用,告诉UDC驱动不再投入工作,如果UDC正在和USB主机连接,会先调用driver->disconnect()                                                                    //函数,而后会调用unbind()函数//在linux/usb/gadget.h中,封装了一些常用的API:                                                                    int usb_ep_enable(struct usb_ep *ep,const struct usb_endpoint_descriptor *desc);  //使能端点 ,该函数会调用struct usb_ep_ops->enable()int usb_ep_disable(struct usb_ep *ep);  //禁止端点,该函数会调用struct usb_ep_ops->disable()struct usb_request *usb_ep_alloc_request(struct usb_ep *ep,gfp_t gfp_flags);    //分配一个依附于某端点的 usb_request,该函数会调用struct usb_ep_ops->usb_request()void usb_ep_free_request(struct usb_ep *ep,struct usb_request *req);            //释放一个依附于某端点的 usb_request,该函数会调用struct usb_ep_ops->free_request()int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags);//提交usb_request,该函数告诉UDC完成usb_request(读写buffer),当请求被完成后,该请求对应的completion函数会被调用,                                                                              //该函数会调用struct usb_ep_ops->queue(),该函数告诉UDC完成usb_request(读写buffer),当请求完成后,该请求对应的completion函数会被调用int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req);  //取消usb_request,该函数会调用struct usb_ep_ops->dequeue()端点FIFO管理:int usb_ep_fifo_status(struct usb_ep *ep);  //该函数会调用truct usb_ep_ops->fifo_status返回目前FIFO中的字节数void usb_ep_fifo_flush(struct usb_ep *ep);  //该函数会调用truct usb_ep_ops->fifo_flush,以flush(冲洗)掉FIFO中的数据int usb_gadget_frame_number(struct usb_gadget *gadget); //它调用gadget->ops->get_frame(gadget)返回目前的帧号/* *S3C2410的UDC驱动在 /driver/usb/gadget/s3c2410_udc.c *//-----------------------------------------------------------------------------------------------------------------------------/

看请求队列的处理

struct xxxxx_request

{

    structlist_head        queue;       /* ep'srequests */

    structusb_request        req;       //对应主机端看到的urb

};

struct xxxxx_ep

{

    struct usb_ep ep; //描述一个端点

   struct list_head queue;

   …

}

 

    /*device/ep0 records init */

    INIT_LIST_HEAD(&dev->gadget.ep_list);

    INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);

   

/* basic endpoint recordsinit */

    for(i = 0; i < XXXXX_ENDPOINTS; i++) {

        structxxxxx_ep *ep = &dev->ep[i];

 

        if(i != 0)//除了ep0

            list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);

        …

        INIT_LIST_HEAD(&ep->queue);

 }

ep0就用gadget里面的,这个在上层的gadget驱动才能看到。Udc只要这样做就可以了

 

gadget驱动会调用xxxxx_udc_queue把请求插入ep->queue

udc在端点中断时会从对应的queue取出处理,我的模板没体现这点。


下面说udc驱动大概包涵哪些。

1.       struct usb_ep_ops

2.       struct usb_gadget_ops xxxxx_ops

3.       有一个中断来处理对应的事件,请求一般包涵在次。(目前的模板没有体现)。

事实上现在的模板就是告诉你上面两个要实现什么,及怎么实现。代码中有详细解释。我不多说了。

.H

/*********************************** Copyright(C), 2013 LDP FileName:  xxxxx_udc.h Author:    wwxxxxll Date:           Description:   History:        Author       Date            Desc************************************/#ifndef __XXXXX_UDC_H__#define __XXXXX_UDC_H__/*************配置选项**************/#define XXXXX_DEBUG_FS  //使用debugfs//struct usb_ep_ops#define XXXXX_NEWSTYLE  //使用udc_start#define XXXXX_SETWEDHE  //实现set_weght方法#define XXXXX_FIFO_STATUS //支持fifo_status方法#define XXXXX_FIFO_FLUSH //支持fifo_flush方法//struct usb_gadget_ops#define XXXXX_XXXXX_GET_FRAME //支持get_frame#define XXXXX_WAKEUP //支持wakeup功能#define XXXXX_SELFPOWERED //selfpowered支持#define XXXXX_VBUS_SESSION //vbus连接控制支持#define XXXXXX_PULLUP //usb连接控制支持#define XXXXX_HAVE_CLK  //有专用的CLK#ifdef XXXXX_HAVE_CLK#define CLK_DELAY_TIME 10 //ms#endif#define XXXXX_USE_IRQ//端口信息#define XXXXX_ENDPOINTS 2 //端口数#define EP0_FIFO_SIZE 8#define EP1_FIFO_SIZE 64#define EP1_ADDRESS 1#define EP1_ATTR USB_ENDPOINT_XFER_BULK#define XXXXX_EP_FILO_SIZE 128/***********************************//*************寄存器定义************//***********************************/struct xxxxx_ep {    struct usb_ep ep; //描述一个端点    struct list_head queue;    struct xxxxx_udc *dev;    const struct usb_endpoint_descriptor *desc;    unsigned char fifosize;    unsigned char bEndpointAddress;    unsigned char bmAttributes;    u16 fifo_size;    u8 num;    unsigned stopped :1;//维护一个端口停止标志#ifdef XXXXX_SETWEDHE    unsigned wedged :1;#endif};#define to_xxxxx_ep(ep_p) container_of(ep_p, struct xxxxx_ep, ep)struct xxxxx_request {    struct list_head        queue;        /* ep's requests */    struct usb_request        req;        //对应主机端看到的urb};#define to_xxxxx_req(req_p) container_of(req_p, struct xxxxx_request, req)//根据实际要求定义,这个不能当做模板,主要是便于软件管理//一般有下面几个,有的驱动不用这些定义去管理enum ep0state {    EP0_IDLE,    EP0_IN,     EP0_OUT,        EP0_STALL,        };    struct xxxxx_udc {    spinlock_t lock;    void __iomem *virl_addr;    u32 phy_addr;    u32 reg_size;    struct usb_gadget gadget;    struct usb_gadget_driver *driver;    enum ep0state ep0state;    struct xxxxx_ep ep[XXXXX_ENDPOINTS];    struct xxxxx_request fifo_req;#ifdef XXXXX_DEBUG_FS    struct dentry *debug_info;#endif #ifdef XXXXX_HAVE_CLK    struct clk *xxxxx_clk;#endif#ifdef XXXXX_USE_IRQ    unsigned int irq_num;#endif    u16    devstatus;};#define to_xxxxx_udc(gadget_p) container_of(gadget_p, struct xxxxx_udc, gadget)#endif//__XXXXX_UDC_H__

.c

/*********************************** Copyright(C), 2013 LDP FileName:  xxxxx_udc.c Author:    wwxxxxll Date:           Description: linux-3.2-36 History:        Author       Date            Desc************************************/#include <linux/module.h>//MODULE_*#include <linux/init.h>//printk#include <linux/slab.h>//kzalloc() kfree()#include <linux/usb/gadget.h>//struct usb_gadget等#include <linux/clk.h>//struct clk#include <linux/platform_device.h>//platform#include <linux/ioport.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <asm/irq.h>#include <asm/io.h>//ioremap#include "xxxxx_udc.h"#ifdef XXXXX_DEBUG_FS#include <linux/debugfs.h>#include <linux/seq_file.h>//seq_printf seq_read#endif#define DRIVER_DESC    "XXXXX USB Device Controller Gadget"#define DRIVER_VERSION    "2013"#define DRIVER_AUTHOR    "wwxxxxll"static const char        gadget_name[] = "xxxxx_udc";static const char        driver_desc[] = DRIVER_DESC;//根据实际情况修改static const char ep0name[] = "ep0";static const char * const ep_name[] = {    ep0name,    "ep1",};#ifdef XXXXX_DEBUG_FSstatic struct dentry *xxxxx_udc_debugfs_root;static int xxxxx_udc_debugfs_seq_show(struct seq_file *m, void *p){    seq_printf(m, "My name is %s\n", gadget_name);    return 0;}static int xxxxx_udc_debugfs_fops_open(struct inode *inode,                     struct file *file){    return single_open(file, xxxxx_udc_debugfs_seq_show, NULL);}static const struct file_operations xxxxx_udc_debugfs_fops = {    .open        = xxxxx_udc_debugfs_fops_open,    .read        = seq_read,    .llseek        = seq_lseek,    .release    = single_release,    .owner        = THIS_MODULE,};#endif/***********************hardware_handler************************/static void xxxxx_usb_reset(struct xxxxx_udc *dev){    //硬件操作}//udc的这个中断,真是包罗万象,各硬件差别比较大//简单一点说,就是清楚中断标致位,再根据中断标志位对应处理//实际要复杂的多,如果是ep0,还会从fifo中取得usb_ctrlrequest//进行对应的处理,我们在实现具体的实现时再说吧static irqreturn_t xxxxx_udc_irq(int dummy, void *_dev){    return IRQ_HANDLED;}/***************************************************************//***********************queue***********************************///对于usb请求,一般都要维护一个list去管理请求//端点list初始化,存入gadget里static void xxxxx_usb_reinit(struct xxxxx_udc *dev){    u32 i;    /* device/ep0 records init */    INIT_LIST_HEAD (&dev->gadget.ep_list);    dev->gadget.ep0 = &dev->ep[0].ep;//ep0单独存放    dev->gadget.speed = USB_SPEED_UNKNOWN;    dev->ep0state = EP0_IDLE;    INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);    for (i = 0; i < XXXXX_ENDPOINTS; i++) {        struct xxxxx_ep *ep = &dev->ep[i];        if (i != 0)            list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);        ep->dev = dev;        ep->desc = NULL;        ep->stopped = 0;        INIT_LIST_HEAD (&ep->queue);    }}static void xxxxx_udc_done(struct xxxxx_ep *ep, struct xxxxx_request *req, int status){    struct xxxxx_udc *dev;    unsigned stopped = ep->stopped;    list_del_init(&req->queue);    if (likely (req->req.status == -EINPROGRESS))//正在进行中        req->req.status = status;    else        status = req->req.status;    dev = ep->dev;    /* don't modify queue heads during completion callback */    ep->stopped = 1;    //先解锁再加锁,加锁是在dequeue_all调用前做的    spin_unlock(&dev->lock);    req->req.complete(&ep->ep, &req->req);    spin_lock(&dev->lock);    ep->stopped = stopped;}static void xxxxx_dequeue_all(struct xxxxx_ep *ep, int status){    struct xxxxx_request *req;    if (&ep->queue == NULL)        return;    while (!list_empty(&ep->queue)) //list_del_init会删除链表中的元素    {        req = list_entry(ep->queue.next, struct xxxxx_request, queue);        xxxxx_udc_done(ep, req, status);    }}/***************************************************************///may not be the endpoint named "ep0".这是gadget.h的源话/**************************ep_ops*******************************///描述端点操作//当设备配置或接口设置改变时,驱动会enable或disable端口static int xxxxx_udc_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){    struct xxxxx_udc    *dev;    struct xxxxx_ep    *ep;    u32 max;    unsigned long    flags;    ep = to_xxxxx_ep(_ep);    if (!_ep || !desc || ep->desc            || (desc->bDescriptorType != USB_DT_ENDPOINT)            || (_ep->name == ep0name))        return -EINVAL;    dev = ep->dev;    if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)        return -ESHUTDOWN;    max = usb_endpoint_maxp(desc) & 0x1fff;    spin_lock_irqsave(&dev->lock, flags);    _ep->maxpacket = max & 0x7fff;    ep->desc = desc;    ep->stopped = 0;#ifdef XXXXX_SETWEDHE    ep->wedged = 0;#endif    ep->bEndpointAddress = desc->bEndpointAddress;    //寄存器操作    spin_unlock_irqrestore(&dev->lock, flags);    return 0;}static int xxxxx_udc_ep_disable(struct usb_ep *_ep){    struct xxxxx_ep *ep = to_xxxxx_ep(_ep);    unsigned long flags;    if (!_ep || !ep->desc) {        return -EINVAL;    }    local_irq_save(flags);    ep->desc = NULL;    ep->stopped = 1;    //清除请求list和关闭ep    xxxxx_dequeue_all(ep, -ESHUTDOWN);//关机后将无法传输端点    local_irq_restore(flags);    return 0;}//动态分配请求static struct usb_request *xxxxx_udc_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags){    struct xxxxx_request *req;    if (!_ep)        return NULL;    req = kzalloc (sizeof(struct xxxxx_request), gfp_flags);    if (!req)        return NULL;    INIT_LIST_HEAD (&req->queue);    return &req->req;}//释放请求static void xxxxx_udc_free_request(struct usb_ep *_ep, struct usb_request *_req){    //struct xxxxx_ep    *ep = to_xxxxx_ep(_ep);    struct xxxxx_request *req = to_xxxxx_req(_req);    if (!_ep || !_req)        return;    WARN_ON (!list_empty (&req->queue));    kfree(req);}//下面的queue是插入一个请求//dequeue删除一个请求static int xxxxx_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags){    struct xxxxx_udc    *dev;    unsigned long        flags;    struct xxxxx_request    *req = to_xxxxx_req(_req);    struct xxxxx_ep        *ep = to_xxxxx_ep(_ep);    if (unlikely (!_ep || (!ep->desc && ep->num != 0))) //这个逻辑下面会看到很多(_ep为空或[ep->desc为空且不是0端点])    {        return -EINVAL;    }    dev = ep->dev;    if (unlikely (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))     {        return -ESHUTDOWN;    }    if (unlikely(!_req || !_req->complete            || !_req->buf || !list_empty(&req->queue))) //_req或_req->buf为空、complete执行错误、req->queue不为空    {        return -EINVAL;    }    local_irq_save (flags);    //硬件操作    if (likely(req != 0))        list_add_tail(&req->queue, &ep->queue);//请求入list    local_irq_restore(flags);    return 0;}static int xxxxx_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req){    struct xxxxx_ep        *ep = to_xxxxx_ep(_ep);    struct xxxxx_udc    *dev;    int            retval = -EINVAL;    unsigned long        flags;    struct xxxxx_request    *req = NULL;    if (!_ep || !_req)        return retval;    dev = ep->dev;    if (!dev->driver)        return -ESHUTDOWN;    local_irq_save (flags);    list_for_each_entry (req, &ep->queue, queue)     {        if (&req->req == _req)         {            list_del_init (&req->queue);            _req->status = -ECONNRESET;//Connection reset by peer            retval = 0;            break;        }    }    if (retval == 0)     {        xxxxx_udc_done(ep, req, -ECONNRESET);    }    local_irq_restore (flags);    return retval;}#ifdef XXXXX_FIFO_STATUS//fifo状态,返回fifo中的字节数。//在上层的调用usb_ep_fifo_statu()如果不用fifo或不支持这个操作返回错误-EOPNOTSUPP//net2272就有寄存器EP_AVAIL记录fifo中的字节数。//s3c2440硬件不支持,没实现,上层调用会得到-EOPNOTSUPPstatic int xxxxx_udc_fifo_status(struct usb_ep *_ep){    struct xxxxx_ep *ep;    u16 retval = 0;    ep = to_xxxxx_ep(_ep);    if (!_ep || (!ep->desc && ep->num != 0))        return -ENODEV;    if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)        return -ESHUTDOWN;    //retval = 读寄存器    return retval;}#endif#ifdef XXXXX_FIFO_FLUSH//冲掉fifo的不明确数据,这个决不用除非端点不能用于任何协议传输,这是上层调用的事static void xxxxx_udc_fifo_flush(struct usb_ep *_ep){    struct xxxxx_ep *ep;    ep = to_xxxxx_ep(_ep);    if (!_ep || (!ep->desc && ep->num != 0))        return;    if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)        return;    //寄存器操作}#endif/*上层调用usb_ep_set_wedge停止一个端点并忽略CLEAR_FEATURE请求。如果Gadget驱动清除停止状态,它将自动Unwedge端点一般用一个位wedge表示如果没有实现set_wedge方法。就用set_halt(ep, 1);代替我们看个例子(在file_storage.c中)Bulk-only当出现无效的CBW时Bulk-only Spec说我们必须停止IN 端点。还说必须保持这个状态知道下一次的reset,但是没有办法告诉控制器忽略CLEAR_FEATURE请求。所以我们用一个位来记录,搞定!下面是参考net2272的代码,value=1:set_halt= 0:clear_halt*/static int xxxxx_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged){    struct xxxxx_ep *ep;    unsigned long flags;    int ret = 0;    ep = container_of(_ep, struct xxxxx_ep, ep);    if (!_ep || (!ep->desc && ep->num != 0))        return -EINVAL;    if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)        return -ESHUTDOWN;    if (ep->desc /* not ep0 */ && usb_endpoint_xfer_isoc(ep->desc))//判断是不是同步端点,见下面        return -EINVAL;    spin_lock_irqsave(&ep->dev->lock, flags);    if (!list_empty(&ep->queue))        ret = -EAGAIN;#ifdef XXXXX_FIFO_STATUS    else if ((ep->bEndpointAddress & USB_DIR_IN) && value && xxxxx_udc_fifo_status(_ep) != 0)//fifo_status是上面实现的        ret = -EAGAIN;#endif    else {        /* set/clear */        if (value) {            if (ep->num == 0)            {                 ep->dev->ep0state = EP0_STALL;                 ep->stopped = 1;                //net2272的端点0在setup时自动复位,没有什么操作。s3c2440就不是了                //ep->dev->protocol_stall = 1;                //ep0 set_halt            }            else                //epx(x != 0) set_halt            if (wedged)//维护wedged                ep->wedged = 1;        } else {            //ep clear_halt            ep->wedged = 0;        }    }    spin_unlock_irqrestore(&ep->dev->lock, flags);    return ret;}//_ep 不能是同步端点,同步端点不支持错误重发机制。在上面判断static int xxxxx_udc_set_halt(struct usb_ep *_ep, int value){    return xxxxx_set_halt_and_wedge(_ep, value, 0);}#ifdef XXXXX_SETWEDHEstatic int xxxxx_udc_set_wedge(struct usb_ep *_ep){    if (!_ep || _ep->name == ep0name)//一般都是端点0请求复位        return -EINVAL;    return xxxxx_set_halt_and_wedge(_ep, 1, 1);}#endifstatic const struct usb_ep_ops xxxxx_ep_ops = {    .enable        = xxxxx_udc_ep_enable,    .disable    = xxxxx_udc_ep_disable,    .alloc_request    = xxxxx_udc_alloc_request,    .free_request    = xxxxx_udc_free_request,    .queue        = xxxxx_udc_queue,    .dequeue    = xxxxx_udc_dequeue,     .set_halt    = xxxxx_udc_set_halt,#ifdef XXXXX_SETWEDHE    .set_wedge  = xxxxx_udc_set_wedge,#endif#ifdef XXXXX_FIFO_STATUS    .fifo_status = xxxxx_udc_fifo_status,#endif#ifdef XXXXX_FIFO_FLUSH    .fifo_flush = xxxxx_udc_fifo_flush,#endif};/***************************************************************///USB 设备的常用操作包括:设备连接、设备移除、设备配置、地址分配、数据传输、 //设备挂起、设备唤醒等。/**************************usb_gadget_ops***********************///硬件操作函数//获取帧号,当主机发送USB 数据包时,每个帧的开始(SOF)包包含一个帧号。//这个帧号一般自动加载到对应寄存器,此函数主要就是读这些寄存器//如果设备不支持返回负static int xxxxx_udc_get_frame(struct usb_gadget *usb_gdt_p){#ifdef XXXXX_GET_FRAME    struct xxxxx_udc *dev = to_xxxxx_udc(usb_gdt_p);    int retval = 0;    unsigned long flags;    spin_lock_irqsave(&dev->lock, flags);    //retval = 读寄存器    spin_unlock_irqrestore(&dev->lock, flags);    return retval;#else    return -EOPNOTSUPP;#endif}#ifdef XXXXX_WAKEUP//唤醒,举个例子net2272。它的寄存器usbctl0的第五位控制唤醒功能使能//寄存器usbctl1的第三位通过写1去resume,s3c2440在PWR_REG也有类似static int xxxxx_udc_wakeup(struct usb_gadget *usb_gdt_p){    struct xxxxx_udc *dev = to_xxxxx_udc(usb_gdt_p);    unsigned long flags;    spin_lock_irqsave(&dev->lock, flags);    //寄存器操作    spin_unlock_irqrestore(&dev->lock, flags);    return 0;}#endif#ifdef XXXXX_SELFPOWERED//设置自供电标志(selfpowered feature),一般就用一个变量位或一个位记录一下。USB_RECIP_DEVICE时返回状态static int xxxxx_udc_set_selfpowered (struct usb_gadget *usb_gdt_p, int is_selfpowered){    struct xxxxx_udc *dev = to_xxxxx_udc(usb_gdt_p);    if (is_selfpowered)        dev->devstatus |= (1 << USB_DEVICE_SELF_POWERED);    else        dev->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);    return 0;}#endif#ifdef XXXXX_VBUS_SESSION//vbus在硬件上就是usb的电源脚,这个函数就是来控制它。一般通过一个gpio拉高拉底//这个vbus会话,实际的我看了s3c2410和at91的处理,就是让usb的D+线与一个gpio口连接//通过置1置0来控制usbstatic int xxxxx_udc_vbus_session (struct usb_gadget *usb_gdt_p, int is_active){    struct xxxxx_udc *dev = to_xxxxx_udc(usb_gdt_p);    unsigned long flags;    spin_lock_irqsave(&dev->lock, flags);    //寄存器操作    spin_unlock_irqrestore(&dev->lock, flags);    return 0;}#endif#ifdef XXXXX_VBBUS_DRAW//强制vbus电源控制器行为,在SET_CONFIGRATION时,设置vbus的电流量//vbus应该是表示总线电压,在硬件上是一个脚//主要是对usb电流的设置,看一下gta02平台,这个函数会操作pcf50633(一种移动设备的电源管理芯片)static int xxxxx_udc_vbus_draw (struct usb_gadget *usb_gdt_p, unsigned mA){    return 0;}#endif#ifdef XXXXXX_PULLUP//这个和上面的vbus_session区别是//vbus_session是控制vbus的连接//pullup是控制usb模块的连接//在udc-core.c中newstyle的驱动probe函数时才调用它,所以你要实现udc_start和udc_stop,//当然除了注册,也可以通过sysfs调用它。和newstyle无关。//composite.c也有一些调用//这个就是根据is_on来connect或disconnect usb//net2272就是由USBCTL0的第三位控制的,s3c2440还是通过gpio和vbus_session没//区别static int xxxxx_udc_pullup (struct usb_gadget *usb_gdt_p, int is_on){    struct xxxxx_udc *dev = to_xxxxx_udc(usb_gdt_p);    unsigned long flags;    spin_lock_irqsave(&dev->lock, flags);    if (is_on)    {        //enable    }    else    {        //disable    }    spin_unlock_irqrestore(&dev->lock, flags);    return 0;}#endif//不好意思,我看了linux-3.2.36的/gadget的目录没发现有实现这个的硬件static int xxxxx_udc_ioctl(struct usb_gadget *usb_gdt_p, unsigned code, unsigned long param){    return 0;}//这个也没看驱动实现它,从名字就是获取配置参数,就简单看看struct usb_dcd_config_params/*struct usb_dcd_config_params {        __u8  bU1devExitLat;    // U1 Device exit Latency  u1设备等待时间#define USB_DEFAULT_U1_DEV_EXIT_LAT     0x01    // Less then 1 microsec 至少1微秒        __le16 bU2DevExitLat;   // U2 Device exit Latency #define USB_DEFAULT_U2_DEV_EXIT_LAT     0x1F4   // Less then 500 microsec };对应struct usb_ss_cap_descriptor 中的成员每一个I/O请求包延迟时间限制*/static void xxxxx_udc_get_config_params(struct usb_dcd_config_params *usb_dc_cfg_pm){}//在udc-core.c中start和udc_start的解释一样,在bind()之前调用,只要实现一个就行了//我知道start主要有bind回调//udc_start主要是设备执行了non-control请求后,要重新连接,net2272和r8a66597实现的就是它#ifdef XXXXX_NEWSTYLEstatic int xxxxx_udc_start(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver);static int xxxxx_udc_stop(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver);#else//s3c2410 xxxxx 实现它static int xxxxx_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *));static int xxxxx_stop(struct usb_gadget_driver *driver);#endifstatic const struct usb_gadget_ops xxxxx_ops = {    .get_frame        = xxxxx_udc_get_frame,#ifdef XXXXX_WAKEUP    .wakeup            = xxxxx_udc_wakeup,#endif#ifdef XXXXX_SELFPOWERED    .set_selfpowered = xxxxx_udc_set_selfpowered,#endif#ifdef XXXXX_VBUS_SESSION    .vbus_session    = xxxxx_udc_vbus_session,#endif#ifdef XXXXX_VBBUS_DRAW    .vbus_draw        = xxxxx_udc_vbus_draw,#endif#ifdef XXXXXX_PULLUP    .pullup            = xxxxx_udc_pullup,#endif    .ioctl          = xxxxx_udc_ioctl,    .get_config_params = xxxxx_udc_get_config_params,#ifdef XXXXX_NEWSTYLE    .udc_start         = xxxxx_udc_start,    .udc_stop       = xxxxx_udc_stop,#else    .start            = xxxxx_start,    .stop            = xxxxx_stop,#endif};/***************************************************************//***************************************************************/static struct xxxxx_udc udc_info = {    .gadget = {        .ops        = &xxxxx_ops,        .ep0        = &udc_info.ep[0].ep,        .name        = gadget_name,        .dev = {            .init_name    = "gadget",        },/*根据自己的硬件选择unsigned is_dualspeed:1;unsigned is_otg:1;unsigned is_a_peripheral:1;unsigned b_hnp_enable:1; //hnp:主机协商协议 otg特有的unsigned a_hnp_support:1;unsigned a_alt_hnp_support:1;*/    },    /* control endpoint */    .ep[0] = {        .num = 0,        .ep =        {            .name        = "ep0",            .ops        = &xxxxx_ep_ops,            .maxpacket    = EP0_FIFO_SIZE,        },        .dev        = &udc_info,    },    /* first group of endpoints */    .ep[1] = {        .num = 1,        .ep =         {            .name        = "ep1",            .ops        = &xxxxx_ep_ops,            .maxpacket    = EP1_FIFO_SIZE,        },        .dev        = &udc_info,        .fifo_size    = EP1_FIFO_SIZE,        .bEndpointAddress = EP1_ADDRESS,        .bmAttributes    = EP1_ATTR,    },};static void stop_activity(struct xxxxx_udc *dev, struct usb_gadget_driver *driver){    unsigned i;    if (dev->gadget.speed == USB_SPEED_UNKNOWN)        driver = NULL;    /* disconnect gadget driver after quiesceing hw and the driver */    xxxxx_usb_reset(dev);//复位或disable    for (i = 0; i < XXXXX_ENDPOINTS; i++)    {        xxxxx_dequeue_all(&dev->ep[i], -ECONNABORTED);    }#ifndef XXXXX_NEWSTYLE/*if (udc_is_newstyle(udc)) {        udc->driver->disconnect(udc->gadget);        udc->driver->unbind(udc->gadget);        usb_gadget_udc_stop(udc->gadget, udc->driver);        usb_gadget_disconnect(udc->gadget);//对应pull_up} else {        usb_gadget_stop(udc->gadget, udc->driver);//所以非newstyle要disconnect}*/    if (driver)     {        spin_unlock(&dev->lock);        driver->disconnect(&dev->gadget);        spin_lock(&dev->lock);    }#endif    if (dev->driver)    {        xxxxx_usb_reinit(dev);//重初始化    }}#ifdef XXXXX_NEWSTYLE/*udc 的probe函数if (udc_is_newstyle(udc)) {//是否实现udc_start and udc_stop        ret = bind(udc->gadget);        if (ret)                goto err1;        ret = usb_gadget_udc_start(udc->gadget, driver);//已绑定,bind是gadget实现的        if (ret) {                driver->unbind(udc->gadget);                goto err1;        }        usb_gadget_connect(udc->gadget);//上面的pullup} else {        ret = usb_gadget_start(udc->gadget, driver, bind);        if (ret)                goto err1;}*///net2272和r8a66597实现的就是它//下面参考net2272static int xxxxx_udc_start(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver){    struct xxxxx_udc *dev;    if (!driver || !driver->unbind || !driver->setup ||        driver->speed != USB_SPEED_HIGH)        return -EINVAL;    dev = container_of(usb_gdt_p, struct xxxxx_udc, gadget);    /* hook up the driver ... */    driver->driver.bus = NULL;    dev->driver = driver;    dev->gadget.dev.driver = &driver->driver;    //使能udc,硬件操作    return 0;}static int xxxxx_udc_stop(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver){    struct xxxxx_udc *dev;    unsigned long flags;    dev = container_of(usb_gdt_p, struct xxxxx_udc, gadget);    spin_lock_irqsave(&dev->lock, flags);    stop_activity(dev, driver);    spin_unlock_irqrestore(&dev->lock, flags);    dev->gadget.dev.driver = NULL;    dev->driver = NULL;    return 0;}#else//s3c2410 goku实现它,参考gokustatic int xxxxx_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *usb_gdt_p)){    struct xxxxx_udc *dev = &udc_info;    int    retval = 0;    if (!driver            || driver->speed < USB_SPEED_FULL            || !bind            || !driver->disconnect            || !driver->setup)        return -EINVAL;    if (!dev)        return -ENODEV;    if (dev->driver)        return -EBUSY;    /* hook up the driver */    driver->driver.bus = NULL;    dev->driver = driver;    dev->gadget.dev.driver = &driver->driver;    if ((retval = device_add(&dev->gadget.dev)) != 0)     {        goto register_error;    }    retval = bind(&dev->gadget);    if (retval)     {        device_del(&dev->gadget.dev);        goto register_error;    }    //使能udc,硬件操作register_error:    dev->driver = NULL;    dev->gadget.dev.driver = NULL;    return retval;}static int xxxxx_stop(struct usb_gadget_driver *driver){    struct xxxxx_udc *dev = &udc_info;    unsigned long    flags;    if (!dev)        return -ENODEV;    if (!driver || driver != dev->driver || !driver->unbind)        return -EINVAL;    spin_lock_irqsave(&dev->lock, flags);    dev->driver = NULL;    stop_activity(dev, driver);    spin_unlock_irqrestore(&dev->lock, flags);    driver->unbind(&dev->gadget);    dev->gadget.dev.driver = NULL;    dev->driver = NULL;    device_del(&dev->gadget.dev);        return 0;}#endif/***************************************************************/static int xxxxx_udc_probe(struct platform_device *pdev){    struct xxxxx_udc *udc = &udc_info;    struct device *dev = &pdev->dev;    int retval;    struct resource *res;#ifdef XXXXX_USE_IRQ    struct resource *resirq;#endif    resource_size_t res_size;    dev_dbg(dev, "%s()\n", __func__);#ifdef XXXXX_HAVE_CLK    udc->xxxxx_clk = clk_get(NULL, "xxxxx");    if (IS_ERR(udc->xxxxx_clk))     {        dev_err(dev, "failed to get usb bus clock source\n");        return PTR_ERR(udc->xxxxx_clk);    }    clk_enable(udc->xxxxx_clk);#if (CLK_DELAY_TIME != 0)    mdelay(CLK_DELAY_TIME);#endif    dev_dbg(dev, "got and enabled clocks\n");#endif //XXXXX_HAVE_CLK    if (strncmp(pdev->name, "xxxxx", 7) == 0) {        dev_info(dev, "xxxxx: increasing FIFO to %d bytes\n", XXXXX_EP_FILO_SIZE);        udc_info.ep[1].fifo_size = XXXXX_EP_FILO_SIZE;    }    spin_lock_init (&udc->lock);    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);    if (!res)     {        dev_err(&pdev->dev, "can't get device resources\n");        retval = -ENODEV;        goto err_clk;    }/*    pdata = pdev->dev.platform_data;    if (!pdata) {        dev_err(&pdev->dev, "driver needs platform data\n");        return -ENODEV;    }*/    res_size = resource_size(res);    if (!request_mem_region(res->start, res_size, res->name))     {        dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",            res_size, res->start);        retval = -ENOMEM;        goto err_clk;    }    udc->virl_addr = ioremap(res->start, res_size);    if (!udc->virl_addr)     {        retval = -ENOMEM;        goto err_mem;    }    udc->phy_addr = res->start;    udc->reg_size = res_size;    device_initialize(&udc->gadget.dev);    udc->gadget.dev.parent = &pdev->dev;    udc->gadget.dev.dma_mask = pdev->dev.dma_mask;    platform_set_drvdata(pdev, udc);    //少不了硬件初始化    xxxxx_usb_reset(udc);    xxxxx_usb_reinit(udc);#ifdef XXXXX_USE_IRQ    resirq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);    if (!resirq)     {        dev_err(&pdev->dev, "can't get device irq resources\n");        retval = -ENODEV;        goto err_map;    }    udc->irq_num = resirq->start;    /* irq setup after old hardware state is cleaned up */    retval = request_irq(udc->irq_num, xxxxx_udc_irq, 0, gadget_name, udc);    if (retval != 0)     {        dev_err(dev, "cannot get irq %i, err %d\n", udc->irq_num, retval);        retval = -EBUSY;        goto err_map;    }    dev_dbg(dev, "got irq %i\n", udc->irq_num);#endif    retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);    if (retval)        goto err_int;#ifdef XXXXX_DEBUG_FS    if (xxxxx_udc_debugfs_root)     {        udc->debug_info = debugfs_create_file("registers", S_IRUGO, xxxxx_udc_debugfs_root,                udc, &xxxxx_udc_debugfs_fops);        if (!udc->debug_info)            dev_warn(dev, "debugfs file creation failed\n");    }#endif    dev_dbg(dev, "probe ok\n");    return 0;err_int:#ifdef XXXXX_USE_IRQ    free_irq(udc->irq_num, udc);#endiferr_map:    iounmap(udc->virl_addr);err_mem:    release_mem_region(res->start, res_size);err_clk:#ifdef XXXXX_HAVE_CLK    clk_put(udc->xxxxx_clk);    clk_disable(udc->xxxxx_clk);#endif    return retval;}static int xxxxx_udc_remove(struct platform_device *pdev){    struct xxxxx_udc *udc = platform_get_drvdata(pdev);    dev_dbg(&pdev->dev, "%s()\n", __func__);    usb_del_gadget_udc(&udc->gadget);    if (udc->driver)        return -EBUSY;#ifdef XXXXX_DEBUG_FS    debugfs_remove(udc->debug_info);#endif#ifdef XXXXX_USE_IRQ    free_irq(udc->irq_num, udc);#endif    iounmap(udc->virl_addr);    release_mem_region(udc->phy_addr, udc->reg_size);    platform_set_drvdata(pdev, NULL);#ifdef XXXXX_HAVE_CLK    if (!IS_ERR(udc->xxxxx_clk) && udc->xxxxx_clk != NULL) {        clk_disable(udc->xxxxx_clk);        clk_put(udc->xxxxx_clk);        udc->xxxxx_clk = NULL;    }#endif    dev_dbg(&pdev->dev, "%s: remove ok\n", __func__);    return 0;}#ifdef CONFIG_PMstatic int xxxxx_udc_suspend(struct platform_device *pdev, pm_message_t message){    return 0;}static int xxxxx_udc_resume(struct platform_device *pdev){    return 0;}#else#define xxxxx_udc_suspend    NULL#define xxxxx_udc_resume    NULL#endif/***************************************************************/static const struct platform_device_id xxxxx_udc_ids[] = {    { "xxxxx-usbgadget", },    { }};MODULE_DEVICE_TABLE(platform, xxxxx_udc_ids);//有些设备可能用struct pci_driver,我就不考虑这么多了。static struct platform_driver udc_driver_xxxxx = {    .driver        = {        .name    = "xxxxx-usbgadget",        .owner    = THIS_MODULE,    },    .probe        = xxxxx_udc_probe,    .remove        = __exit_p(xxxxx_udc_remove),    .suspend    = xxxxx_udc_suspend,    .resume        = xxxxx_udc_resume,    .id_table    = xxxxx_udc_ids,};static int __init udc_init(void){    int retval;    xxxxx_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);    if (IS_ERR(xxxxx_udc_debugfs_root)) {        printk(KERN_ERR "%s: debugfs dir creation failed %ld\n",            gadget_name, PTR_ERR(xxxxx_udc_debugfs_root));        xxxxx_udc_debugfs_root = NULL;    }    retval = platform_driver_register(&udc_driver_xxxxx);    if (retval)        goto err;    return 0;err:    debugfs_remove(xxxxx_udc_debugfs_root);    return retval;}static void __exit udc_exit(void){    platform_driver_unregister(&udc_driver_xxxxx);    debugfs_remove(xxxxx_udc_debugfs_root);}module_init(udc_init);module_exit(udc_exit);MODULE_AUTHOR(DRIVER_AUTHOR);MODULE_DESCRIPTION(DRIVER_DESC);MODULE_VERSION(DRIVER_VERSION);MODULE_LICENSE("GPL");

下期预告:

基于模板实现一个实际的udc驱动。