首先,我不是做驱动的开发人员。所以只能用自娱自乐来表示我的行为。
我不知道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驱动。