-
2021-09-13 21:41:19
Linux-USB驱动笔记(七)--设备控制器UDC驱动
1、前言
在Linux-USB驱动笔记(四)–USB整体框架中有説到设备侧的设备控制器(UDC),下面我们来具体看一下。
2、设备控制器(UDC)
USB设备控制器(UDC)驱动指的是作为其他USB主机控制器的USB硬件设备上的底层控制器驱动。该硬件和驱动负责将一个USB设备依附于一个USB主机控制器上。
UDC驱动位于/drivers/usb/gadget目录下。
2.1、usb_gadget – USB从机设备
struct usb_gadget { struct work_struct work; struct usb_udc *udc; /* readonly to gadget driver */ const struct usb_gadget_ops *ops; struct usb_ep *ep0; struct list_head ep_list; enum usb_device_speed speed; enum usb_device_speed max_speed; enum usb_device_state state; const char *name; struct device dev; unsigned isoch_delay; unsigned out_epnum; unsigned in_epnum; unsigned mA; struct usb_otg_caps *otg_caps; //标志是否支持某些功能 unsigned sg_supported:1; unsigned is_otg:1; unsigned is_a_peripheral:1; unsigned b_hnp_enable:1; unsigned a_hnp_support:1; unsigned a_alt_hnp_support:1; unsigned hnp_polling_support:1; unsigned host_request_flag:1; unsigned quirk_ep_out_aligned_size:1; unsigned quirk_altset_not_supp:1; unsigned quirk_stall_not_supp:1; unsigned quirk_zlp_not_supp:1; unsigned quirk_avoids_skb_reserve:1; unsigned is_selfpowered:1; unsigned deactivated:1; unsigned connected:1; unsigned lpm_capable:1; };
字段 含义 struct work_struct work; sysfs_notify()使用的工作队列 struct usb_udc *udc; usb设备控制器 const struct usb_gadget_ops *ops; UDC操作函数 struct usb_ep *ep0; 端点0,用于驱动设置请求应答 struct list_head ep_list; 其他端口列表 enum usb_device_speed speed; 当前连接的USB主机速率 enum usb_device_speed max_speed; UDC能处理的最大速率 enum usb_device_state state; UDC状态 const char *name; 名称 struct device dev; 设备 unsigned isoch_delay; 设置等待请求 unsigned out_epnum; 最近使用的输出端点号 unsigned in_epnum; 最近使用的输入端点号 unsigned mA; 最近设置的mA值 struct usb_otg_caps *otg_caps; OTG功能 2.2、usb_gadget_ops – UDC操作函数
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); void (*get_config_params)(struct usb_dcd_config_params *); int (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *); int (*udc_stop)(struct usb_gadget *); void (*udc_set_speed)(struct usb_gadget *, enum usb_device_speed); struct usb_ep *(*match_ep)(struct usb_gadget *, struct usb_endpoint_descriptor *, struct usb_ss_ep_comp_descriptor *); };
以面的操作函数不涉及端点操作。
2.3、usb_ep – 端点
struct usb_ep { void *driver_data; const char *name; const struct usb_ep_ops *ops; struct list_head ep_list; struct usb_ep_caps caps; bool claimed; bool enabled; unsigned maxpacket:16; unsigned maxpacket_limit:16; unsigned max_streams:16; unsigned mult:2; unsigned maxburst:5; u8 address; const struct usb_endpoint_descriptor *desc; const struct usb_ss_ep_comp_descriptor *comp_desc; };
重要字段及其含义如下:
字段 含义 const struct usb_ep_ops *ops 端点操作函数集 const struct usb_endpoint_descriptor *desc 端点描述符 2.4、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); void (*dispose) (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); };
3、总结如下
-
1、usb_gadget表示一个从机设备(UDC + 端点),它包含一些端点(包括端点0和其他端点)。所以它有一个端点列表成员ep_list, 初始化过程中会将所有需要的端点放到该链表中。
-
2、 usb_gadget_ops主要用来操作UDC。
-
3、 usb_ep表示一个端点,每个端点都有自己的操作函数usb_ep_ops。
-
4、 usb_ep_ops主要用来操作端点。
更多相关内容 -
-
自娱自乐1之Linux UDC驱动(形式模板)
2013-08-14 11:55:14我不知道udc和gadget驱动是不是冷门的驱动,资料真是不多。我之前买了一本书,上面说到这些,就教你如何调试已写好的驱动。这样也可以写书,太坑了吧!我随便从网上搜搜都能写的比他好。难道现在的育人机构为了钱都...
首先,我不是做驱动的开发人员。所以只能用自娱自乐来表示我的行为。
我不知道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\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硬件不支持,没实现,上层调用会得到-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()\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); #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()\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_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\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驱动。
-
USB总线-Linux内核USB3.0设备控制器之UDC驱动分析(六)
2022-03-13 21:47:13UDC驱动的接口都定义在drivers/usb/gadget/udc/core.c文件中。USB Function驱动通过调用这些接口匹配及访问USB设备控制器,而底层USB控制器驱动要实现这些接口定义的功能。下面分析一下主要的UDC驱动接口调用流程。 ...1.概述
UDC驱动的接口都定义在drivers/usb/gadget/udc/core.c文件中。USB Function驱动通过调用这些接口匹配及访问USB设备控制器,而底层USB控制器驱动要实现这些接口定义的功能。下面分析一下主要的UDC驱动接口调用流程。
2. 接口分析
2.1.驱动绑定
Composite层通过调用UDC core层的
usb_udc_attach_driver
和usb_gadget_probe_driver
接口将Function驱动和UDC驱动绑定。前者通过UDC设备的名称匹配,通常是configfs配置的USB Function驱动使用,后者直接匹配udc_list
链表中的第一个UDC驱动,通常是legacy类型的USB Function驱动使用,如g_audio驱动。使用usb_gadget_unregister_driver
函数解除Function驱动和UDC驱动的绑定。具体的绑定过程,在分析具体的Function驱动时说明。[drivers/usb/gadget/udc/core.c] int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver); int usb_gadget_probe_driver(struct usb_gadget_driver *driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
底层的USB设备控制器驱动(dwc3 gadget)使用
usb_add_gadget_udc
函数将自身加入到Core UDC Framework中,使用usb_del_gadget_udc
从Core UDC Framework删除。加入过程在UDC驱动初始化的时候已经分析过了,这里不再赘述。[include/linux/usb/gadget.h] int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget); void usb_del_gadget_udc(struct usb_gadget *gadget);
2.2.开启和停止UDC
下面两个函数用来开启和停止UDC,Function驱动和UDC驱动绑定的时候通过调用
usb_udc_attach_driver
或usb_gadget_probe_driver
间接调用usb_gadget_udc_start
开启UDC,Function驱动和UDC驱动解除绑定的时候通过调用usb_gadget_unregister_driver
间接调用usb_gadget_udc_stop
停止UDC。[drivers/usb/gadget/udc/core.c] static inline int usb_gadget_udc_start(struct usb_udc *udc); static inline void usb_gadget_udc_stop(struct usb_udc *udc); [include/linux/usb/gadget.h] int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver); int usb_gadget_probe_driver(struct usb_gadget_driver *driver); int usb_gadget_unregister_driver(struct usb_gadget_driver *driver);
2.2.1.开启UDC
USB Function驱动和UDC绑定成功后,通过调用
usb_gadget_udc_start
接口开启USB设备控制器,接收主机发送的请求,其内部调用的dwc3设备控制器驱动的dwc3_gadget_start
函数。主要的工作是设置相关参数并使能USB设备控制器,具体如下:- 注册中断上半部分处理函数dwc3_interrupt和中断下半部分处理函数dwc3_thread_interrupt(运行在线程上下文中).
- 将composite层提供的
usb_gadget_driver
结构体地址保存到dwc3
结构体中。legacy Function驱动使用的usb_gadget_driver
结构体是composite_driver_template
,configfs Function驱动使用的usb_gadget_driver
结构体是configfs_driver_template
。 - 设置控制器速度(默认超高速)、使能接收数据包数量统计、设置端点0能处理的最大包长为512字节(SuperSpeed Default)。
- 使能ep0_out和ep0_in硬件端点,设备枚举阶段需要使用端点0,因此需要先使能。使能端点0的步骤如下:
- 向控制器发送开始新配置命令
DWC3_DEPCMD_DEPSTARTCFG
(初始化的时候需要发送此命令),所有端点设置端点传输资源配置命令DWC3_DEPCMD_SETTRANSFRESOURCE
。向端点发送命令需要通过USB3_DEPnCMD
、USB3_DEPnCMDPAR0
、USB3_DEPnCMDPAR1
、USB3_DEPnCMDPAR2
寄存器,第一个寄存器保存命令,后面三个寄存器保存命令的参数。向端点发送命令都是通过dwc3_send_gadget_ep_cmd
函数完成,后面会经常看到。 - 设置端点0的一些参数。如端点类型(控制端点)、端点的MaxPacketSize(512)、设置USB3.0的Burst size(端点的burst为1)、使能设备端点完成传输事件
DWC3_DEPCFG_XFER_COMPLETE_EN
和传输未准备好事件DWC3_DEPCFG_XFER_NOT_READY_EN
。 - 设置端点编号,直接使用
dep->number
。 - 设置端点对应的FIFO编号为
dep->number >> 1
,控制端点的输入和输出端点其FIFO编号相同,其他输出端点FIFO编号为0。 - 设置端点的bInterval。
- 上述这些参数通过向控制器发送
DWC3_DEPCMD_SETEPCONFIG
命令设置。 - 保存端点描述符、USB3.0伴侣描述符、保存端点类型、设置端点使能标志、向寄存器中写入端点编号以使能端点,非控制端点还要初始化TRB。
- 向控制器发送开始新配置命令
- 开始ep0 out传输,用于响应主机发送的请求。首先准备一个TRB,接着将TRB中的缓冲区地址设置为控制请求DMA缓冲区
ctrl_req_addr
地址,最后向控制器发送开始传输命令DWC3_DEPCMD_STARTTRANSFER
,参数为TRB自身的DMA地址。 - 写DWC3_DEVTEN寄存器,使能设备事件(中断),这样控制器就可响应主机发送的请求了,可以正常被枚举了。
2.2.2.停止UDC
USB Function驱动和UDC解除绑定成功后会调用
usb_gadget_udc_stop
函数停止USB设备控制器。主要的工作是清除USB控制器上的USB请求,然后关闭端点,具体如下:- 将DWC3_DEVTEN寄存器清空,屏蔽所有设备事件(中断)。
- 关闭ep0_out和ep0_in硬件端点。
- 向控制器发送停止传输命令
DWC3_DEPCMD_ENDTRANSFER
。 - 移除端点上的所有USB请求,并调用其回调函数。
- 若端点处于STALL状态,则需要先清除该状态
- 关闭硬件端点
- 清空端点描述符。
- 清空USB3.0端点伴侣描述符。
- 清空端点类型。
- 清空端点标志。
- 向控制器发送停止传输命令
- 注销注册的中断处理程序。
2.2.3.端点0描述符
端点0的端点描述符定义在dwc3的设备控制器驱动中,Function驱动无需定义,具体如下。
[drivers/usb/dwc3/gadget.c] /* 端点0的描述符在dwc3 gadget驱动中定义 */ static struct usb_endpoint_descriptor dwc3_gadget_ep0_desc = { .bLength = USB_DT_ENDPOINT_SIZE, // 端点描述符长度 .bDescriptorType = USB_DT_ENDPOINT, // 描述符类型为端点描述符 .bmAttributes = USB_ENDPOINT_XFER_CONTROL, // 端点0使用控制传输 };
2.2.4.命令和参数
从上面的分析中可以看出,软件通过向USB设备控制器端点
USB3_DEPnCMD
寄存器写入命令来驱动USB端点工作,命令可以携带参数,参数写到USB3_DEPnCMDPAR1
、USB3_DEPnCMDPAR2
、USB3_DEPnCMDPAR3
寄存器。命令的类型由USB3_DEPnCMD
寄存器的bit[0:3]位决定,bit[10]控制命令是否执行,软件设置为1则端点开始执行命令,执行完毕,端点自动清零,软件可以根据此位判断端点是否执行完命令。USB3_DEPnCMD
寄存器的低11bit意义如下图所示。在执行命令之前,需要提前把命令参数写到参数寄存器中,若不需要参数,则写入0即可。下面是参数寄存器
USB3_DEPnCMDPAR0
、USB3_DEPnCMDPAR1
、USB3_DEPnCMDPAR2
的具体意义。- USB3_DEPnCMDPAR0
位域 说明 31:30 配置行为。
0-初始化端点状态,用于第一次配置端点
1-恢复端点状态,用于休眠唤醒后恢复端点状态,需要恢复的状态保存到DEPCMDPAR2寄存器中
2-修改端点状态,用于修改已经配置好的端点配置,例如修改DEPEVTEN使能位、interrupt number、MaxPacketSize。29:26 Reserved 25:22 设置突发传输数值Burst Size,即一个微帧内传输多少包数据,只有USB3.0及以上支持此特性。
0:Burst length = 1
1:Burst length = 2…
15:Burst length = 1621:17 分配给此端点的FIFO编号。对于控制端点,输出端点和输入端点的值应该相等,即ep0_in和ep0_out的FIFO编号相等。对于输出端点此值为0。尽管在DRD模式中TxFIFOs数量超过16个,但在设备模式中必须使用较低的16个。 16:14 Reserved 13:3 输入输出端点支持的最大包长。USB3.0支持的最大包长为1024字节。 2:1 端点类型
2’b00: Control
2’b01: Isochronous
2’b10: Bulk
2’b11: Interrupt0 Reserved - USB3_DEPnCMDPAR1
位域 说明 31 FIFO-based 30 Reserved 29:25 设置USB端点编号。
bit[29:26]: Endpoint number
bit[25]: Endpoint direction,0-OUT,1-IN
物理端点0必须分配给控制器输出端点
物理端点1必须分配给控制器输入端点24 Stream的能力,指出此端点具有流传输功能(MaxStreams != 0) 23:16 设置bInterval的值,端点描述符中设置了该值(写入寄存器的值为真实bInterval-1)。即轮训数据传送端点的时间间隔,对于批量传送和控制传送的端点忽略,对于同步传送的端点,必须为1,对于中断传送的端点,范围为1-255。dwc3控制器支持的有效值范围为0-13 15 如果bulk端点使用了 External Buffer Control (EBC)模式 14 如果bit15设置了该位才能设置,否则不要设置此位。
1:控制器不会更新TRB的HWO位域
0:控制器会更新TRB的HWO位域13:8 使能设备端点的特殊事件DEPEVTEN,写入0关闭。
Bit 13: Stream Event Enable (StreamEvtEn)
Bit 12: Reserved
Bit 11: Reserved
Bit 10: XferNotReady Enable (XferNRdyEn)
Bit 9: XferInProgress Enable (XferInProgEn)
Bit 8: XferComplete Enable (XferCmplEn)7:5 Reserved 4:0 输入和输出端点的中断编号。指示此端点产生的与端点相关的中断的中断/事件缓冲区号。如果没有使能multiple Interrupter配置,则此位域必须设置为0 - USB3_DEPnCMDPAR2
位域 说明 31:0 当USB3_DEPnCMDPAR0寄存器的bit[31:30]为1时,该寄存器中保存的是需要恢复端点状态的值,否则为Reserved 2.3.打开和禁止端点
2.3.1.打开端点
USB Function驱动通过调用
usb_ep_enable
打开除端点0以外的端点。端点0打开逻辑由dwc3驱动自身处理,不需要USB Function驱动直接参与。[include/linux/usb/gadget.h] int usb_ep_enable(struct usb_ep *ep);
usb_ep_enable
函数的工作流程如下图所示,和usb_gadget_udc_start
函数一样,最终通过调用__dwc3_gadget_ep_enable
实现。__dwc3_gadget_ep_enable
函数的执行流程见2.2.1.节,下面主要说不同的地方:- 设置端点参数,见2.2.2.节,对于非端点0,若是bulk传输,还会使能stream事件,并且还会使能
DWC3_DEPCFG_XFER_IN_PROGRESS_EN
事件 - 保存端点数据、设置端点使能标志、使能端点
- 非控制端点需要设置TRB相关内容。首先将TRB ring的索引
trb_dequeue
和trb_enqueue
清零,接着将trb_pool
全部清零,最后设置link TRB,即trb_pool
最后一个TRB中保存第一个TRB的地址,并且设置DWC3_TRBCTL_LINK_TRB
和DWC3_TRB_CTRL_HWO
标志。 - 设置端点使能状态为
true
。
2.3.2.关闭端点
USB Function驱动通过调用
usb_ep_disable
关闭除端点0以外的端点。端点0关闭逻辑由dwc3驱动自身处理,不需要USB Function驱动直接参与。[include/linux/usb/gadget.h] int usb_ep_disable(struct usb_ep *ep);
usb_ep_disable
函数的工作流程如下图所示,和usb_gadget_udc_stop
函数一样,最终通过调用__dwc3_gadget_ep_disable
实现。__dwc3_gadget_ep_disable
函数的执行流程见2.2.1.节,最后设置端点状态关闭。2.4.匹配端点
USB Function驱动可以通过
gadget_find_ep_by_name
和usb_gadget_ep_match_desc
函数来匹配要使用的端点。前者实现较为简单,通过端点的名称进行匹配,使用的不多。后者通过端点描述符和USB3.0端点伴侣描述符进行匹配,使用较多,返回值为1时表示匹配成功,返回值为0表示匹配失败,其匹配的流程如下:- 若
ep->claimed
字段为true说明该端点已经被使用,则直接返回0。 - 检查端点描述符和要匹配的端点输入输出方向是否相同,不相同则返回0。
- 检查描述符的
maxpacket_limit
是否大于真实端点,若大于则返回0。 - 检查各种传输类型的参数是否设置正确。
- 对于控制传输直接返回0,控制传输使用端点0,无法被匹配
- 对于等时传输,若使用全速模式则最大包长为1023字节。
- 对于批量传输,若使用超高速模式,则描述符请求的流数量要大于端点支持的最大流数量。
- 对于中断传输,若使用全速模式则最大包长为64字节,高速和超高速最大包长为1024字节。
- 若上述条件都满足,则匹配成功,返回1。
[include/linux/usb/gadget.h] // name为要匹配端点的名称 struct usb_ep *gadget_find_ep_by_name( struct usb_gadget *g, const char *name); // ep为要匹配端点的数据结构,desc匹配端点的端点描述符 // ep_comp匹配端点的USB3.0端点伴侣描述符 int usb_gadget_ep_match_desc(struct usb_gadget *gadget, struct usb_ep *ep, struct usb_endpoint_descriptor *desc, struct usb_ss_ep_comp_descriptor *ep_comp);
2.5.分配和释放USB请求
USB Function驱动通过调用
usb_ep_alloc_request
和usb_ep_free_request
分配和释放USB I/O请求。[include/linux/usb/gadget.h] struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags); void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req);
2.5.1.分配USB请求
dwc3所有端点都是通过
dwc3_gadget_ep_alloc_request
分配USB请求,实质上分配的是dwc3_request
数据结构,内部包含了通用的USB请求数据结构usb_request
。2.5.2.释放USB请求
dwc3所有端点都是通过
dwc3_gadget_ep_free_request
释放USB请求。2.6.提交和取消USB请求
USB Function驱动通过调用
usb_ep_queue
和usb_ep_dequeue
将USB I/O请求提交到发送队列和从发送队列取消。[include/linux/usb/gadget.h] int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags); int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req);
2.6.1.提交USB请求
向端点0和非端点0提交USB请求,
usb_ep_queue
内部调用的函数不同。端点0调用dwc3_gadget_ep0_queue
函数,非端点0调用dwc3_gadget_ep_queue
函数。2.6.1.1.向端点0提交USB请求
如下图所示,向端点0提交USB请求时,会调用dwc3 gadget驱动的
dwc3_gadget_ep0_queue
函数,其主要的执行流程为:- 端点0共享一个TRB,若
pending_list
链表不为空,说明还有未完成的request,则直接退出。端点0的request传输完成后,在其回调函数中会将request从pending_list
移除。 - 设置request的
actual
、status
字段,保存端点0的编号,将request放入pending_list
链表。 - 调用
usb_gadget_map_request
映射request的缓冲区。 - 准备一个TRB,将其和request绑定,固定使用
dwc->ep0_trb[0]
。初始化的时候端点0分配了2个TRB。 - 调用
dwc3_ep0_start_trans
开始传输request,该函数前面已经介绍过了。
2.6.1.2.向非端点0提交USB请求
usb_ep_queue
向非端点0提交USB请求的过程如下图所示,最终通过__dwc3_gadget_ep_queue
函数提交。下面分析一下向非端点0提交USB请求的主要工作内容:- 设置USB请求的
actual
、status
、direction
、epnum
和length
等字段,对于输出端点,length
必须是MaxPacketSize的整数倍。 - 对USB请求的缓冲区进行流式DMA映射。USB请求缓冲区是Function驱动使用
kmalloc
等函数分配,DMA不能直接使用,需要进行DMA映射,具体的映射过程在2.7小结介绍。 - 将提交的USB请求先放到
pending_list
链表。 - 调用
__dwc3_gadget_kick_transfer
函数驱动USB设备控制器发送USB请求。这里有几个特殊情况,图里面没有画出,下面简要说明一下。- bulk和control传输,则直接调用
__dwc3_gadget_kick_transfer
发送USB请求。 - int和isoc传输,需要处理XferNotReady、XferInProgress、Stream Capable Bulk Endpoints等情况。
- bulk和control传输,则直接调用
- 调用
dwc3_prepare_trbs
函数遍历pending_list
将TRB和request绑定,执行完后pending_list
链表上的request都会被放到started_list
链表上。该函数会根据DMA缓冲区的使用形式做不同的处理,若DMA支持Scatter-gather,则调用dwc3_prepare_one_trb_sg
函数,否则调用dwc3_prepare_one_trb_linear
函数,最终都是通过dwc3_prepare_one_trb
将TRB和request绑定。 - 从
started_list
链表中获取一个USB请求。若端点空闲,将其绑定的TRB DMA地址设置到param0和param1寄存器中,命令设置为DWC3_DEPCMD_STARTTRANSFER
,开始传输。若端点忙碌,则将命令设置为DWC3_DEPCMD_UPDATETRANSFER
,只更新传输,则不发送本次的USB请求。端点是否忙碌通过DWC3_EP_BUSY
标志判断。 - 若端点空闲,则获取端点资源索引,用于辨别是那一次传输。
上面说过了,TRB和request最终通过
dwc3_prepare_one_trb
实现,该函数的执行过程如下图所示,主要的工作内容为:- 从
trb_pool
获取一个空闲的TRB,trb_pool
的索引为dep->trb_enqueu
。 - 设置request状态为开始传输,将request放到
started_list
链表上。 - request保存TRB的虚拟地址、保存TRB的DMA地址、保存绑定TRB的索引、
dep->trb_enqueu
增大。 - TRB保存request中缓冲区的长度、保存request中缓冲区的DMA地址。
- 设置TRB的控制位。
2.6.2.取消USB请求
端点0和非端点0取消USB请求的过程是一样的,最终都是通过调用dwc3 gadget驱动的
dwc3_gadget_ep_dequeue
实现,其主要的工作内容如下:- 遍历
pending_list
链表,查找要取消的request是否在该链表中,若在,则调用dwc3_gadget_giveback
函数回调request的回调函数,将其从pending_list
链表中删除。 - 若要取消的request不在
pending_list
链表,则遍历started_list
链表查找,若找不到,直接返回-EINVAL
错误,反之调用dwc3_stop_active_transfer
函数向控制发送DWC3_DEPCMD_ENDTRANSFER
命令停止传输,最后再调用dwc3_gadget_giveback
函数回调request的回调函数,将其从pending_list
链表中删除。
2.7.map和unmap USB请求
dwc3驱动在发送USB请求的时候需要调用
usb_gadget_map_request
映射缓冲区,获取缓冲区的DMA地址。USB请求发送完或dequeue USB请求的时候,需要调用usb_gadget_unmap_request
将缓冲区进行反向映射。[include/linux/usb/gadget.h] int usb_gadget_map_request(struct usb_gadget *gadget, struct usb_request *req, int is_in); void usb_gadget_unmap_request(struct usb_gadget *gadget, struct usb_request *req, int is_in);
2.7.1.map USB请求
usb_gadget_map_request
的执行流程如下图所示,dma_map_sg
适用于支持SG的DMA,dma_map_single
适用于不支持SG的DMA,底层映射的机制是相同的,这里只说明dma_map_single
。- 获取DMA的操作函数集合
dma_map_ops
,RK3399上使用swiotlb_dma_ops
。在drivers/iommu/rockchip-iommu.c文件中调用arch_setup_dma_ops
设置。 - 验证DMA的方向,是
DMA_BIDIRECTIONAL
(数据可双向移动)、DMA_TO_DEVICE
(数据传输到设备中)、DMA_FROM_DEVICE
(数据从设备中传出)其中之一。 - 调用
swiotlb_map_page
进行映射,将缓冲区地址转换为DMA地址,若不是coherent缓冲区,且是DMA_TO_DEVICE
方向,则需要clean cache,若是DMA_FROM_DEVICE
,则需要invalidate cache。这里只是简单的说明一下,关于DMA映射的代码,后面专门介绍。
2.7.2.unmap USB请求
usb_gadget_unmap_request
的执行流程如下图所示,dma_unmap_sg
适用于支持SG的DMA,dma_unmap_single
适用于不支持SG的DMA,底层映射的机制是相同的,这里只说明dma_unmap_single
。- 获取DMA的操作函数集合
dma_map_ops
,rk3399上使用swiotlb_dma_ops
。在drivers/iommu/rockchip-iommu.c文件中调用arch_setup_dma_ops
设置。 - 验证DMA的方向,是
DMA_BIDIRECTIONAL
(数据可双向移动)、DMA_TO_DEVICE
(数据传输到设备中)、DMA_FROM_DEVICE
(数据从设备中传出)其中之一。 - 若不是coherent缓冲区,且是
DMA_FROM_DEVICE
,则需要invalidate cache,接着调用swiotlb_unmap_page
解除缓冲区映射。
2.7.3.swiotlb_dma_ops
swiotlb_dma_ops
是arm64平台上使用软件实现的smmu/iommu,定义如下。[arch/arm64/mm/dma-mapping.c] void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size, struct iommu_ops *iommu, bool coherent); static struct dma_map_ops swiotlb_dma_ops = { .alloc = __dma_alloc, .free = __dma_free, .mmap = __swiotlb_mmap, .get_sgtable = __swiotlb_get_sgtable, .map_page = __swiotlb_map_page, .unmap_page = __swiotlb_unmap_page, .map_sg = __swiotlb_map_sg_attrs, .unmap_sg = __swiotlb_unmap_sg_attrs, .sync_single_for_cpu = __swiotlb_sync_single_for_cpu, .sync_single_for_device = __swiotlb_sync_single_for_device, .sync_sg_for_cpu = __swiotlb_sync_sg_for_cpu, .sync_sg_for_device = __swiotlb_sync_sg_for_device, .dma_supported = swiotlb_dma_supported, .mapping_error = swiotlb_dma_mapping_error, };
2.8.USB请求回调函数
当USB请求传输完成后,可通过
usb_gadget_giveback_request
函数回调USB请求的回调函数。USB请求的回调函数一般由Function驱动设置。[include/linux/usb/gadget.h] void usb_gadget_giveback_request(struct usb_ep *ep, struct usb_request *req) { if (likely(req->status == 0)) usb_led_activity(USB_LED_EVENT_GADGET); trace_usb_gadget_giveback_request(ep, req, 0); req->complete(ep, req); }
2.9.端点状态
USB协议定义了一组联络代码,表明传输的状态。联络代码会在数据信息包或联络信息包中传输。联络代码有ACK、NAK、STALL、NYET和ERR。具体如下:
- ACK(acknowledge)
表明主机或设备已经正确的接收到了数据。如果令牌和数据信息包被正确的接收了,设备必须在Setup事物的联络信息包中返回ACK。设备也可能会在OUT事物的联络信息包中返回ACK。与设备一样,如果令牌和数据信息包被正确的接收了,主机也会在IN事务的联络信息包中返回ACK。 - NAK(negative acknowledge)
表明设备繁忙或没有数据要返回。若主机在设备繁忙的时候请求数据,则设备端点会在联络信息包中返回NAK。若主机向设备请求数据,而此时设备没有数据要发送,则设备端点会在数据信息包中返回NAK。主机从不发送NAK,等时传输不使用NAK。 - STALL(失败)
表示不被支持的控制请求、控制请求失败或端点失败。
设备在收到不支持的控制请求时,将在数据或状态阶段返回STALL。若设备虽支持请求,但由于某种原因不能执行所请求的动作,设备也会返回STALL。为了解决STALL,主机会发送另一个设置信息包,开始新的控制传输。USB 2.0规范称这种失败类型为“协议失败”(protocol stall)。
另一种失败的方式是端点的Halt(暂停)特性被设置时的响应,这表示端点根本不能发送和接数据。USB规范称这类失败为”功能失败“(function stall)。
批量和中断端点必须支持功能失败。USB 2.0控制端点也可能会支持功能失败,但很少这么做。功能失败的控制端点必须正常响应其他请求,以监视和控制失败状态。端点”能够响应请求“是指能够通信且因此不能被设为设备。等时传输不使用STALL。超高速控制端点不使用功能失败。主机收到功能失败的时,将放弃等待设备的请求,并且不恢复通信,直到主机发送控制请求,成功的清除了设备上的Halt特性。主机从不发送STALL。 - NYET(not yet)
只有高速设备才会发送NYET。高速批量和控制传输都支持一种协议,它使主机在有设备可接收数据的情况下,能够在发送数据之前找到此设备。在低速或全速状态下,主机若想在控制、批量或中断传翰中发送数据,它将首先发送令牌和数据信息包,并接收设备在事务的联络包中所作出的回复。若设备还没准备好接收数据,会返回NAK,主机会在稍候重新尝试。如果数据信息包很大且设备总是没有准备好,不断重新尝试可能会浪费很多总线时间。这种情况下,使用带有多个数据信息包的高速批量和控制传输将是更好的途径。在接收到数据信息包后,设备端点可返回NYET联络信息,它表明端点接收此数据但没有准备好接收另一数据信息包。当主机认为设备可能准备好的时候,主机可发送PING令牌信息包。端点则会返回ACK来表明设备已可接收下个数据信息包,或者如果设备还是没有准备好,将返回NAK或STALL。发送PING的方法只在确定了设备没有准备好,必须在稍后重传的情况下才会比发送整个数据信息包的方式更为有效。主机是否使用PING是可选的。 USB 2.0集线器可能会在完成分割事务中返回NYET。主机以及低速或全速设备从不发送NYET。 - ERR
ERR联络信息只在完成分割事务中,被高速集线器使用。ERR表明设备没有返回事务所预期的表明集线器与主机完成通信的联络信息号。 - 无响应
另一种状态指示类型发生在主机或设备期望接收联络但却没有收到任何信息的情况下。接收端的错误检查发现错误时,就会产生这种响应缺失。若没接收到任何响应,发送端需要重新尝试,在多个尝试失败后,发送端可采取其他动作。
UDC驱动提供了设置端点状态的函数,如下所示。
usb_ep_set_halt
可以将端点特性设置为Halt,此时端点将处于STALL状态,不发送数据也不接收数据,除非主机发送CLEAR_FEATURE请求。usb_ep_clear_halt
函数用于清除端点的Halt特性,此时端点将从STALL状态中恢复。usb_ep_set_wedge
将端点特性设置为Halt并且会忽略CLEAR_FEATURE请求,只有Function驱动才可以清除Halt特性。[include/linux/usb/gadget.h] int usb_ep_set_halt(struct usb_ep *ep); int usb_ep_clear_halt(struct usb_ep *ep); int usb_ep_set_wedge(struct usb_ep *ep);
2.9.1.端点0 halt特性
端点0设置和清除halt特性的执行流程如下图所示。
usb_ep_set_halt
和usb_ep_clear_halt
函数最终都会调到dwc3 gadget驱动的dwc3_gadget_ep0_set_halt
函数,当第二个参数value=1
时表示usb_ep_set_halt
,value=0
时表示usb_ep_clear_halt
。虽然上层区t分了se halt和clear halt,但对于端点0,底层都是进行stall and restart操作,流程如下:- 将ep0_in端点标志设置为
DWC3_EP_ENABLED
。 - 将ep0_out端点的状态设置为stall。
- 清除ep0_out端点
pending_list
上所有USB请求。 - 设置ep0_ou端点控制传输的阶段为
EP0_SETUP_PHASE
,准备被主机枚举。 - 设置ep0_out开始传输,准备接收主机发送的USB控制请求。
2.9.2.非端点0 halt特性
非端点0设置和清除halt特性的执行流程如下图所示。
usb_ep_set_halt
和usb_ep_clear_halt
函数最终都会调到dwc3 gadget驱动的dwc3_gadget_ep_set_halt
函数,当第二个参数value=1
时表示usb_ep_set_halt
,value=0
时表示usb_ep_clear_halt
。蓝色表示usb_ep_set_halt
函数执行路径,橙黄色表示usb_ep_clear_halt
函数执行路径。绿色两者都会执行。需要注意的是isoc端点无法设置和清除halt特性。
usb_ep_set_halt
执行流程如下:- 非protocol STALL的IN端点并且最近使用的TRB未传输完成或OUT端点有pending的request,说明该端点还有未处理完的request,不能halt,返回-EAGAIN错误。
- 若端点上没有未处理request,则将该端点设置为stall状态。
- 端点标志设置为
DWC3_EP_STALL
。
usb_ep_clear_halt
执行流程如下:- 发送
DEPCMD_CLEARSTALL
命令,清除端点的stall状态。 - 清除端点的
DWC3_EP_STALL
和DWC3_EP_WEDGE
标志,DWC3_EP_WEDGE
标志设置时主机无法清除设备端点的stall状态,但设备驱动可以自己清除。
2.9.3.set_wedge
usb_ep_set_wedge
函数将端点的状态设置为stall,并且会忽略主机发送的CLEAR_FEATURE
请求,也就是说主机无法清除带有DWC3_EP_WEDGE
标志的端点的stall状态,只有设备驱动自己可以清除。2.10.连接和断开
软件可以通过
usb_gadget_connect
和usb_gadget_disconnect
函数控制设备连接主机和设备和主机断开。usb_gadget_connect
调用后,设备会将D+上拉,主机识别D+上拉后开始枚举设备。[include/linux/usb/gadget.h] int usb_gadget_connect(struct usb_gadget *gadget); int usb_gadget_disconnect(struct usb_gadget *gadget);
usb_gadget_connect
和usb_gadget_disconnect
的执行流程如下图所示。将DWC3_DCTL寄存器第31位设置为1时,USB设备控制器开始运行,会将D+上拉,主机识别D+上拉后开始枚举设备。将DWC3_DCTL寄存器第31位设置为0时,USB设备停止运行,会和主机断开连接,在将第31位设置为0之前,需要将所有传输的USB请求清空,将31位设置为0之后,需要等待设备和主机断开连接的操作完成,通过读取DWC3_DSTS寄存器的第22位判断,为0时表示已断开连接。Function驱动和UDC驱动绑定的时候,实质上是通过
usb_udc_connect_control
函数控制设备和主机连接和断开。主机通过vbus向设备供电,若设备不需要主机供电,则udc->vbus=true
。若需要,则通过usb_udc_vbus_handler
函数更新vbus的状态,像U盘、移动硬盘这类设备,必须要主机供电,供电以后才能开始工作和被主机枚举。[include/linux/usb/gadget.h] static void usb_udc_connect_control(struct usb_udc *udc) { if (udc->vbus) usb_gadget_connect(udc->gadget); else usb_gadget_disconnect(udc->gadget); } /* updates the udc core vbus status, and try to * connect or disconnect gadget */ void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) { struct usb_udc *udc = gadget->udc; if (udc) { udc->vbus = status; usb_udc_connect_control(udc); } }
2.11.属性文件
UDC驱动向/sys目录导出了一些属性文件,供使用者在用户空间查询和操作。下面以rk3399为例,进行介绍:
查看当前的USB控制器是否支持OTG /sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/is_otg 查看当前的USB控制器是否处于设备模式 /sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/is_a_peripheral 查看当前的USB控制器是否是自供电 /sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/is_selfpowered 查看当前的USB控制器速度 /sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/current_speed 查看当前的USB控制器支持的最大速度 /sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/maximum_speed USB控制器的速度定义如下: "UNKNOWN","low-speed","full-speed","high-speed","wireless","super-speed","super-speed-plus"。 控制USB设备控制器连接主机,connect-连接,disconnect-断开连接 /sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/soft_connect 查看当前USB设备控制器的状态 /sys/devices/platform/usb0/fe800000.dwc3/udc/fe800000.dwc3/state USB设备控制器的状态定义如下: "not attached","attached","powered","reconnecting","unauthenticated","default","addressed","configured","suspended"。
3.总结
上面总结了常用的UDC驱动接口,这些接口大多数可被Function驱动直接调用,少部分经过封装被Function驱动调用。通过分析这些接口的调用流程,对认识USB控制器内部的工作流程有很大的帮助。其实最重要的是弄清楚USB设备控制器接收数据和发送数据的流程,发送数据通过
usb_ep_queue
发送,上面已经介绍过了,但接收数据的流程牵扯到中断处理和中断处理线程,后面专门开一章节介绍USB设备控制器的中断处理过程和数据接收过程。参考资料
- Rockchip RK3399TRM V1.3 Part1
- Rockchip RK3399TRM V1.3 Part2
- Linux内核4.4.179版本源码
- USB开发大全(第四版)
-
胜为蓝牙驱动UDC-324B
2019-03-23 19:00:11胜为蓝牙驱动UDC-324B,支持win10,请安提示安装驱动和固件 -
自娱自乐8之Linux UDC驱动4(自编udc驱动,基本功能完成)
2013-10-11 16:34:24直接上代码,可以和我之前写的模板比较比较 /*********************************** Copyright(C), 2013 LDP FileName: s3c2440_udc.h Author: wwxxxxll Date: Description: History: Author Date直接上代码,可以和我之前写的模板比较比较
/*********************************** Copyright(C), 2013 LDP FileName: s3c2440_udc.h Author: wwxxxxll Date: Description: History: Author Date Desc ************************************/ #ifndef __S3C2440_UDC_H__ #define __S3C2440_UDC_H__ /*************配置选项**************/ #define S3C2440_DEBUG_FS //使用debugfs #define DEBUG #ifdef DEBUG #define printInfo(ARGs...) printk(ARGs) #else #define printInfo(ARGs...) #endif //struct usb_ep_ops //#define S3C2440_NEWSTYLE //使用udc_start #define S3C2440_SETWEDHE //实现set_weght方法 #define S3C2440_FIFO_STATUS //支持fifo_status方法 #define S3C2440_FIFO_FLUSH //支持fifo_flush方法 //struct usb_gadget_ops #define S3C2440_S3C2440_GET_FRAME //支持get_frame #define S3C2440_WAKEUP //支持wakeup功能 #define S3C2440_SELFPOWERED //selfpowered支持 //#define S3C2440_VBUS_SESSION //vbus连接控制支持 //#define S3C2440_VBBUS_DRAW #define S3C2440X_PULLUP //usb连接控制支持 //s3c2440 有时钟控制 //寄存器CLKSLOW开启UPLL //CLKCON开启USB device时钟,我会定义两个clk,见下面的结构体 //我可以直接操作寄存器,但是那样太粗鲁了,我们还是用平台提供 //的时钟机制解决吧 #define S3C2440_HAVE_CLK //有专用的CLK #ifdef S3C2440_HAVE_CLK #define CLK_DELAY_TIME 10 //ms #endif #define S3C2440_USE_IRQ //端口信息 #define S3C2440_ENDPOINTS 5 //端口数 //一个端点的最大数据包 #define EP0_FIFO_SIZE 8 #define EP1_FIFO_SIZE 64 #define EP2_FIFO_SIZE 64 #define EP3_FIFO_SIZE 64 #define EP4_FIFO_SIZE 64 #define EP1_ADDRESS 1 #define EP2_ADDRESS 2 #define EP3_ADDRESS 3 #define EP4_ADDRESS 4 #define EP1_ATTR USB_ENDPOINT_XFER_BULK #define EP2_ATTR USB_ENDPOINT_XFER_BULK #define EP3_ATTR USB_ENDPOINT_XFER_BULK #define EP4_ATTR USB_ENDPOINT_XFER_BULK //fifo长度 #define S3C2440_EP0_FIFO_SIZE 16 #define S3C2440_EP1_FIFO_SIZE 128 #define S3C2440_EP2_FIFO_SIZE 128 #define S3C2440_EP3_FIFO_SIZE 128 #define S3C2440_EP4_FIFO_SIZE 128 /***********************************/ /*************寄存器定义************/ //s3c2440 有个MISCCR控制usb1为设备还是主机 //芯片默认为设备,我就不控制了。MISCCR的USB挂起 //主要为芯片进入睡眠时用的 //如果按字节模式访问则偏移地址在大端和小端模式中是不同的。 //我是小端的地址 #define FUNC_ADDR_REG 0x140 //func_addr_reg 存储usb地址,更新地址时,第7位置位 #define PWR_REG 0x144 /* pwr_reg: 3: USB_RESET R 主机发复位信号,由USB置位 2: MCS_RESUME R/W MCU置位来给MCU重置,在挂起模式时,产生10ms重置信号 1: SUSPEND_MODE R 设备进入挂起模式时由USB置位。 0: SUBSPEND_EN R 挂起使能位,0:禁止 1:使能 */ //一旦 MCU 发生中断,MCU 应该读取中断相关寄存器的内容并且如果需要写回清除其内容。 #define EP_INT_REG 0x148 #define USB_INT_REG 0x158 //中断使能 #define EP_INT_EN_REG 0x15c #define USB_INT_EN_REG 0x16c //帧号 #define FRAME_NUM1_REG 0x170 //低字节 #define FRAME_NUM2_REG 0x174 //高字节 //通常被标记的寄存器随INDEX寄存器(INDEX_REG)(偏移地址:0X178)值而定。例如如果希望改写EP0 //CSR寄存器,必须在写IN_CSR1寄存器前写入‘0x00’到INDEX_REG中 #define INDEX_REG 0x178 #define MAXP_REG 0x180 /* 推荐: EP0 MAXP=8 EP1~4 MAXP=64, 64自动使能双数据包模式 就是data0和data1 */ #define EP0_CSR 0x184 #define IN_CSR1_REG 0x184 #define IN_CSR2_REG 0x188 #define OUT_CSR1_REG 0x190 #define OUT_CSR2_REG 0x194 //FIFO //端点输出写计数寄存器 //此寄存器保存着包的字节数,该数由MCU卸载 #define OUT_FIFO_CNT1 0x198 #define OUT_FIFO_CNT2 0x19c //EPn_FIFO_REG使能MCU访问EPn FIFO #define EP0_FIFO 0x1c0 #define EP1_FIFO 0x1c4 #define EP2_FIFO 0x1c8 #define EP3_FIFO 0x1cc #define EP4_FIFO 0x1d0 //DMA #define EP1_DMA_CON 0x200 #define EP2_DMA_CON 0x218 #define EP3_DMA_CON 0x240 #define EP4_DMA_CON 0x258 #define EP1_DMA_UNIT 0x204 #define EP2_DMA_UNIT 0x21c #define EP3_DMA_UNIT 0x244 #define EP4_DMA_UNIT 0x25c #define EP1_DMA_FIFO 0x208 #define EP2_DMA_FIFO 0x220 #define EP3_DMA_FIFO 0x248 #define EP4_DMA_FIFO 0x260 #define EP1_DMA_TTC_L 0x20c #define EP1_DMA_TTC_M 0x210 #define EP1_DMA_TTC_H 0x214 #define EP2_DMA_TTC_L 0x224 #define EP2_DMA_TTC_M 0x228 #define EP2_DMA_TTC_H 0x22c #define EP3_DMA_TTC_L 0x24c #define EP3_DMA_TTC_M 0x250 #define EP3_DMA_TTC_H 0x254 #define EP4_DMA_TTC_L 0x264 #define EP4_DMA_TTC_M 0x268 #define EP4_DMA_TTC_H 0x26c /***********************************/ /************操作定义***************/ #define WRITE_REG(_s3c2440_udc, reg, data) writel(data, _s3c2440_udc->virl_addr + reg) #define READ_REG(_s3c2440_udc, reg) readl(_s3c2440_udc->virl_addr + reg) #define SETB(_s3c2440_udc, reg, n) (writel((readl(_s3c2440_udc->virl_addr + reg) | (1 << n)), _s3c2440_udc->virl_addr + reg)) #define CLRB(_s3c2440_udc, reg, n) (writel((readl(_s3c2440_udc->virl_addr + reg) & (~(1 << n))), _s3c2440_udc->virl_addr + reg)) #define GETB(_s3c2440_udc, reg, n) ((readl(_s3c2440_udc->virl_addr + reg) >> n) & 1) //我的D+控制口为gpc5,这里我也偷懒了,没有mmap gpio,用了平台的 #define PULL_UP() do { \ writel((readl(S3C2410_GPCCON) | (1 << 10)) & (~(1 << 11)), S3C2410_GPCCON); \ writel(readl(S3C2410_GPCUP) & (~(1 << 5)), S3C2410_GPCUP); \ writel(readl(S3C2410_GPCDAT) | (1 << 5), S3C2410_GPCDAT); \ }while(0); #define PULL_DOWN() do { \ writel((readl(S3C2410_GPCCON) | (1 << 10)) & (~(1 << 11)), S3C2410_GPCCON); \ writel(readl(S3C2410_GPCUP) & (~(1 << 5)), S3C2410_GPCUP); \ writel(readl(S3C2410_GPCDAT) & (~(1 << 5)), S3C2410_GPCDAT); \ }while(0); /***********************************/ /*************简单操作**************/ //清楚setup_end标志 #define EP0_CLRSE(_s3c2440_udc) do { \ WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \ SETB(_s3c2440_udc, EP0_CSR, 7); \ }while(0) //out数据已读完标志 #define EP0_CLROPR(_s3c2440_udc) do { \ WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \ SETB(_s3c2440_udc, EP0_CSR, 6); \ }while(0) #define EP0_SETDE(_s3c2440_udc) do {\ WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \ SETB(_s3c2440_udc, EP0_CSR, 3); \ }while(0) //清楚stall标志 #define EP0_CLRSST(_s3c2440_udc) do { \ WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \ CLRB(_s3c2440_udc, EP0_CSR, 5); \ }while(0) //发送stall #define EP0_SETSST(_s3c2440_udc) do { \ WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \ SETB(_s3c2440_udc, EP0_CSR, 5); \ }while(0) //清楚数据异常 #define EP0_CLRDE(_s3c2440_udc) do { \ WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \ CLRB(_s3c2440_udc, EP0_CSR, 2); \ }while(0) //in数据已写完标志 #define EP0_SETIPR(_s3c2440_udc) do { \ WRITE_REG(_s3c2440_udc, INDEX_REG, 0); \ SETB(_s3c2440_udc, EP0_CSR, 1); \ }while(0) /***********************************/ struct s3c2440_ep { struct usb_ep ep; //描述一个端点 struct list_head queue; struct s3c2440_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 S3C2440_SETWEDHE unsigned wedged :1; #endif }; #define to_s3c2440_ep(ep_p) container_of(ep_p, struct s3c2440_ep, ep) struct s3c2440_request { struct list_head queue; /* ep's requests */ struct usb_request req; //对应主机端看到的urb }; #define to_s3c2440_req(req_p) container_of(req_p, struct s3c2440_request, req) //根据实际要求定义,这个不能当做模板,主要是便于软件管理 //一般有下面几个,有的驱动不用这些定义去管理 enum ep0state { EP0_IDLE, EP0_IN, EP0_OUT, EP0_STALL, }; struct s3c2440_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 s3c2440_ep ep[S3C2440_ENDPOINTS]; struct s3c2440_request fifo_req; #ifdef S3C2440_DEBUG_FS struct dentry *debug_info; #endif #ifdef S3C2440_HAVE_CLK struct clk *s3c2440_clk_upll; struct clk *s3c2440_clk_udc; #endif #ifdef S3C2440_USE_IRQ unsigned int irq_num; #endif u16 devstatus; }; #define to_s3c2440_udc(gadget_p) container_of(gadget_p, struct s3c2440_udc, gadget) #endif//__S3C2440_UDC_H__
/*********************************** Copyright(C), 2013 LDP FileName: s3c2440_udc.c Author: wwxxxxll Date: Description: linux-3.2-36 History: Author Date Desc ************************************/ #include <linux/module.h>//MODULE_* #include <linux/init.h>//printInfo #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 <linux/prefetch.h> #include <asm/irq.h> #include <asm/io.h>//ioremap #include <mach/regs-gpio.h> #include "s3c2440_udc.h" #ifdef S3C2440_DEBUG_FS #include <linux/debugfs.h> #include <linux/seq_file.h>//seq_printf seq_read #endif #define DRIVER_DESC "S3C2440 USB Device Controller Gadget" #define DRIVER_VERSION "2013" #define DRIVER_AUTHOR "wwxxxxll" static const char gadget_name[] = "s3c2440_udc"; static const char driver_desc[] = DRIVER_DESC; //根据实际情况修改 //在epautoconf.c会看到 /* type-restriction: "-iso", "-bulk", or "-int". * direction-restriction: "in", "out". */ //我们 static const char ep0name[] = "ep0"; static const char * const ep_name[] = { ep0name, "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk", }; //需要mount -t debugfs none /sys/kernel/debug/ ///sys/kernel/debug #ifdef S3C2440_DEBUG_FS static struct dentry *s3c2440_udc_debugfs_root; static int s3c2440_udc_debugfs_seq_show(struct seq_file *m, void *p) { seq_printf(m, "My name is %s\n", gadget_name); return 0; } static int s3c2440_udc_debugfs_fops_open(struct inode *inode, struct file *file) { return single_open(file, s3c2440_udc_debugfs_seq_show, NULL); } static const struct file_operations s3c2440_udc_debugfs_fops = { .open = s3c2440_udc_debugfs_fops_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, .owner = THIS_MODULE, }; #endif /***********************hardware_handler************************/ //s3c2440没有控制usb开启位,就等到start时使能中断,imx不是这样 static void s3c2440_usb_reset(struct s3c2440_udc *dev) { //disable intterupt WRITE_REG(dev, EP_INT_EN_REG, 0x00); WRITE_REG(dev, USB_INT_EN_REG, 0x00); //clear intterupt flag WRITE_REG(dev, EP_INT_REG, 0x1f); WRITE_REG(dev, USB_INT_REG, 0x07); PULL_DOWN(); dev->gadget.speed = USB_SPEED_UNKNOWN; } static void s3c2440_udc_enable(struct s3c2440_udc *dev) { int i; dev->gadget.speed = USB_SPEED_FULL;//s3c2440只支持1.1 for (i = 0; i < S3C2440_ENDPOINTS; i++)//最大包设置 { WRITE_REG(dev, INDEX_REG, i); WRITE_REG(dev, MAXP_REG, dev->ep[i].ep.maxpacket >> 3); } //SETB(dev, PWR_REG, 0);//enable suspend模式 //enable intterupt SETB(dev, EP_INT_EN_REG, 0); WRITE_REG(dev, USB_INT_EN_REG, 0x07); #ifndef S3C2440_NEWSTYLE PULL_UP(); #endif } static void s3c2440_usb_fifocnt(struct s3c2440_udc *dev, u32 *fifo_size) { *fifo_size = READ_REG(dev, OUT_FIFO_CNT1); *fifo_size |= READ_REG(dev, OUT_FIFO_CNT2) << 8; } static u32 s3c2440_read_ctrlq(struct s3c2440_udc *dev, struct usb_ctrlrequest *_ctrlq) { u32 count; WRITE_REG(dev, INDEX_REG, 0); s3c2440_usb_fifocnt(dev, &count); count = (count > sizeof(struct usb_ctrlrequest)) ? sizeof(struct usb_ctrlrequest) : count; readsb(EP0_FIFO + dev->virl_addr, (unsigned char *)_ctrlq, count); /* _ctrlq->bRequest bit7: 方向 10 int 0: out bit 6:5: 请求类型 bit 4:0: 接收者 */ printInfo( "Host: bRequest = %02x bRequestType = %02x \ wValue = 0x%x wIndex=0x%x wLength=0x%x\n", _ctrlq->bRequest, _ctrlq->bRequestType, \ _ctrlq->wValue, _ctrlq->wIndex, _ctrlq->wLength); return count; } static int s3c2440_get_status(struct s3c2440_udc *dev, struct usb_ctrlrequest *crq) { u16 status = 0; u8 ep_num = crq->wIndex & 0x7F;//端点号 u8 is_in = crq->wIndex & USB_DIR_IN;//方向 switch (crq->bRequestType & USB_RECIP_MASK) { case USB_RECIP_INTERFACE://2.0为预留 break; case USB_RECIP_DEVICE://设备状态是0位表示供电方式,1位表示是否可以远程唤醒 status = dev->devstatus; break; case USB_RECIP_ENDPOINT://0位表示halt,如果当前的ep为halt,此位为1 if (ep_num > 4 || crq->wLength > 2)//crq->wLength为2 return 1; WRITE_REG(dev, INDEX_REG, ep_num); status = 0; if (ep_num == 0) { status = GETB(dev, EP0_CSR, 5); } else { if (is_in) { status = GETB(dev, IN_CSR1_REG, 4); } else { status = GETB(dev, OUT_CSR1_REG, 5); } } break; default: return 1; } WRITE_REG(dev, EP0_FIFO, status & 0xFF); WRITE_REG(dev, EP0_FIFO, status >> 8); EP0_SETIPR(dev); EP0_SETDE(dev); return 0; } static void s3c2440_udc_done(struct s3c2440_ep *ep, struct s3c2440_request *req, int status); /* 返回 1: 读包结束 0: 还没读完 -1: 错误 */ static int s3c2440_read_fifo(struct s3c2440_udc *dev, struct s3c2440_ep *ep, struct s3c2440_request *req) { u32 fifo_reg; u8 *buf; u32 idx = 0; u32 avail, len, bufspace; u32 fifo_count = 0; int ret = 0; //printInfo( "%s\n", __func__); idx = ep->bEndpointAddress & 0x7f; if (idx > 4) { idx = 0; } WRITE_REG(dev, INDEX_REG, idx); s3c2440_usb_fifocnt(dev, &fifo_count); if (fifo_count == 0) { return 0; } fifo_reg = EP0_FIFO + 4 * idx; if (req->req.length == 0) { return 1; } if (req->req.length <= req->req.actual) { return -1; } bufspace = req->req.length - req->req.actual; buf = req->req.buf + req->req.actual; avail = (fifo_count > ep->ep.maxpacket) ? ep->ep.maxpacket : fifo_count;//一次最多读ep->ep.maxpacket len = (bufspace < avail) ? bufspace : avail; req->req.actual += len; readsb(fifo_reg + dev->virl_addr, buf, len); //req->req.actual已接收长度,req->req.length要接收的总长度 //printInfo("read: fifo_count = %d, req->req.actual = %d, req->req.length = %d len = %d avail = %d\n", fifo_count, req->req.actual, req->req.length, len, avail); if (fifo_count < ep->ep.maxpacket) { ret = 1; if (len != avail) { req->req.status = -EOVERFLOW;//溢出 } } if (ret) { if (idx == 0) { EP0_SETDE(dev); ep->dev->ep0state = EP0_IDLE; } else { CLRB(dev, OUT_CSR1_REG, 0); } s3c2440_udc_done(ep, req, 0); } else { if (idx == 0) { EP0_CLROPR(dev); } else { //SETB(dev, OUT_CSR1_REG, 4); CLRB(dev, OUT_CSR1_REG, 0); } } return ret; } #ifdef DEBUG static int printDesc = 0; #endif static int s3c2440_write_fifo(struct s3c2440_udc *dev, struct s3c2440_ep *ep, struct s3c2440_request *req) { u32 fifo_reg; u8 *buf; u32 idx = 0; u32 len; int ret = 0; struct usb_device_descriptor *desc; struct usb_string_descriptor *string; struct usb_config_descriptor *config; u16 language; u32 n; u8 *tmp; #ifdef DEBUG //printInfo( "%s\n", __func__); switch (printDesc) { case USB_DT_DEVICE: desc = (struct usb_device_descriptor*)req->req.buf; printInfo( "Slave: length = %d Vendor = %x Product = %x Device = %x iManufacturer = %d iProduct = %d iSerialNumber = %d bNumConfigurations = %d\n", \ desc->bLength, le16_to_cpu(desc->idVendor), le16_to_cpu(desc->idProduct), le16_to_cpu(desc->bcdDevice),\ desc->iManufacturer,desc->iProduct,desc->iSerialNumber,desc->bNumConfigurations); break; case USB_DT_DEVICE_QUALIFIER: break; case USB_DT_OTHER_SPEED_CONFIG: break; case USB_DT_CONFIG: config = (struct usb_config_descriptor *)req->req.buf; printInfo( "Slave: length = %d TotalLength = %d NumInterfaces = %d ConfigurationValue = %d iConfiguration = %d bMaxPower = %d\n", \ config->bLength, le16_to_cpu(config->wTotalLength), config->bNumInterfaces, config->bConfigurationValue, config->iConfiguration, config->bMaxPower); break; case USB_DT_STRING: string = (struct usb_string_descriptor *)req->req.buf; printInfo( "Slave: length = %d\n", string->bLength); language = cpu_to_le16(0x0409);//这里偷工减料了,因为gadget是我自己写的我知道是什么语言 if (string->bLength == 4)//支持语言数量 { break; } for (tmp = (u8 *)string->wData, n = 0; n < string->bLength; n++, tmp++) { if (*tmp == language) { } else { printInfo( "%c", *tmp);//没考虑大小端 } } printInfo("\n"); break; case USB_DT_BOS: break; default: break; } printDesc = 0; #endif idx = ep->bEndpointAddress & 0x7f; if (idx > 4) { idx = 0; } fifo_reg = EP0_FIFO + 4 * idx; len = ((req->req.length - req->req.actual) < ep->ep.maxpacket) ? (req->req.length - req->req.actual) : ep->ep.maxpacket; buf = req->req.buf + req->req.actual; prefetch(buf);//prefetch将这一块数据读取到cache之中,以便后继快速访问,为了下面的writesb req->req.actual += len; writesb(fifo_reg + dev->virl_addr, buf, len); //req->req.actual已发送长度 //printInfo( " %dbytes ", req->req.actual); if (len != ep->ep.maxpacket) ret = 1; else if (req->req.length != req->req.actual || req->req.zero)//zero当要发送的长度小于请求长度是为1 ret = 0; else ret = 2; //printInfo( \ "Written ep%d %d.%d of %d b [last %d,z %d], max = %d\n", \ idx, len, req->req.actual, req->req.length, \ ret, req->req.zero,ep->ep.maxpacket); if (ret) { if (idx == 0) { if (!GETB(dev, USB_INT_REG, 2)) { EP0_SETIPR(dev); EP0_SETDE(dev); } ep->dev->ep0state = EP0_IDLE; } else { SETB(dev, IN_CSR1_REG, 0); } printInfo("write done\n"); s3c2440_udc_done(ep, req, 0); } else { if (idx == 0) { if (!GETB(dev, USB_INT_REG, 2)) { EP0_SETIPR(dev); } } else { SETB(dev, IN_CSR1_REG, 0); } } return ret; } static void s3c2440_dequeue_all(struct s3c2440_ep *ep, int status); static int s3c2440_udc_set_halt(struct usb_ep *_ep, int value); static void s3c2440_udc_handle_ep0_idle(struct s3c2440_udc *dev, struct s3c2440_ep *ep, u32 ep0_csr) { struct usb_ctrlrequest ctrlq; int tmp; bool config = 0; //printInfo( "%s\n", __func__); if (!(ep0_csr & 1))//判断数据是否接收完成 { return; } s3c2440_dequeue_all(ep, -EPROTO); if (s3c2440_read_ctrlq(dev, &ctrlq) < sizeof(struct usb_ctrlrequest)) { EP0_SETSST(dev); return; } //EP0_CLROPR是数据接收结束,EP0_SETDE是数据传输结束 switch (ctrlq.bRequest) { case USB_REQ_GET_STATUS: printInfo( "USB_REQ_GET_STATUS\n"); EP0_CLROPR(dev); if ((ctrlq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { if (!s3c2440_get_status(dev, &ctrlq)) { return; } } break; case USB_REQ_CLEAR_FEATURE: printInfo( "USB_REQ_CLEAR_FEATURE\n"); EP0_CLROPR(dev); if (ctrlq.bRequestType != USB_RECIP_ENDPOINT) break; if (ctrlq.wValue != USB_ENDPOINT_HALT || ctrlq.wLength != 0) break; s3c2440_udc_set_halt(&dev->ep[ctrlq.wIndex & 0x7f].ep, 0); EP0_CLROPR(dev); EP0_SETDE(dev); return; case USB_REQ_SET_FEATURE: printInfo( "USB_REQ_SET_FEATURE\n"); EP0_CLROPR(dev); if (ctrlq.bRequestType != USB_RECIP_ENDPOINT) break; if (ctrlq.wValue != USB_ENDPOINT_HALT || ctrlq.wLength != 0) break; s3c2440_udc_set_halt(&dev->ep[ctrlq.wIndex & 0x7f].ep, 1); EP0_CLROPR(dev); EP0_SETDE(dev); return; case USB_REQ_SET_ADDRESS: printInfo( "USB_REQ_SET_ADDRESS\n"); if (ctrlq.bRequestType == USB_RECIP_DEVICE) { tmp = ctrlq.wValue & 0x7F; WRITE_REG(dev, FUNC_ADDR_REG, (1 << 7) | tmp); EP0_CLROPR(dev); EP0_SETDE(dev); dev->ep0state = EP0_IDLE; return; } break; case USB_REQ_GET_DESCRIPTOR: printInfo( "USB_REQ_GET_DESCRIPTOR\n"); switch (ctrlq.wValue >> 8) { case USB_DT_DEVICE: printInfo( "USB_DT_DEVICE\n"); break; //设备限定描述符用于指定另一传输速率下该设备的总体信息,如果高速 USB设备既需要采用高速传输又需 //要全速传输,则它必须支持设备限定描述符(Device_Qualifier)。全速设备不支持 case USB_DT_DEVICE_QUALIFIER: printInfo( "USB_DT_DEVICE_QUALIFIER\n"); break; case USB_DT_OTHER_SPEED_CONFIG: printInfo( "USB_DT_OTHER_SPEED_CONFIG\n"); break; case USB_DT_CONFIG: printInfo( "USB_DT_CONFIG\n"); break; case USB_DT_STRING: printInfo( "USB_DT_STRING\n"); break; //其他速率配置描述符用于指定另一传输速率下该设备的配置信息,如果高速USB设备既需要采用高速传输 //又需要全速传输,则它必须支持其他速率配置描述符 case USB_DT_BOS: printInfo( "USB_DT_BOS\n"); break; } EP0_CLROPR(dev); break; case USB_REQ_SET_DESCRIPTOR: printInfo( "USB_REQ_SET_DESCRIPTOR\n"); EP0_CLROPR(dev); break; case USB_REQ_GET_CONFIGURATION: printInfo( "USB_REQ_GET_CONFIGURATION\n"); EP0_CLROPR(dev); break; case USB_REQ_SET_CONFIGURATION: if (ctrlq.bRequestType == USB_RECIP_DEVICE) { printInfo( "USB_REQ_SET_CONFIGURATION\n"); config = 1; EP0_CLROPR(dev); EP0_SETDE(dev); } break; case USB_REQ_GET_INTERFACE: printInfo( "USB_REQ_GET_INTERFACE\n"); EP0_CLROPR(dev); break; case USB_REQ_SET_INTERFACE: if (ctrlq.bRequestType == USB_RECIP_INTERFACE) { printInfo( "SB_REQ_SET_INTERFACE\n"); config = 1; EP0_CLROPR(dev); EP0_SETDE(dev); } break; case USB_REQ_SYNCH_FRAME: printInfo( "USB_REQ_SYNCH_FRAME\n"); EP0_CLROPR(dev); break; } if (config != 1)//设置就一次传输就可以了 { if (ctrlq.bRequestType & USB_DIR_IN) dev->ep0state = EP0_IN; else dev->ep0state = EP0_OUT; } if (!dev->driver) return; #ifdef DEBUG //为了queue()中的调试打印设置标志 switch (ctrlq.bRequest) { case USB_REQ_GET_DESCRIPTOR: switch (ctrlq.wValue >> 8) { case USB_DT_DEVICE:printDesc = USB_DT_DEVICE; break; case USB_DT_DEVICE_QUALIFIER: printDesc = USB_DT_DEVICE_QUALIFIER; break; case USB_DT_OTHER_SPEED_CONFIG: printDesc = USB_DT_OTHER_SPEED_CONFIG; break; case USB_DT_CONFIG: printDesc = USB_DT_CONFIG; break; case USB_DT_STRING: printDesc = USB_DT_STRING; break; case USB_DT_BOS: break; } break; } #endif if (dev->driver->setup(&dev->gadget, &ctrlq) < 0) { if (config == 1)//配置错误,不要send stall,会重新选配置 { return; } EP0_SETSST(dev); EP0_SETDE(dev); dev->ep0state = EP0_IDLE; } } void s3c2440_handle_ep0(struct s3c2440_udc *dev) { struct s3c2440_ep *ep = &dev->ep[0]; struct s3c2440_request *req; u32 ep0_csr = 0; if (!list_empty(&ep->queue)) req = list_entry(ep->queue.next, struct s3c2440_request, queue); else req = NULL; WRITE_REG(dev, INDEX_REG, 0); ep0_csr = READ_REG(dev, EP0_CSR); if (ep0_csr & (1 << 5))//send_stall { s3c2440_dequeue_all(ep, -EPIPE);//调用complete函数 EP0_CLRSST(dev); dev->ep0state = EP0_IDLE; return; } if (ep0_csr & (1 << 4))//setup_end { s3c2440_dequeue_all(ep, 0); EP0_CLRSE(dev); dev->ep0state = EP0_IDLE; } switch (dev->ep0state) { case EP0_IDLE: s3c2440_udc_handle_ep0_idle(dev, ep, ep0_csr); break; case EP0_IN: if ((!(ep0_csr & (1 << 1))) && req) { s3c2440_write_fifo(dev, ep, req); } break; case EP0_OUT: if ((ep0_csr & 1) && req) { s3c2440_read_fifo(dev, ep, req); } break; case EP0_STALL: dev->ep0state = EP0_IDLE; break; } } void s3c2440_handle_ep(struct s3c2440_udc *dev, u8 n) { struct s3c2440_ep *ep = &dev->ep[n]; struct s3c2440_request *req; u32 ep_csr1; if (!list_empty(&ep->queue)) req = list_entry(ep->queue.next, struct s3c2440_request, queue); else req = NULL; WRITE_REG(dev, INDEX_REG, n); if (ep->bEndpointAddress & USB_DIR_IN) { ep_csr1 = READ_REG(dev, IN_CSR1_REG); if (ep_csr1 & (1 << 5))//SENT_STALL { CLRB(dev, IN_CSR1_REG, 5); return; } if ((!(ep_csr1 & 1)) && req) { s3c2440_write_fifo(dev, ep, req); } } else { ep_csr1 = READ_REG(dev, OUT_CSR1_REG); if (ep_csr1 & (1 << 6)) //SENT_STALL { CLRB(dev, OUT_CSR1_REG, 6); return; } if ((ep_csr1 & 1) && req) { s3c2440_read_fifo(dev, ep, req); } } } //udc的这个中断,真是包罗万象,各硬件差别比较大 //简单一点说,就是清楚中断标致位,再根据中断标志位对应处理 //实际要复杂的多,如果是ep0,还会从fifo中取得usb_ctrlrequest //进行对应的处理,我们在实现具体的实现时再说吧 static irqreturn_t s3c2440_udc_irq(int dummy, void *_dev) { struct s3c2440_udc *dev = (struct s3c2440_udc *)_dev; unsigned long flags; int usb_status; int ep_status; int pwr_reg; int ep0csr; u8 n; //printInfo( "enter irq\n"); spin_lock_irqsave(&dev->lock, flags); usb_status = READ_REG(dev, USB_INT_REG); ep_status = READ_REG(dev, EP_INT_REG); //printInfo( "USB_INT_REG = 0x%x\n", usb_status); //printInfo( "EP_INT_REG = 0x%x\n", ep_status); /* Driver connected ? */ if (!dev->driver) { /* Clear interrupts */ WRITE_REG(dev, USB_INT_REG, READ_REG(dev, USB_INT_REG)); WRITE_REG(dev, EP_INT_REG, READ_REG(dev, EP_INT_REG)); } //reset if (usb_status & (1 << 2)) { printInfo( "USB reset\n"); WRITE_REG(dev, INDEX_REG, 0); WRITE_REG(dev, MAXP_REG, (dev->ep[0].ep.maxpacket & 0x7ff) >> 3); dev->ep0state = EP0_IDLE; dev->gadget.speed = USB_SPEED_FULL; s3c2440_dequeue_all(&dev->ep[0], -EPROTO); SETB(dev, USB_INT_REG, 2); spin_unlock_irqrestore(&dev->lock, flags); return IRQ_HANDLED; } //resume if (usb_status & (1 << 1)) { printInfo( "USB resume\n"); SETB(dev, USB_INT_REG, 1); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->resume) dev->driver->resume(&dev->gadget); } //suspend if (usb_status & 1) { printInfo( "USB suspend\n"); SETB(dev, USB_INT_REG, 0); if (dev->gadget.speed != USB_SPEED_UNKNOWN && dev->driver && dev->driver->suspend) dev->driver->suspend(&dev->gadget); dev->ep0state = EP0_IDLE; } if (ep_status & 1) { //printInfo( "USB ep0 irq\n"); SETB(dev, EP_INT_REG, 0); s3c2440_handle_ep0(dev); } for (n = 1; n < S3C2440_ENDPOINTS; n++) { if (ep_status & (1 << n)) { //printInfo( "USB ep%d irq\n", n); SETB(dev, EP_INT_REG, n);//清除中断 s3c2440_handle_ep(dev, n); } } pwr_reg = READ_REG(dev, PWR_REG); WRITE_REG(dev, INDEX_REG, 0); ep0csr = READ_REG(dev, EP0_CSR); if (!usb_status && !ep_status && !pwr_reg && !ep0csr) { for (n = 1; n < S3C2440_ENDPOINTS; n++) { WRITE_REG(dev, INDEX_REG, n); if (GETB(dev, OUT_CSR1_REG, 0)) { s3c2440_handle_ep(dev, n); } } } spin_unlock_irqrestore(&dev->lock, flags); return IRQ_HANDLED; } /***************************************************************/ /***********************queue***********************************/ //对于usb请求,一般都要维护一个list去管理请求 //端点list初始化,存入gadget里 static void s3c2440_usb_reinit(struct s3c2440_udc *dev) { u8 i; /* device/ep0 records init */ INIT_LIST_HEAD (&dev->gadget.ep_list); dev->gadget.ep0 = &dev->ep[0].ep;//ep0单独存放 dev->ep0state = EP0_IDLE; INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); for (i = 0; i < S3C2440_ENDPOINTS; i++) { struct s3c2440_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 s3c2440_udc_done(struct s3c2440_ep *ep, struct s3c2440_request *req, int status) { struct s3c2440_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 s3c2440_dequeue_all(struct s3c2440_ep *ep, int status) { struct s3c2440_request *req; if (&ep->queue == NULL) return; while (!list_empty(&ep->queue)) //list_del_init会删除链表中的元素 { req = list_entry(ep->queue.next, struct s3c2440_request, queue); s3c2440_udc_done(ep, req, status); } } /***************************************************************/ //may not be the endpoint named "ep0".这是gadget.h的源话 /**************************ep_ops*******************************/ //描述端点操作 //当设备配置或接口设置改变时,驱动会enable或disable端口 static int s3c2440_udc_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct s3c2440_udc *dev; struct s3c2440_ep *ep; u32 max; unsigned long flags; printInfo( "%s\n", __func__); ep = to_s3c2440_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 S3C2440_SETWEDHE ep->wedged = 0; #endif ep->bEndpointAddress = desc->bEndpointAddress; WRITE_REG(dev, INDEX_REG, ep->num); WRITE_REG(dev, MAXP_REG, max >> 3); if (desc->bEndpointAddress & USB_DIR_IN) { //SETB(dev, IN_CSR1_REG, 0);//清楚IN_PKT_RDY SETB(dev, IN_CSR1_REG, 3);//FLUSH fifo SETB(dev, IN_CSR1_REG, 6);//CLR DATA SETB(dev, IN_CSR2_REG, 4);//关闭in dma中断,先不用dma //CLRB(dev, IN_CSR2_REG, 4);//打开in dma中断 SETB(dev, IN_CSR2_REG, 5);//in CLRB(dev, IN_CSR2_REG, 6);//批量端点 } else { SETB(dev, IN_CSR1_REG, 6);//CLR DATA SETB(dev, IN_CSR2_REG, 4);//关闭in dma中断,先不用dma CLRB(dev, IN_CSR2_REG, 5);//out //SETB(dev, OUT_CSR1_REG, 0);//清楚IN_PKT_RDY SETB(dev, OUT_CSR1_REG, 7); SETB(dev, OUT_CSR1_REG, 4);//FLUSH fifo CLRB(dev, OUT_CSR2_REG, 6);//批量端点 SETB(dev, OUT_CSR2_REG, 5);//关闭out dma中断,先不用dma //CLRB(dev, OUT_CSR2_REG, 5);//打开out dma中断 } SETB(dev, EP_INT_EN_REG, ep->num);//开中断 spin_unlock_irqrestore(&dev->lock, flags); return 0; } static int s3c2440_udc_ep_disable(struct usb_ep *_ep) { struct s3c2440_udc *dev; struct s3c2440_ep *ep = to_s3c2440_ep(_ep); unsigned long flags; printInfo( "%s\n", __func__); if (!_ep || !ep->desc) { return -EINVAL; } local_irq_save(flags); ep->desc = NULL; ep->stopped = 1; dev = ep->dev; //清除请求list和关闭ep s3c2440_dequeue_all(ep, -ESHUTDOWN);//关机后将无法传输端点 CLRB(dev, EP_INT_REG, ep->num);//关对应ep中断 local_irq_restore(flags); return 0; } //动态分配请求 static struct usb_request *s3c2440_udc_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { struct s3c2440_request *req; printInfo( "%s\n", __func__); if (!_ep) return NULL; req = kzalloc (sizeof(struct s3c2440_request), gfp_flags); if (!req) return NULL; INIT_LIST_HEAD (&req->queue); return &req->req; } //释放请求 static void s3c2440_udc_free_request(struct usb_ep *_ep, struct usb_request *_req) { //struct s3c2440_ep *ep = to_s3c2440_ep(_ep); struct s3c2440_request *req = to_s3c2440_req(_req); printInfo( "%s\n", __func__); if (!_ep || !_req) return; WARN_ON (!list_empty (&req->queue)); kfree(req); req = NULL; } //下面的queue是插入一个请求 //dequeue删除一个请求 static int s3c2440_udc_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) { struct s3c2440_udc *dev; unsigned long flags; struct s3c2440_request *req = to_s3c2440_req(_req); struct s3c2440_ep *ep = to_s3c2440_ep(_ep); u32 ep_csr; printInfo( "%s\n", __func__); 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; } local_irq_save (flags); //因为中断中有queue操作,所以要放在list_empty前 if (unlikely(!_req || !_req->complete || !_req->buf || !list_empty(&req->queue))) //_req或_req->buf为空、complete执行错误、req->queue不为空 { local_irq_restore(flags); return -EINVAL; } _req->status = -EINPROGRESS; _req->actual = 0; WRITE_REG(dev, INDEX_REG, ep->bEndpointAddress & 0x7F); //s3c2440_usb_fifocnt(dev, &fifo_count); if ((ep->bEndpointAddress & 0x7F) == 0) { ep_csr = READ_REG(dev, EP0_CSR); } else { if (ep->bEndpointAddress & USB_DIR_IN) { ep_csr = READ_REG(dev, IN_CSR1_REG); } else { ep_csr = READ_REG(dev, OUT_CSR1_REG); } //(ep->bEndpointAddress & USB_DIR_IN) ? IN_CSR1_REG : OUT_CSR1_REG); } if (list_empty(&ep->queue) && !ep->stopped) { if (ep->bEndpointAddress == 0) { switch(dev->ep0state) { case EP0_IN: if (!(ep_csr & (1 << 1))) { if (s3c2440_write_fifo(dev, ep, req)) { dev->ep0state = EP0_IDLE; req = NULL; } } break; case EP0_OUT: if (ep_csr & 1) { if (s3c2440_read_fifo(dev, ep, req)) { dev->ep0state = EP0_IDLE; req = NULL; } } break; default: local_irq_restore(flags); return -EL2HLT; } } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { if ((!(ep_csr & 1)) && req) { if (s3c2440_write_fifo(dev, ep, req)) { req = NULL; } } } else { if ((ep_csr & 1) && req) { if (s3c2440_read_fifo(dev, ep, req)) { req = NULL; } } } } if (likely(req != 0)) list_add_tail(&req->queue, &ep->queue);//请求入list local_irq_restore(flags); return 0; } static int s3c2440_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct s3c2440_ep *ep = to_s3c2440_ep(_ep); struct s3c2440_udc *dev; int retval = -EINVAL; unsigned long flags; struct s3c2440_request *req = NULL; printInfo( "%s\n", __func__); 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) { s3c2440_udc_done(ep, req, -ECONNRESET); } local_irq_restore (flags); return retval; } #ifdef S3C2440_FIFO_STATUS //fifo状态,返回fifo中的字节数。 //在上层的调用usb_ep_fifo_statu()如果不用fifo或不支持这个操作返回错误-EOPNOTSUPP //net2272就有寄存器EP_AVAIL记录fifo中的字节数。 //s3c2440硬件不支持,没实现,上层调用会得到-EOPNOTSUPP static int s3c2440_udc_fifo_status(struct usb_ep *_ep) { struct s3c2440_ep *ep; u16 retval = 0; printInfo( "%s\n", __func__); ep = to_s3c2440_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 S3C2440_FIFO_FLUSH //冲掉fifo的不明确数据,这个决不用除非端点不能用于任何协议传输,这是上层调用的事 static void s3c2440_udc_fifo_flush(struct usb_ep *_ep) { struct s3c2440_ep *ep; printInfo( "%s\n", __func__); ep = to_s3c2440_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 s3c2440_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) { struct s3c2440_ep *ep; unsigned long flags; int ret = 0; struct s3c2440_udc *dev; ep = container_of(_ep, struct s3c2440_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; dev = ep->dev; spin_lock_irqsave(&ep->dev->lock, flags); if (!list_empty(&ep->queue)) ret = -EAGAIN; #ifdef S3C2440_FIFO_STATUS else if ((ep->bEndpointAddress & USB_DIR_IN) && value && s3c2440_udc_fifo_status(_ep) != 0)//fifo_status是上面实现的 ret = -EAGAIN; #endif else { WRITE_REG(dev, INDEX_REG, ep->num); /* set/clear */ if (value) { if (ep->num == 0) { ep->dev->ep0state = EP0_STALL; //net2272的端点0在setup时自动复位,没有什么操作。s3c2440就不是了 //ep->dev->protocol_stall = 1; //ep0 set_halt EP0_SETSST(dev); } else { //epx(x != 0) set_halt if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { SETB(dev, IN_CSR1_REG, 4); } else { SETB(dev, OUT_CSR1_REG, 5); } } if (wedged)//维护wedged ep->wedged = 1; } else { if (ep->num == 0) { ep->dev->ep0state = EP0_IDLE; EP0_CLRSST(dev); } else { //epx(x != 0) set_halt if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { CLRB(dev, IN_CSR1_REG, 4); } else { CLRB(dev, OUT_CSR1_REG, 5); } } //ep clear_halt ep->wedged = 0; } } ep->stopped = value ? 1 : 0; spin_unlock_irqrestore(&ep->dev->lock, flags); return ret; } //_ep 不能是同步端点,同步端点不支持错误重发机制。在上面判断 static int s3c2440_udc_set_halt(struct usb_ep *_ep, int value) { printInfo( "%s\n", __func__); return s3c2440_set_halt_and_wedge(_ep, value, 0); } #ifdef S3C2440_SETWEDHE static int s3c2440_udc_set_wedge(struct usb_ep *_ep) { printInfo( "%s\n", __func__); if (!_ep || _ep->name == ep0name)//一般都是端点0请求复位 return -EINVAL; return s3c2440_set_halt_and_wedge(_ep, 1, 1); } #endif static const struct usb_ep_ops s3c2440_ep_ops = { .enable = s3c2440_udc_ep_enable, .disable = s3c2440_udc_ep_disable, .alloc_request = s3c2440_udc_alloc_request, .free_request = s3c2440_udc_free_request, .queue = s3c2440_udc_queue, .dequeue = s3c2440_udc_dequeue, .set_halt = s3c2440_udc_set_halt, #ifdef S3C2440_SETWEDHE .set_wedge = s3c2440_udc_set_wedge, #endif #ifdef S3C2440_FIFO_STATUS .fifo_status = s3c2440_udc_fifo_status, #endif #ifdef S3C2440_FIFO_FLUSH .fifo_flush = s3c2440_udc_fifo_flush, #endif }; /***************************************************************/ //USB 设备的常用操作包括:设备连接、设备移除、设备配置、地址分配、数据传输、 //设备挂起、设备唤醒等。 /**************************usb_gadget_ops***********************/ //硬件操作函数 //获取帧号,当主机发送USB 数据包时,每个帧的开始(SOF)包包含一个帧号。 //这个帧号一般自动加载到对应寄存器,此函数主要就是读这些寄存器 //如果设备不支持返回负 static int s3c2440_udc_get_frame(struct usb_gadget *usb_gdt_p) { printInfo( "%s\n", __func__); #ifdef S3C2440_GET_FRAME struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p); int retval = 0; unsigned long flags; spin_lock_irqsave(&dev->lock, flags); retval = READ_REG(dev, S3C2410_UDC_FRAME_NUM2_REG) << 8; retval |= READ_REG(dev, S3C2410_UDC_FRAME_NUM1_REG); spin_unlock_irqrestore(&dev->lock, flags); return retval; #else return -EOPNOTSUPP; #endif } #ifdef S3C2440_WAKEUP //唤醒,举个例子net2272。它的寄存器usbctl0的第五位控制唤醒功能使能 //寄存器usbctl1的第三位通过写1去resume,s3c2440在PWR_REG也有类似 static int s3c2440_udc_wakeup(struct usb_gadget *usb_gdt_p) { struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p); unsigned long flags; printInfo( "%s\n", __func__); spin_lock_irqsave(&dev->lock, flags); if (GETB(dev, PWR_REG, 0))//如果使能挂起模式 { SETB(dev, PWR_REG, 2); } spin_unlock_irqrestore(&dev->lock, flags); return 0; } #endif #ifdef S3C2440_SELFPOWERED //设置自供电标志(selfpowered feature),一般就用一个变量位或一个位记录一下。USB_RECIP_DEVICE时返回状态 static int s3c2440_udc_set_selfpowered (struct usb_gadget *usb_gdt_p, int is_selfpowered) { struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p); printInfo( "%s\n", __func__); if (is_selfpowered) dev->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else dev->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); return 0; } #endif #ifdef S3C2440_VBUS_SESSION //vbus在硬件上就是usb的电源脚,这个函数就是来控制它。一般通过一个gpio拉高拉底 //这个vbus会话,实际的我看了s3c2410和at91的处理,就是让usb的D+线与一个gpio口连接 //通过置1置0来控制usb static int s3c2440_udc_vbus_session (struct usb_gadget *usb_gdt_p, int is_active) { struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p); unsigned long flags; printInfo( "%s\n", __func__); spin_lock_irqsave(&dev->lock, flags); //寄存器操作 spin_unlock_irqrestore(&dev->lock, flags); return 0; } #endif #ifdef S3C2440_VBBUS_DRAW //强制vbus电源控制器行为,在SET_CONFIGRATION时,设置vbus的电流量 //vbus应该是表示总线电压,在硬件上是一个脚 //主要是对usb电流的设置,看一下gta02平台,这个函数会操作pcf50633(一种移动设备的电源管理芯片) static int s3c2440_udc_vbus_draw (struct usb_gadget *usb_gdt_p, unsigned mA) { return 0; } #endif #ifdef S3C2440X_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 s3c2440_udc_pullup (struct usb_gadget *usb_gdt_p, int is_on) { struct s3c2440_udc *dev = to_s3c2440_udc(usb_gdt_p); unsigned long flags; printInfo( "%s\n", __func__); spin_lock_irqsave(&dev->lock, flags); if (is_on) { PULL_UP(); } else { PULL_DOWN(); } spin_unlock_irqrestore(&dev->lock, flags); return 0; } #endif //不好意思,我看了linux-3.2.36的/gadget的目录没发现有实现这个的硬件 static int s3c2440_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 s3c2440_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 S3C2440_NEWSTYLE static int s3c2440_udc_start(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver); static int s3c2440_udc_stop(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver); #else //s3c2410 s3c2440 实现它 static int s3c2440_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *)); static int s3c2440_stop(struct usb_gadget_driver *driver); #endif static const struct usb_gadget_ops s3c2440_ops = { .get_frame = s3c2440_udc_get_frame, #ifdef S3C2440_WAKEUP .wakeup = s3c2440_udc_wakeup, #endif #ifdef S3C2440_SELFPOWERED .set_selfpowered = s3c2440_udc_set_selfpowered, #endif #ifdef S3C2440_VBUS_SESSION .vbus_session = s3c2440_udc_vbus_session, #endif #ifdef S3C2440_VBBUS_DRAW .vbus_draw = s3c2440_udc_vbus_draw, #endif #ifdef S3C2440X_PULLUP .pullup = s3c2440_udc_pullup, #endif .ioctl = s3c2440_udc_ioctl, .get_config_params = s3c2440_udc_get_config_params, #ifdef S3C2440_NEWSTYLE .udc_start = s3c2440_udc_start, .udc_stop = s3c2440_udc_stop, #else .start = s3c2440_start, .stop = s3c2440_stop, #endif }; /***************************************************************/ /***************************************************************/ static struct s3c2440_udc udc_info = { .gadget = { .ops = &s3c2440_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 = &s3c2440_ep_ops, .maxpacket = EP0_FIFO_SIZE, }, .dev = &udc_info, .fifo_size = S3C2440_EP0_FIFO_SIZE, }, /* first group of endpoints */ .ep[1] = { .num = 1, .ep = { .name = "ep1-bulk", .ops = &s3c2440_ep_ops, .maxpacket = EP1_FIFO_SIZE, }, .dev = &udc_info, .fifo_size = S3C2440_EP1_FIFO_SIZE, .bEndpointAddress = EP1_ADDRESS, .bmAttributes = EP1_ATTR, }, .ep[2] = { .num = 2, .ep = { .name = "ep2-bulk", .ops = &s3c2440_ep_ops, .maxpacket = EP2_FIFO_SIZE, }, .dev = &udc_info, .fifo_size = S3C2440_EP2_FIFO_SIZE, .bEndpointAddress = EP2_ADDRESS, .bmAttributes = EP2_ATTR, }, .ep[3] = { .num = 3, .ep = { .name = "ep3-bulk", .ops = &s3c2440_ep_ops, .maxpacket = EP3_FIFO_SIZE, }, .dev = &udc_info, .fifo_size = S3C2440_EP3_FIFO_SIZE, .bEndpointAddress = EP3_ADDRESS, .bmAttributes = EP3_ATTR, }, .ep[4] = { .num = 4, .ep = { .name = "ep4-bulk", .ops = &s3c2440_ep_ops, .maxpacket = EP4_FIFO_SIZE, }, .dev = &udc_info, .fifo_size = S3C2440_EP4_FIFO_SIZE, .bEndpointAddress = EP4_ADDRESS, .bmAttributes = EP4_ATTR, }, }; static void stop_activity(struct s3c2440_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 */ s3c2440_usb_reset(dev);//复位或disable for (i = 0; i < S3C2440_ENDPOINTS; i++) { s3c2440_dequeue_all(&dev->ep[i], -ECONNABORTED); } #ifndef S3C2440_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) { s3c2440_usb_reinit(dev);//重初始化 } } #ifdef S3C2440_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 s3c2440_udc_start(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver) { struct s3c2440_udc *dev; printInfo( "%s\n", __func__); if (!driver || !driver->unbind || !driver->setup || driver->speed != USB_SPEED_HIGH) return -EINVAL; dev = container_of(usb_gdt_p, struct s3c2440_udc, gadget); /* hook up the driver ... */ driver->driver.bus = NULL; dev->driver = driver; dev->gadget.dev.driver = &driver->driver; s3c2440_udc_enable(dev); return 0; } static int s3c2440_udc_stop(struct usb_gadget *usb_gdt_p, struct usb_gadget_driver *driver) { struct s3c2440_udc *dev; unsigned long flags; printInfo( "%s\n", __func__); dev = container_of(usb_gdt_p, struct s3c2440_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 s3c2440 实现它 //下面参考s3c2440 static int s3c2440_start(struct usb_gadget_driver *driver, int (*bind)(struct usb_gadget *usb_gdt_p)) { struct s3c2440_udc *dev = &udc_info; int retval = 0; printInfo( "%s\n", __func__); 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; } s3c2440_udc_enable(dev); return 0; register_error: dev->driver = NULL; dev->gadget.dev.driver = NULL; return retval; } static int s3c2440_stop(struct usb_gadget_driver *driver) { struct s3c2440_udc *dev = &udc_info; unsigned long flags; printInfo( "%s\n", __func__); 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 s3c2440_udc_probe(struct platform_device *pdev) { struct s3c2440_udc *udc = &udc_info; struct device *dev = &pdev->dev; int retval; struct resource *res; #ifdef S3C2440_USE_IRQ struct resource *resirq; #endif resource_size_t res_size; dev_dbg(dev, "%s()\n", __func__); #ifdef S3C2440_HAVE_CLK udc->s3c2440_clk_upll = clk_get(NULL, "usb-bus-gadget"); if (IS_ERR(udc->s3c2440_clk_upll)) { dev_err(dev, "failed to get usb bus clock source\n"); return PTR_ERR(udc->s3c2440_clk_upll); } clk_enable(udc->s3c2440_clk_upll); udc->s3c2440_clk_udc = clk_get(NULL, "usb-device"); if (IS_ERR(udc->s3c2440_clk_udc)) { dev_err(dev, "failed to get udc clock source\n"); retval = PTR_ERR(udc->s3c2440_clk_udc); goto err_clk_upll; } clk_enable(udc->s3c2440_clk_udc); #if (CLK_DELAY_TIME != 0) mdelay(CLK_DELAY_TIME); #endif dev_dbg(dev, "got and enabled clocks\n"); #endif //S3C2440_HAVE_CLK 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_udc; } 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; } 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); //少不了硬件初始化 s3c2440_usb_reset(udc); s3c2440_usb_reinit(udc); #ifdef S3C2440_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; retval = request_irq(udc->irq_num, s3c2440_udc_irq, 0, gadget_name, (void*)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 S3C2440_DEBUG_FS if (s3c2440_udc_debugfs_root) { udc->debug_info = debugfs_create_file("registers", S_IRUGO, s3c2440_udc_debugfs_root, udc, &s3c2440_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 S3C2440_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_udc: #ifdef S3C2440_HAVE_CLK clk_put(udc->s3c2440_clk_udc); clk_disable(udc->s3c2440_clk_udc); #endif err_clk_upll: #ifdef S3C2440_HAVE_CLK clk_put(udc->s3c2440_clk_upll); clk_disable(udc->s3c2440_clk_upll); #endif return retval; } static int s3c2440_udc_remove(struct platform_device *pdev) { struct s3c2440_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 S3C2440_DEBUG_FS debugfs_remove(udc->debug_info); #endif #ifdef S3C2440_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 S3C2440_HAVE_CLK if (!IS_ERR(udc->s3c2440_clk_udc) && udc->s3c2440_clk_udc != NULL) { clk_disable(udc->s3c2440_clk_udc); clk_put(udc->s3c2440_clk_udc); udc->s3c2440_clk_udc = NULL; } if (!IS_ERR(udc->s3c2440_clk_upll) && udc->s3c2440_clk_upll != NULL) { clk_disable(udc->s3c2440_clk_upll); clk_put(udc->s3c2440_clk_upll); udc->s3c2440_clk_upll = NULL; } #endif dev_dbg(&pdev->dev, "%s: remove ok\n", __func__); return 0; } #ifdef CONFIG_PM static int s3c2440_udc_suspend(struct platform_device *pdev, pm_message_t message) { return 0; } static int s3c2440_udc_resume(struct platform_device *pdev) { return 0; } #else #define s3c2440_udc_suspend NULL #define s3c2440_udc_resume NULL #endif /***************************************************************/ //有些设备可能用struct pci_driver,我就不考虑这么多了。 static struct platform_driver udc_driver_s3c2440 = { .driver = { .name = "s3c2440-usbgadget", .owner = THIS_MODULE, }, .probe = s3c2440_udc_probe, .remove = __exit_p(s3c2440_udc_remove), .suspend = s3c2440_udc_suspend, .resume = s3c2440_udc_resume, }; static int __init udc_init(void) { int retval; s3c2440_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); if (IS_ERR(s3c2440_udc_debugfs_root)) { printInfo( "%s: debugfs dir creation failed %ld\n", gadget_name, PTR_ERR(s3c2440_udc_debugfs_root)); s3c2440_udc_debugfs_root = NULL; } retval = platform_driver_register(&udc_driver_s3c2440); if (retval) goto err; return 0; err: debugfs_remove(s3c2440_udc_debugfs_root); return retval; } static void __exit udc_exit(void) { platform_driver_unregister(&udc_driver_s3c2440); debugfs_remove(s3c2440_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");
-
自娱自乐7之Linux UDC驱动2(自编udc驱动,现完成枚举过程,从驱动代码分析枚举过程)
2013-09-13 17:51:28在《自娱自乐1》中的模板,比较一下,我做了什么,这会给你写udc驱动提供个思路。我直接分析 调试打印,就是枚举过程,我们从代码看枚举。打印位置可以在下面的代码里找到。 如果你要弄懂驱动代码中涉及枚举的地方... -
usb udc 驱动
2022-03-28 16:59:221.1 gadegt 初始化 // dwc3 == dwc3_gadget_init(struct dwc3 *dwc); == usb_add_gadget(dwc->gadget); == device_add(&gadget->dev);... == list_add_tail(&...== dummy_udc_probe(st -
USB UDC驱动 gadget驱动
2019-07-13 16:35:36例如,当某运行Linux系统的手机作为PC的U盘时,手机中的底层USB控制器行使USB设备控制器的功能,这时运行在底层的是UDC驱动,手机要成为U盘,在UDC驱动之上需要另外一个驱动,对于USB大容量存储... -
Linux USB驱动-鼠标驱动
2021-03-18 21:28:36UDC驱动程序直接访问硬件,控制USB设备控制器与USB主机控制器通信,向上提供与硬件交互的接口。Gadget Function API封装了UDC驱动程序向上提供的接口。Gadget Function驱动程序实现具体的USB设备功能。Linux内核支持... -
USB总线-Linux内核USB3.0设备控制器驱动框架分析(四)
2021-09-16 22:21:47USB控制器作为Device时,称为USB设备控制器,使用UDC(usb device controller)驱动。本节只分析USB控制器作为Device时的驱动框架。 USB控制器作为Device时,驱动框架可分为5层。最上层的是Gadget Function驱动,... -
Linux驱动进阶学习--USB Gadget驱动分析(三)实例源码分析
2021-01-27 20:26:232、UDC 设备控制器层 一、引言 前一章大致分析了USB Gadget驱动的编写流程,这章就来结合UVC驱动,详细分析一下整个框架 二、UVC USB Gadge驱动分析 1、驱动入口 DECLARE_USB_FUNCTION_INIT(uvc, uvc_alloc_inst, ... -
Linux usb 6. HC/UDC 测试
2021-11-11 15:44:30在 Linux Kernel 中对 HC/UDC 有一套专有的测试方案,在底层对 control/bulk/int/iso 几种 endpoint 进行针对性的功能和压力测试。 上图的测试方案由几部分组成: 1、Device 侧的 gadget zero 测试设备,提供了测试... -
Linux驱动开发之USB驱动深入学习(一)——USB驱动架构
2021-03-30 12:14:55从实习,跳槽,再到试用、转正,也做过一些项目,算是在Linux驱动开发这一行入了门。 一些简单的外设自己也有能力编写了,但一些复杂的驱动还是需要原厂支持。每到这个时候,就感觉自己特别的废物,就会想到,... -
Linux USB驱动架构
2021-07-07 16:19:57USB驱动分为主机侧和从机侧:主机侧由设备驱动、USB核心、USB主机控制器驱动组成,从机侧由:Gadget Function驱动、Gadget Function API、UDC驱动组成,此外OTG协议既能当主机也能当从机: 主机侧:USB核心将USB... -
Linux2.6环境下USB设备的驱动实现
2021-05-14 13:16:41Linux的USB设备端的源代码中主要有USBdevice的海量存储设备、串口设备、网络设备等设备驱动程序及各种USB device控制器芯片的驱动程序。市场上USB设备控制器芯片种类繁多,大多数用户需要针对特定应用来开发相关的... -
linux gadget 驱动
2021-05-15 07:44:46介绍USB系统框架,只关注框架部分,不涉及细节这里的USB设备控制器(UDC)驱动指作为其他usb主机控制器外设的usb硬件设备上底层硬件控制器的驱动,该硬件和驱动负责将一个usb设备依附于一个usb主机控制器上。... -
Linux USB驱动工作流程
2021-05-12 17:28:17Linux USB驱动工作流程2012-06-25 11:00:35 我来说两句Linux USB驱动工作流程1. USB主机在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控制器驱动,主机控制器之上为USB核心层,... -
Linux设备驱动入门
2021-05-15 08:09:00/*****************************************USB主机与设备驱动*主机侧:由底到高:USB主机控制器硬件--&...USB*设备驱动层*设备侧:UDC驱动程序、GadgetAPI和Gadget*驱动程序*逻辑组织:设备(1)配置(n)*配置(1)接口... -
[Linux设备驱动开发详解(第2版)].宋宝华.pdf
2021-05-14 23:04:42版权归原作者所有 本资料仅供试读攻城狮论坛(技术+生活)群 22580971 2008 1.61!... input USB UDC gadget SPI ASoC1 2 40%Linux 2 11VirtualBox/21cnbao2 S3C6410 SoC LDD6410 Linux Device Drivers64103 Linux ... -
Linux系统中USB驱动程序的工作流程详解
2021-05-10 21:24:241.USB主机在Linux驱动中,USB驱动处于最底层的是USB主机控制器硬件,在其之上运行的是USB主机控制器驱动,主机控制器之上为USB核心层,再上层为USB设备驱动层(插入主机上的U盘、鼠标、USB转串口等设备驱动)。... -
linux下usb 驱动
2021-06-02 11:18:54今天我们来简单地把USB在Linux里的结构框架大致整理下,其中重点解析下USB Core和Hub。 0. 预备理论 说实话,读USB协议还是蛮痛苦的,它仅仅是一个协议,一个在USB世界里制定的游戏规则,就像法律条文一样,它并不...