zoukankan      html  css  js  c++  java
  • [置顶] 自娱自乐1之Linux UDC驱动(形式模板)


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

    我不知道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_head		ep_list;	/* of usb_ep */    //其他端点列表
    	enum usb_device_speed		speed;                   
    	unsigned			is_dualspeed:1;
    	unsigned			is_otg:1;
    	unsigned			is_a_peripheral:1;
    	unsigned			b_hnp_enable:1;                    //A-HOST使能了HNP支持
    	unsigned			a_hnp_support:1;                   //A-HOST支持HNP
    	unsigned			a_alt_hnp_support:1;
    	const char			*name;
    	struct device			dev;
    };
    
    ------------------------------------------------------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_speed	speed;
    	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_driver	driver;
    };
    
    -----------------------------------------------------struct usb_request----------------------------------------------------------
    struct usb_request {  //表示一个传输请求的usb_request(与从机端看到的urb相似)
    	void			*buf;
    	unsigned		length;
    	dma_addr_t		dma;
    
    	unsigned		no_interrupt:1;
    	unsigned		zero:1;
    	unsigned		short_not_ok:1;
    
    	void			(*complete)(struct usb_ep *ep,
    					struct usb_request *req);
    	void			*context;
    	struct list_head	list;
    
    	int			status;
    	unsigned		actual;
    };
    
    ----------------------------------------------------------struct usb_ep---------------------------------------------------------
    struct usb_ep {   //描述一个端点
    	void			*driver_data;
    
    	const char		*name;
    	const struct usb_ep_ops	*ops;
    	struct list_head	ep_list;
    	unsigned		maxpacket: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_FS
    static 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
    ", 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硬件不支持,没实现,上层调用会得到-EOPNOTSUPP
    static 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_SETWEDHE
    
    static 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);
    }
    #endif
    
    static 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来控制usb
    static 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_NEWSTYLE
    static 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);
    #endif
    
    static 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实现的就是它
    //下面参考net2272
    static 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实现它,参考goku
    static 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()
    ", __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
    ");
            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
    ");
    #endif //XXXXX_HAVE_CLK
    
        if (strncmp(pdev->name, "xxxxx", 7) == 0) {
            dev_info(dev, "xxxxx: increasing FIFO to %d bytes
    ", 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
    ");
            retval = -ENODEV;
            goto err_clk;
        }
    /*
        pdata = pdev->dev.platform_data;
        if (!pdata) {
            dev_err(&pdev->dev, "driver needs platform data
    ");
            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
    ",
                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
    ");
            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
    ", udc->irq_num, retval);
            retval = -EBUSY;
            goto err_map;
        }
    
        dev_dbg(dev, "got irq %i
    ", 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
    ");
        }
    #endif
    
        dev_dbg(dev, "probe ok
    ");
    
        return 0;
    
    err_int:
    #ifdef XXXXX_USE_IRQ
        free_irq(udc->irq_num, udc);
    #endif
    err_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()
    ", __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
    ", __func__);
    
        return 0;
    }
    
    #ifdef CONFIG_PM
    static 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
    ",
                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驱动。



  • 相关阅读:
    nohup 忽略 nohup.out 输出
    elementui 发送时间格式到 gin 后端问题
    在 Ubuntu 下使用 Puppeteer 时无法加载类库 libX11-xcb.so.1
    获取注解上的注解
    tomcat 远程 debug
    使用pytorch-lightning漂亮地进行深度学习研究(转)
    CTR预估模型发展历程(转)
    pytorch重要函数介绍
    Dataset和Dataloader
    推荐系统中的ID特征为什么有用?
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3258278.html
Copyright © 2011-2022 走看看