自娱自乐之Linux UDC驱动
![](http://static.blog.csdn.net/images/category_icon.jpg)
版权声明:本文为博主原创文章,未经博主允许不得转载。
首先,我不是做驱动的开发人员。所以只能用自娱自乐来表示我的行为。
我不知道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驱动。事实我已经做了,等待测试。当我要测试时,我发现还是要用gadget驱动去调试udc驱动。虽然有现成的,但是你如果搞不懂的话,出问题你根本不知道在哪。所以调试udc驱动之前我们要看gadget驱动。
我看了linux-2.6.10没有composite的东西。还是直接操作struct usb_gadget_driver。那已经是过去式了。现在我用的linux-3.2.36是composite。首先我们就看composite有哪些东西,接着通过实例分析怎么用。
struct usb_function {
const char *name;//
struct usb_gadget_strings **strings;//字符串描述附
//这个struct usb_descriptor_header包涵了接口描述符和端点描述符
//在zero中
/*
static structusb_descriptor_header *fs_source_sink_descs[] = {
(structusb_descriptor_header *) &source_sink_intf,
(structusb_descriptor_header *) &fs_sink_desc,
(structusb_descriptor_header *) &fs_source_desc,
NULL,
};
*/
struct usb_descriptor_header **descriptors;//全速
struct usb_descriptor_header **hs_descriptors;//高速
struct usb_descriptor_header **ss_descriptors;//超速
struct usb_configuration *config;//下面看到这个,usb_add_function会调用
//绑定可用的源,我看sourecesink就是gedget->ep_list获取可用的端点、并获取端//点的wMaxPacketSize、bEndpointAddress。还有高速和超速描述符赋值
int (*bind)(structusb_configuration *,
structusb_function *);
void (*unbind)(structusb_configuration *,
structusb_function *);
//配置接口,sourecesink就是通过gadget的速度来选择对应的structusb_desctiptor_header
//就是上面的三个
//set_alt:重配置altsettings
int (*set_alt)(structusb_function *,
unsignedinterface, unsigned alt);
//get_alt:返回当前活动 altsettings
int (*get_alt)(structusb_function *,
unsignedinterface);
//表示这个function应该disable,原因可能是主机复位、重配置、断开
void (*disable)(structusb_function *);
//setup用于接口特性控制请求
int (*setup)(structusb_function *,
const structusb_ctrlrequest *);
//suspend和resume不用解释吧
void (*suspend)(structusb_function *);
void (*resume)(structusb_function *);
//对应主机的GETSTATUS请求
int (*get_status)(structusb_function *);
//接受到SETFEATURE的USB_INTRF_FUNC_SUSPEND时的回调函数
int (*func_suspend)(structusb_function *,
u8suspend_opt);
struct list_head list;//下面
DECLARE_BITMAP(endpoints, 32);
};
//把function加入到config中
//就是这句list_add_tail(&function->list,&config->functions);
//然后执行bind(如果有的话),速度标志位赋值
int usb_add_function(struct usb_configuration *config, struct usb_function *function);
//这俩个会调用udc中的pull_up,可以多次调用。
//如果deactivate调用三次,那么avtivate要调用三次才能把D+拉高
int usb_function_deactivate(struct usb_function *);
int usb_function_activate(struct usb_function *);
/*
int usb_interface_id(structusb_configuration *config,
struct usb_function *function)
{
unsigned id =config->next_interface_id;
if (id < MAX_CONFIG_INTERFACES) {
config->interface[id] =function;
config->next_interface_id =id + 1;
return id;
}
return -ENODEV;
}
简单,不解释
*/
intusb_interface_id(struct usb_configuration *, struct usb_function *);
/*
这就是上面的通过gadget的速度来选择对应的structusb_desctiptor_header
的函数
*/
int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
struct usb_ep *_ep);
//usb最多255个接口,linux定义是16个
#define MAX_CONFIG_INTERFACES 16 /* arbitrary;max 255 */
//相当于配置
struct usb_configuration {
const char *label;//相当于name
struct usb_gadget_strings **strings;//字符串描述符
const struct usb_descriptor_header**descriptors;//先前的所有function描述表
//和bind相反 ,bind在哪,下面看到
void (*unbind)(structusb_configuration *);
//代表非标准控制请求,sourecesink实现的就是它
int (*setup)(structusb_configuration *,
const structusb_ctrlrequest *);
//配置描述符的东西,spec说的很详细
u8 bConfigurationValue;
u8 iConfiguration;
u8 bmAttributes;
u8 bMaxPower;
//对应设备
struct usb_composite_dev *cdev;
struct list_head list; //和上面的结构体的list一样
struct list_head functions; //上面已看到使用
u8 next_interface_id; //下一个接口id,上面的usb_interface_id有用
// usb_add_function会设置它们
unsigned superspeed:1;
unsigned highspeed:1;
unsigned fullspeed:1;
struct usb_function *interface[MAX_CONFIG_INTERFACES];
};
//和上面的usb_add_function差不多,bind对应上面的unbind
intusb_add_config(struct usb_composite_dev *cdev,
struct usb_configuration*config,
int (*bind)(structusb_configuration *))
//相当于usb_gadget_driver
struct usb_composite_driver {
const char *name;//
const char *iProduct;//产品
const char *iManufacturer;//厂商信息
const struct usb_device_descriptor *dev;//下面
struct usb_gadget_strings **strings;//字符串描述符
enum usb_device_speed max_speed;//最高速度
unsigned needs_serial:1;//是否需要iSerialNumber
//下面不解释
int (*unbind)(structusb_composite_dev *);
void (*disconnect)(structusb_composite_dev *);
/* global suspend hooks */
void (*suspend)(structusb_composite_dev *);
void (*resume)(structusb_composite_dev *);
};
//先根据driver给composite_driver赋值,再
//usb_gadget_probe_driver(&composite_driver, composite_bind);
/*
composite_driver是个全局的变量
static struct usb_gadget_driver composite_driver = {
#ifdefCONFIG_USB_GADGET_SUPERSPEED
.speed = USB_SPEED_SUPER,
#else
.speed = USB_SPEED_HIGH,
#endif
.unbind = composite_unbind,
.setup = composite_setup,
.disconnect = composite_disconnect,
.suspend = composite_suspend,
.resume = composite_resume,
.driver = {
.owner = THIS_MODULE,
},
};
composite就是通过它和gadget的接口联系的
*/
extern int usb_composite_probe(struct usb_composite_driver *driver,
int (*bind)(struct usb_composite_dev*cdev));
extern void usb_composite_unregister(struct usb_composite_driver *driver);
//继续控制传输
extern void usb_composite_setup_continue(struct usb_composite_dev*cdev);
//相当于设备
struct usb_composite_dev {
struct usb_gadget *gadget;//只读,抽象的表示gadget的usb外设控制器
struct usb_request *req;//用于控制答复
unsigned bufsiz;//req的预分配大小
struct usb_configuration *config;//当前活动的config
/* private: */
/* internals */
unsigned int suspended:1;
struct usb_device_descriptor desc;//设备描述符
struct list_head configs;//配置链表
struct usb_composite_driver *driver;//上面的
u8 next_string_id;
//下面表示对应的字符串的id
u8 manufacturer_override;
u8 product_override;
u8 serial_override;
/*deactivations是int usb_function_deactivate(struct usb_function *);
int usb_function_activate(struct usb_function *);计数用的
*/
unsigned deactivations;
/* the composite driver won't completethe control transfer's
*data/status stages till delayed_status is zero.//这段英文自己理解
*/
/*
若果设备
USB_REQ_SET_INTERFACE结果返回USB_GADGET_DELAYED_STATUS,计数加1
usb_composite_setup_continue计数减1,到0继续执行控制请求。
*/
int delayed_status;
/* protects deactivations anddelayed_status counts*/
spinlock_t lock;
};
/*
int usb_string_id(struct usb_composite_dev *cdev)
{
if (cdev->next_string_id < 254) {
/* string id 0 is reserved byUSB spec for list of
* supported languages */
/* 255 reserved as well? --mina86 */
cdev->next_string_id++;
return cdev->next_string_id;
}
return -ENODEV;
}
简单
看zero
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_MANUFACTURER_IDX].id =id;
device_desc.iManufacturer = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_PRODUCT_IDX].id = id;
device_desc.iProduct = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_SERIAL_IDX].id = id;
device_desc.iSerialNumber = id;
就是给上面的字符串分配id
*/
extern int usb_string_id(struct usb_composite_dev *c);
//下面两个是批量分配
extern intusb_string_ids_tab(struct usb_composite_dev *c,
struct usb_string *str);
extern intusb_string_ids_n(struct usb_composite_dev *c, unsigned n);
这几天在群里忙着和别人讨论问题,一直没在弄了。今天简单看了一下zero.c
比较让我惊奇的是,有个
/* If the optional "autoresume" mode isenabled, it provides good
* functionalcoverage for the "USBCV" test harness from USB-IF.
*/
unsigned autoresume = DEFAULT_AUTORESUME;
module_param(autoresume, uint, S_IRUGO);
MODULE_PARM_DESC(autoresume, "gadget_transfer, or seconds before remotewakeup");
从英文看是为USB-IF测试软件USBCV(可以下载到)做的,这就不管了。
我之前看过hub有个自动挂起的东西。它的主要就是一定时间(默认2s)不用就自动suspend。
这里先假设是2秒吧
在f_sourcesink.c sourcesink_add
/* support autoresume for remote wakeup testing */
if (autoresume)
sourcesink_driver.bmAttributes|= USB_CONFIG_ATT_WAKEUP;
/* support OTG systems */
if (gadget_is_otg(cdev->gadget)) {
sourcesink_driver.descriptors =otg_desc;
sourcesink_driver.bmAttributes|= USB_CONFIG_ATT_WAKEUP;
}
下面的otg是几乎每个gadget驱动都这样写。我们不管otg
上面说的很清楚支持autoresume为了远程唤醒功能测试
static void zero_autoresume(unsignedlong _c)
{
struct usb_composite_dev *cdev = (void *)_c;
struct usb_gadget *g= cdev->gadget;
/* unconfigured devices can't issue wakeups */
if (!cdev->config)
return;
/* Normally the host would be woken up for something
* more significant than just a timer firing; likely
* because of some direct user request.
*/
if (g->speed != USB_SPEED_UNKNOWN) {
int status =usb_gadget_wakeup(g);//这个会调用udc中的wakeup
INFO(cdev, "%s -->%d\n", __func__, status);
}
}
static void zero_suspend(structusb_composite_dev *cdev)
{
if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
return;
if (autoresume) {
mod_timer(&autoresume_timer, jiffies + (HZ * autoresume));
DBG(cdev, "suspend, wakeupin %d seconds\n", autoresume);
} else
DBG(cdev, "%s\n",__func__);
}
static void zero_resume(structusb_composite_dev *cdev)
{
DBG(cdev, "%s\n", __func__);
del_timer(&autoresume_timer);
}
这个zero_suspend和zero_resume一般都在udc的中断处理中调用
有autoresume也就是定时到时自动wakeup。
没这个的话,这两个函数就等于什么都没干。
主机端set feature会有个叫remote_wake的值是0x0001,与配置描述符对应的是
sourcesink_driver.bmAttributes|= USB_CONFIG_ATT_WAKEUP,前面是主机的请求,后面是表示设备有这个能力。
我看了net2272有个IRQSTAT1寄存器,如果有请求改变,再判断是否是suspend请求
是调用dev->driver->suspend不是调用dev->driver_resume。这两个不对应上面的。
是composite_suspend和composite_resume。在它们里面调用上面的。
在看s3c2440的,它的suspend比较变态,每当总线无活动多于 3ms 将置位此位(指的是suspend中断标志位)。因此,如果 MCU 不在第一个挂起中断后停止时钟,它将会持续每 3ms 中断一次,直到 USB 总线无活动。默认是禁止此中断。
我们看第一句话,可以看出s3c2440的suspend中断和主机无关,真的叫自动化。
那个zero的resume大概就是已经唤醒了就不要在执行后面的自动化了,所以del timer。
把这个suspend和resume提出来是因为我要写的东西不会包涵它们。
我这几天写文章就像挤牙膏啊,再说一点。gadget_chips.h
这个里面有些东西要定义一下,
static inline int usb_gadget_controller_number(struct usb_gadget *gadget);
获取控制器号
static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
是否支持altsettings,你会看到它里面放的是不支持的,可以想到一般的控制器是支持altsettings的。
说一下第一个
我们如果要写个自己的udc,我的是s3c2440,大家知道s3c2440用的是s3c2410。现在让它独立
#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
#define gadget_is_s3c2440(g) (!strcmp("s3c2440_udc", (g)->name))
首先定义这个,"s3c2440_udc"要和它的udc驱动一样的。
在usb_gadget_controller_number()里面加上
else if (gadget_is_s3c2410(gadget))
return 0x12;
else if (gadget_is_s3c2440(gadget))
return 0x12;
其实我还是用了一样的数。
看zero怎么用这个
gcnum = usb_gadget_controller_number(gadget);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
else {
/* gadget gadget_transfer is so simple (for now, no altsettings) that
* it SHOULD NOT have problems with bulk-capable hardware.
* so just warn about unrcognized controllers -- don't panic.
*
* things like configuration and altsetting numbering
* can need hardware-specific attention though.
*/
pr_warning("%s: controller '%s' not recognized\n",
longname, gadget->name);
device_desc.bcdDevice = cpu_to_le16(0x9999);
}
就获得之后转为小端做为设备bcd。就是设备描述符里的。没获得就是0x9999
今天说的是字符串描述符,struct usb_gadget_strings我之前说是字符串描述符并不准确,
真正的描述符是struct usb_string_descriptor
上一篇说的结构体中有很多都包涵了struct usb_gadget_strings,它们是struct usb_function、struct usb_configuration、struct usb_composite_driver三世同堂
struct usb_composite_drive包涵struct usb_configuration包涵struct usb_function
而且zero.c f_souresink.c f_loopback.c(这个我不用就不看了)composite.c都有
string usb_string_descripter结构体,这篇文章就了解一下他们的关系
先看一下这几个结构体
/* USB_DT_STRING: String descriptor */
struct usb_string_descriptor {
__u8 bLength;//描述符长度
__u8 bDescriptorType;//就是USB_DT_STRING 0x03
__le16 wData[1]; /* UTF-16LE encoded */
} __attribute__ ((packed));
//这里可以看出usb_gadget_strings包涵struct usb_string
//实例中的定义会看到
struct usb_gadget_strings {
u16 language; /* 0x0409 for en-us */
struct usb_string *strings;
};
struct usb_string {
u8 id;
const char *s;
};
不过在驱动中我们大部分看到的是struct usb_string
先看zero.c
//首先定义
static const char longname[] = "Gadget gadget_transfer";
static char manufacturer[50];
/* default serial number takes at least two packets */
static char serial[] = "0123456789.0123456789.0123456789";
static struct usb_string strings_dev[] = {
[STRING_MANUFACTURER_IDX].s = manufacturer,
[STRING_PRODUCT_IDX].s = longname,
[STRING_SERIAL_IDX].s = serial,
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,//包涵struct usb_string
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
//没什么要解释的吧
//在gadget_transfer_bind()中
//各字符串描述符的引索
id = usb_string_id(cdev);//这个东西之前有说过,就是cdev->next_string_id++返回,怕id冲突
if (id < 0)
return id;
strings_dev[STRING_MANUFACTURER_IDX].id = id;
device_desc.iManufacturer = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_PRODUCT_IDX].id = id;
device_desc.iProduct = id;
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_dev[STRING_SERIAL_IDX].id = id;
device_desc.iSerialNumber = id;
//manufacturer[50]下面赋值
//本来下面这句就是给manufacturer赋值,可是非要整个init_utsname()
//linux gadget的驱动都有这个,我们看一下吧
/*
.name = {
.sysname = UTS_SYSNAME,
.nodename = UTS_NODENAME,
.release = UTS_RELEASE,
.version = UTS_VERSION,
.machine = UTS_MACHINE,
.domainname = UTS_DOMAINNAME,
},
#ifndef UTS_SYSNAME//也有定义"uClinux"等
#define UTS_SYSNAME "Linux"
#endif
#define UTS_RELEASE "3.2.0"//我的版本
*/
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
//还有个静态结构
static struct usb_composite_driver gadget_transfer_driver = {
.name = "gadget_transfer",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
.unbind = gadget_transfer_unbind,
};//我们找到了爷爷usb_composite_driver
//f_sourecesink.c 我只用sink
static struct usb_string strings_sourcesink[] = {
[0].s = "source and sink data",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_sourcesink = {
.language = 0x0409, /* en-us */
.strings = strings_sourcesink,
};
static struct usb_gadget_strings *sourcesink_strings[] = {
&stringtab_sourcesink,
NULL,
};
static struct usb_configuration sourcesink_driver = {
.label = "source/sink",
.strings = sourcesink_strings,
.setup = sourcesink_setup,
.bConfigurationValue = 3,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};//我们找到了爸爸struct usb_configuration
probe时会加入cdev中
usb_add_config(cdev, &sourcesink_driver, sourcesink_bind_config);
list_add_tail(&config->list, &cdev->configs);//下面会见到查找列表
还差儿子struct usb_function
ss->function.name = "source/sink";
ss->function.descriptors = fs_source_sink_descs;
ss->function.bind = sourcesink_bind;
ss->function.unbind = sourcesink_unbind;
ss->function.set_alt = sourcesink_set_alt;
ss->function.disable = sourcesink_disable;
status = usb_add_function(c, &ss->function);
很可惜这是个不争气的孩子,没有给string赋值
//composite.c
struct usb_composite_dev中的这几位,下面会赋值
u8 manufacturer_override;
u8 product_override;
u8 serial_override;
static char *iManufacturer;
module_param(iManufacturer, charp, 0);
MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
static char *iProduct;
module_param(iProduct, charp, 0);
MODULE_PARM_DESC(iProduct, "USB Product string");
static char *iSerialNumber;
module_param(iSerialNumber, charp, 0);
MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
//上面的可以装载时赋值
static char composite_manufacturer[50];
在static int composite_bind(struct usb_gadget *gadget)中
//有个全局的tatic struct usb_composite_driver *composite;
//在int usb_composite_probe(struct usb_composite_driver *driver,
// int (*bind)(struct usb_composite_dev *cdev))中
//composite =driver,对我们来说就是zero中的struct usb_composite_driver
cdev->desc = *composite->dev;
/*
cdev->desc对我们来说就是zero中的
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_VENDOR_SPEC,
.idVendor = cpu_to_le16(DRIVER_VENDOR_NUM),//我们有赋值
.idProduct = cpu_to_le16(DRIVER_PRODUCT_NUM),
.bNumConfigurations = 2,
};
*/
/* string overrides */
if (iManufacturer || !cdev->desc.iManufacturer) {//只要iMonufacturer指定就会用
/*
对于struct usb_composite_driver它里面也有
const char *iProduct;//产品
const char *iManufacturer;//厂商信息
zero并没有指定,下面会看到composite->iManufacturer和
composite->iProduct判断
*/
if (!iManufacturer && !composite->iManufacturer &&
!*composite_manufacturer)//如果这几个都没有指定
snprintf(composite_manufacturer,
sizeof composite_manufacturer,
"%s %s with %s",
init_utsname()->sysname,
init_utsname()->release,
gadget->name);//这个和zero中的一样
/*
static u8 override_id(struct usb_composite_dev *cdev, u8 *desc)
{
if (!*desc) {//如果不是0,就是已分配过id
int ret = usb_string_id(cdev);//这个已说过
if (unlikely(ret < 0))
WARNING(cdev, "failed to override string ID\n");//出错最后返回就是0
else
*desc = ret;
}
return *desc;
}
*/
cdev->manufacturer_override =
override_id(cdev, &cdev->desc.iManufacturer);
}
//下面逻辑和上面差不多,不赘述了
if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
cdev->product_override =
override_id(cdev, &cdev->desc.iProduct);
if (iSerialNumber)
cdev->serial_override =
override_id(cdev, &cdev->desc.iSerialNumber);
上面这一大堆就是初始化赋值,下面看usb请求,请求我不细说了,就是主机发获得字符串描述符请求时
调用int get_string(struct usb_composite_dev *cdev, void *buf, u16 language, int id)
static int get_string(struct usb_composite_dev *cdev,
void *buf, u16 language, int id)
{
struct usb_configuration *c;
struct usb_function *f;
int len;
const char *str;
/* Yes, not only is USB's I18N support probably more than most
* folk will ever care about ... also, it's all supported here.
* (Except for UTF8 support for Unicode's "Astral Planes".)
*/
/* 0 == report all available language codes *///这里写的很清楚,0返回所有支持语言codes
if (id == 0) {
struct usb_string_descriptor *s = buf;//字符串描述符,这个buf就是usb_request中的buf,我们就就是要给它赋值
struct usb_gadget_strings **sp;
memset(s, 0, 256);
s->bDescriptorType = USB_DT_STRING;//开始就说了
//下面直到list_for_each_entry结束,我们看到了上面说的包涵关系
/*
唯一要说的就是void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
不贴代码了,就是获得struct usb_gadget_strings的language id并与buf中比较
如果没有相同的就记录下来,里面有个126限制下面也有,应该是指buf的最大长度
*/
sp = composite->strings;//就是zero中的dev_strings
if (sp)
collect_langs(sp, s->wData);
list_for_each_entry(c, &cdev->configs, list) {
sp = c->strings;
if (sp)
collect_langs(sp, s->wData);
list_for_each_entry(f, &c->functions, list) {
sp = f->strings;
if (sp)
collect_langs(sp, s->wData);
}
}
for (len = 0; len <= 126 && s->wData[len]; len++)
continue;
if (!len)
return -EINVAL;
s->bLength = 2 * (len + 1);//这个其实就是2 * len + 2,前面*2是应为wData是le16就是16位,+2是bLength和bDescriptorType
return s->bLength;
}
/* Otherwise, look up and return a specified string. First
* check if the string has not been overridden.
*/
if (cdev->manufacturer_override == id)
str = iManufacturer ?: composite->iManufacturer ?:
composite_manufacturer;
else if (cdev->product_override == id)
str = iProduct ?: composite->iProduct;
else if (cdev->serial_override == id)
str = iSerialNumber;
else
str = NULL;
if (str) {
struct usb_gadget_strings strings = {
.language = language,
.strings = &(struct usb_string) { 0xff, str }
};
return usb_gadget_get_string(&strings, 0xff, buf);
//usb_gadget_get_string
//linux解释fill out a string descriptor,不用说了吧,不过你看一下代码会发现,它又判断了id == 0,在这有点多余
}
//上面一段就是先用转载是指定的strings,下面不看也知道,肯定是查三世同堂的strings
/* String IDs are device-scoped, so we look up each string
* table we're told about. These lookups are infrequent;
* simpler-is-better here.
*/
//和上面统计语言ID逻辑一样,就是用了lookup_string
/*
static int lookup_string(
struct usb_gadget_strings **sp,
void *buf,
u16 language,
int id
)
也会调用usb_gadget_get_string,不过调用之前会判断一下
是否和指定language相同
*/
if (composite->strings) {
len = lookup_string(composite->strings, buf, language, id);
if (len > 0)
return len;
}
list_for_each_entry(c, &cdev->configs, list) {
if (c->strings) {
len = lookup_string(c->strings, buf, language, id);
if (len > 0)
return len;
}
list_for_each_entry(f, &c->functions, list) {
if (!f->strings)
continue;
len = lookup_string(f->strings, buf, language, id);
if (len > 0)
return len;
}
}
return -EINVAL;
}
bind和字符串描述符一样也是有多级关系,首先说一下usb很基础的关系
一个设备可能有多个配置
一个配置可能有多个接口
一个接口可能有多个端点或设置
还记得我在自娱自乐2中说的的结构体吧
我们先变通一下
struct usb_function 相当于接口
struct usb_configuration 相当于配置
struct usb_composite_dev 相当于设备
bind会建立他们的关系,当然bind不止是这层涵义
我们在看它们的bind之前先看看
int usb_composite_probe(struct usb_composite_driver *driver,
int(*bind)(struct usb_composite_dev *cdev))
这玩意也有个绑定
在zero中usb_composite_probe(&gadget_transfer_driver,gadget_transfer_bind);
gadget_transfer_bind()这个函数我在之前已经列出了它的一部分,只是没明说
主要就是分配厂商设备id,获取设备bcd码,生成厂商字符串。定时器初始化话。
还有重要一点就是调用sourcesink_add(loop我们不看)
int usb_composite_probe(structusb_composite_driver *driver,
int(*bind)(struct usb_composite_dev *cdev))
{
//一些判断赋值省去
composite = driver;
composite_gadget_bind = bind;//请记住zero提供的bind函数在此只是赋值
return usb_gadget_probe_driver(&composite_driver, composite_bind);
//composite_driver前面的文章已说过了,这个usb_gadget_probe_drvier不贴全代码了,看一点
/*
//这个udc是从udc_list中找到的
if (udc_is_newstyle(udc)) {//自娱自乐1有说udc_is_newstyle()
ret =bind(udc->gadget);//这个bind是composite_bind,不要搞错
if (ret)
goto err1;
//…
} else {
ret =usb_gadget_start(udc->gadget, driver, bind);//gadget->ops->start(driver, bind);不懂就看我之前写的udc模板
//…
}
不管怎么样都要调用composite_bind()函数,下面看到
*/
}
static int composite_bind(struct usb_gadget *gadget)//记住这个gadget是udc中的
{
structusb_composite_dev *cdev;
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
if(!cdev)
returnstatus;
spin_lock_init(&cdev->lock);//初始化lock
cdev->gadget = gadget;//保存对应udc中的gadget
set_gadget_data(gadget, cdev);//相当于gadget->dev->driver_data= cdev;
//上面两句就是把gadget于udc驱动联系起来
INIT_LIST_HEAD(&cdev->configs);//初始化配置链表头
cdev->req =usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);//会调用udc中对应方法
if(!cdev->req)
gotofail;
cdev->req->buf =kmalloc(USB_BUFSIZ, GFP_KERNEL);
if(!cdev->req->buf)
gotofail;
cdev->req->complete =composite_setup_complete;//这个是请求完成函数,暂且不管
gadget->ep0->driver_data =cdev;
cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;//还记得他是谁吧,就是zero中的gadget_transfer_driver
if(CONFIG_USB_GADGET_VBUS_DRAW/*内核配置*/ <= USB_SELF_POWER_VBUS_MAX_DRAW/*100*/)//电力不足
usb_gadget_set_selfpowered(gadget);//设为自供电
usb_ep_autoconfig_reset(cdev->gadget);
/*
void usb_ep_autoconfig_reset (structusb_gadget *gadget)
{
struct usb_ep *ep;
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
#ifdef MANY_ENDPOINTS
in_epnum = 0;
#endif
epnum = 0;
}
没什么要解释吧
*/
status =composite_gadget_bind(cdev);//还记的它把,就是zero中的bind函数gadget_transfer_bind,这里面当然会调用sourcesink_add和loopback_add,我们下面看
if(status < 0)
gotofail;
cdev->desc =*composite->dev;//zero中的device_desc
//这里有一段就是我们之前说的判断装载是是否定义字符串的代码,
//当然还有厂商、产品id等。直接略过
//如果看过我之前写的设备模型,应该对device_create_file不陌生吧
status =device_create_file(&gadget->dev, &dev_attr_suspended);
if(status)
gotofail;
return0;
fail:
composite_unbind(gadget);
returnstatus;
}
//上面就相当于设备的绑定,设备过后当然就是配置
if (loopdefault) {
loopback_add(cdev, autoresume !=0);
sourcesink_add(cdev, autoresume!= 0);
} else {
sourcesink_add(cdev, autoresume!= 0);
loopback_add(cdev, autoresume!= 0);
}
不管loopdefault是什么,我们可以认为zero有两个配置
我只用f_sourcesink.c
sourcesink_add主要就是
usb_add_config(cdev,&sourcesink_driver, sourcesink_bind_config);
//为了节约纸张,我去掉了一下注释和DBG()
int usb_add_config(struct usb_composite_dev*cdev,
structusb_configuration *config,
int(*bind)(struct usb_configuration *))
{
int status = -EINVAL;
structusb_configuration *c;
if(!config->bConfigurationValue || !bind)
gotodone;
list_for_each_entry(c,&cdev->configs, list) {//这段很好理解就是判断是否已经注册过了
if(c->bConfigurationValue == config->bConfigurationValue) {
status = -EBUSY;
goto done;
}
}
config->cdev = cdev;
list_add_tail(&config->list,&cdev->configs);//加入设备的配置链表
INIT_LIST_HEAD(&config->functions);//初始化配置中的接口链表头
config->next_interface_id =0;//接口id初始化为0
status = bind(config);// 就是sourcesink_bind_config,这个就是把接口加入配置,下面看
if(status < 0) {
list_del(&config->list);
config->cdev = NULL;
} else{
unsigned i;
for(i = 0; i < MAX_CONFIG_INTERFACES; i++) {
struct usb_function *f =config->interface[i];
if (!f)
continue;
}
}
usb_ep_autoconfig_reset(cdev->gadget);//上面说过,不过上面的gadget是udc中的,这个是zero驱动中的
done:
returnstatus;
}
//到这我们可以说配置绑完了,下面看接口
static int __initsourcesink_bind_config(struct usb_configuration*c)
{
structf_sourcesink *ss;
int status;
ss = kzalloc(sizeof *ss, GFP_KERNEL);
if(!ss)
return-ENOMEM;
ss->function.name = "source/sink";
ss->function.descriptors =fs_source_sink_descs;
ss->function.bind =sourcesink_bind;
ss->function.unbind =sourcesink_unbind;
ss->function.set_alt =sourcesink_set_alt;
ss->function.disable =sourcesink_disable;
status = usb_add_function(c,&ss->function);//下面会看到
if(status)
kfree(ss);
returnstatus;
}
sink只用一个接口,在这我们要改变一下思维,上面说struct usb_function 相当于接口,在这
我改为struct f_sourcesink相当于接口,struct usb_function相当于接口的操作功能集合
struct f_sourcesink {
structusb_function function;
structusb_ep *in_ep;
structusb_ep *out_ep;
};
//为了节约纸张,我去掉了一下注释和DBG()
int usb_add_function(struct usb_configuration*config,
structusb_function *function)
{
int value = -EINVAL;
if(!function->set_alt || !function->disable)//set_alt和disable必须实现
gotodone;
function->config = config;//记住父亲是谁
list_add_tail(&function->list,&config->functions);
if(function->bind) {//下面看到的sourcesink_bind
value =function->bind(config, function);
if(value < 0) {
list_del(&function->list);
function->config =NULL;
}
} else
value = 0;
//设置速度标志
if(!config->fullspeed && function->descriptors)
config->fullspeed = true;
if(!config->highspeed && function->hs_descriptors)
config->highspeed = true;
if(!config->superspeed && function->ss_descriptors)
config->superspeed = true;
done:
returnvalue;
}
//到这我们看到接口也绑到配置了,只剩下端点了
static int __init
sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev*cdev = c->cdev;
struct f_sourcesink *ss = func_to_ss(f);
int id;
id = usb_interface_id(c, f);//和之前说的分配id没什么两样
if (id < 0)
return id;
source_sink_intf.bInterfaceNumber = id;//接口id赋值
ss->in_ep =usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
/*
struct usb_ep *usb_ep_autoconfig(
struct usb_gadget *gadget,
structusb_endpoint_descriptor *desc
)
{
returnusb_ep_autoconfig_ss(gadget, desc, NULL);
}
struct usb_ep *usb_ep_autoconfig_ss(
struct usb_gadget *gadget,
structusb_endpoint_descriptor *desc,
structusb_ss_ep_comp_descriptor *ep_comp
)
{
//没全贴,重点就是下面这个,就是从gadget的ep列表中找到合适的ep,主要通过ep_matches()
//这个不细说了,还要说一下就是是通过desc的信息查找的
list_for_each_entry (ep,&gadget->ep_list, ep_list) {
if(ep_matches(gadget, ep, desc, ep_comp))
return ep;
}
}
*/
if (!ss->in_ep) {
autoconf_fail:
return -ENODEV;
}
ss->in_ep->driver_data= cdev; /* claim */
ss->out_ep =usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
//和上面一样的,我们可以认为这个就是把端点绑定到接口
if (!ss->out_ep)
goto autoconf_fail;
ss->out_ep->driver_data = cdev; /* claim */
//高速设置
if(gadget_is_dualspeed(c->cdev->gadget)) {
hs_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
f->hs_descriptors= hs_source_sink_descs;
}
//超速设置
if(gadget_is_superspeed(c->cdev->gadget)) {
ss_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
ss_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
f->ss_descriptors= ss_source_sink_descs;
}
return 0;
}
该绑的都绑了,我们来总结一下吧
看上面的过程,bind首先把端点、接口、配置、设备绑在一起
还有最关键是把gadget和udc绑在一起,这个有两个地方体现,一个是composite_bind()调用传入了udc的gadget,还有就是sourcesink_bind()调用usb_ep_autoconfig()找端点。
事实上绑定还有一个涵义就是把gadget驱动和其他驱动联系到一起,例如串口、网卡等。我在后面也尽量模拟一下这个过程,当然是简单的驱动。
最后还要提一下
if(!function->set_alt || !function->disable)//set_alt和disable必须实现
gotodone;
至少告诉我们基于composite的gadget驱动,必须要实现它们,这个对后面写gadget有用。
![](http://static.blog.csdn.net/images/category_icon.jpg)
版权声明:本文为博主原创文章,未经博主允许不得转载。
f_sourcesink.c里面在执行set_alt方法后就调用source_sink_start_ep(),这里面就有usb_ep_queue()可以认为开始进行数据交互。后期我要改一下这个过程,通过一个简单的misc驱动去分开调用接受发送
我们现在看
static int source_sink_start_ep(struct f_sourcesink *ss,boolis_in)
{
structusb_ep *ep;
structusb_request *req;
int status;
ep = is_in ? ss->in_ep :ss->out_ep;
//是in还是out,
req = alloc_ep_req(ep);
if(!req)
return-ENOMEM;
req->complete =source_sink_complete;//下面来说
if(is_in)// 这里要说的就是in是对于主机来说
reinit_write_data(ep, req);//把数据写到req的buf中
else
memset(req->buf, 0x55,req->length);//初始化req->buf
status = usb_ep_queue(ep, req,GFP_ATOMIC);//提交req到硬件fifo
if(status) {
structusb_composite_dev *cdev;
cdev =ss->function.config->cdev;
free_ep_req(ep, req);
}
/*
这里总结一下数据交互过程
1. 动态申请空间。
2. 设置完成函数。
3. 是in就或取数据(操作硬件的话,可能用到别的驱动),是out就初始化req->buf。
4. 提交req到硬件fifo。
万变不离其宗,大概就这样。
*/
returnstatus;
}
看完成函数
static void source_sink_complete(struct usb_ep *ep,structusb_request *req)
{
structf_sourcesink *ss = ep->driver_data;
structusb_composite_dev *cdev = ss->function.config->cdev;
int status = req->status;
switch(status) {
case0: /*normal completion? */
/*
正常,继续操作req。
*/
if(ep == ss->out_ep) {
check_read_data(ss,req);//f_sourcesink提供,MOD63或0
memset(req->buf,0x55, req->length);
} else
reinit_write_data(ep,req);
break;
/*this endpoint is normally active while we're configured */
case-ECONNABORTED: /* hardware forced ep reset */
case-ECONNRESET: /*request dequeued */
case-ESHUTDOWN: /*disconnect from host *///这几个说明传输停止
if(ep == ss->out_ep)
check_read_data(ss,req);
free_ep_req(ep, req);
return;
case-EOVERFLOW: /*buffer overrun on read means that
* we didn't provide a big enough
* buffer.
*/
default:
case-EREMOTEIO: /*short read *///一些错误,没有操作buf,相当于重发
break;
}
status = usb_ep_queue(ep, req,GFP_ATOMIC);
//继续提交,几乎所有的complete都要做的,正常就要继续
}
/*
总结一下complete
1.是否有一些错误,根据错误判断是停止还是重发。
2.正常就要继续提交req。
*/
最后是setup
这个要从udc说起
看s3c2410,假设ep0中断,且是置位了setup_end位
会调用
dev->driver->setup(&dev->gadget,crq);
就是它,事实这就是和usb枚举相关
static int
composite_setup(struct usb_gadget *gadget,conststructusb_ctrlrequest *ctrl)//ctrl是硬件获得的
{
u8 intf = w_index & 0xFF;
structusb_function *f = NULL;
//省掉了一大堆
switch(ctrl->bRequest) {
//对于各请求,composite_setup写成了一个统一的,我们只要实现接口就可以了
caseUSB_REQ_GET_DESCRIPTOR:
/* any number of configs can work */
caseUSB_REQ_SET_CONFIGURATION:
caseUSB_REQ_GET_CONFIGURATION:
caseUSB_REQ_SET_INTERFACE:
caseUSB_REQ_GET_INTERFACE:
caseUSB_REQ_GET_STATUS:
caseUSB_REQ_CLEAR_FEATURE:
caseUSB_REQ_SET_FEATURE:
break;
default:
switch(ctrl->bRequestType & USB_RECIP_MASK) {//接受对象
caseUSB_RECIP_INTERFACE://intf = w_index & 0xFF,对应的接口号
if (!cdev->config || intf >=MAX_CONFIG_INTERFACES)
break;
f =cdev->config->interface[intf];
break;
caseUSB_RECIP_ENDPOINT:
endp = ((w_index &0x80) >> 3) | (w_index & 0x0f);
/*
这个操作过后,endp的第4位是方向,0~3是地址,对应set_config()中的
addr =((ep->bEndpointAddress & 0x80) >> 3)
| (ep->bEndpointAddress & 0x0f);
set_bit(addr,f->endpoints);
*/
list_for_each_entry(f,&cdev->config->functions, list) {
if (test_bit(endp, f->endpoints))
break;
}
if (&f->list == &cdev->config->functions)
f = NULL;
break;
}//上面就是找对应的struct usb_function
if(f && f->setup)// structusb_function中的setup优先使用,f_sourcesink没实现
value = f->setup(f,ctrl);
else{
struct usb_configuration *c;
c = cdev->config;
if (c && c->setup)
value =c->setup(c, ctrl); //对我们来说就是sourcesink_setup()
}
gotodone;
}
}
static int sourcesink_setup(struct usb_configuration *c,
conststruct usb_ctrlrequest *ctrl)
{
structusb_request *req = c->cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);//小端转换要注意一下
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
req->length = USB_BUFSIZ;
//我们知道请求分标准和非标准,这下面两个就是非标准,
//控制端口允许多包请求,为了测试
switch(ctrl->bRequest) {
case0x5b: //控制写。 填充buffer
if(ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
goto unknown;
if(w_value || w_index)
break;
if(w_length > req->length)//大于USB_BUFSIZ,就用USB_BUFSIZ
break;
value = w_length;//请求的数据长度
break;
case0x5c: //控制读。 返回buffer
if(ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
goto unknown;
if(w_value || w_index)
break;
if(w_length > req->length)
break;
value = w_length;
break;
default:
unknown:
}
if(value >= 0) {
req->zero = 0;
req->length = value;
value =usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);//看到是从控制端口输出的,对于上面的多包请求,这就是最特别的一点
}
returnvalue;
}
/*
总结一下setup
1. composite_setup()为我们考虑了标准请求。
2. struct usb_function和struct usb_configuration只要实现一个setup,都实现的话会用struct usb_function。
3. 我们实现的setup从上面看主要是处理特殊请求。
*/
在虚拟机装载
板子提示
虚拟机
有个skel0 设备文件
虚拟机执行
如果板子不动作,最后
现在执行cat /dev/skel0
板子执行
虚拟机
反过来
板子读,虚拟机写
当你拔去usb是你会发现/dev/skel0消失
![](http://static.blog.csdn.net/images/category_icon.jpg)
版权声明:本文为博主原创文章,未经博主允许不得转载。
花了半个月,才搞定驱动中的枚举部分,现在说linux的枚举,windows可能有差别。
代码我会贴在后面,现在只是实现枚举,你可能对代码不感兴趣,我就不分析代码了,你可以看看
在《自娱自乐1》中的模板,比较一下,我做了什么,这会给你写udc驱动提供个思路。我直接分析
调试打印,就是枚举过程,我们从代码看枚举。打印位置可以在下面的代码里找到。
如果你要弄懂驱动代码中涉及枚举的地方,你就仔细看看代码在那打印的,这个对你完成一个udc驱
动有帮助。
如果你只是想简单了解枚举你就看看我分析的调试打印就可以了。
http://wenku.baidu.com/view/87064d244b35eefdc8d333dc.html
这个是枚举过程,对照着上面说的看我的调试打印,提示我的是从机,他说的是主机。
# insmod s3c2440_add_udc.ko
UPLLCON = 38022, wxl add//驱动usb的时钟源
# insmod s3c2440_udc.ko
# insmod gadget_transfer.ko
s3c2440_start//调用s3c2440_start(),在此会PULL_UP
s3c2440_udc_alloc_request//分配请求结构体内存
gadget_transfer gadget: gadget_transfer ready
# USB reset//pc检测到PULL_UP 复位设备
USB ep0 irq//ep0中断
Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x40//请求信息
//bRequest = 06是请求描述符,bRequestType = 80输入方向(输入是对主机而言) 端点0,
//请求长度64(微软的策略,这里linux学微软的)。
//wValue = 0x100: 这个前面的1表示设备描述符
USB_REQ_GET_DESCRIPTOR//第一次请求主要是获取最大包长度,此值在设备描述符第8个字节
USB_DT_DEVICE//设备描述符请求
s3c2440_udc_queue//调用了s3c2440_udc_queue()
Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1
// Vendor = ff0 Product = ff0 Device = 212 这个在上一篇的gadget_transfer驱动中可以看到
// iManufacturer = 1 iProduct = 2iSerialNumber = 3 这个是字符串描述符引索
// bNumConfigurations = 1 你看看我上一篇的gadget_transfar驱动写的是2,这里却是1
/*
bNumConfigurations是配置数,是用count_configs()统计的,和你在gadget驱动中赋值无关
多配置很少,不过还是有,例如multi.c可以配置为RNDIS或ECM。
*/
8bytes USB USB reset //一次发出8个字节,我的ep0最大包长度是8,还有下面的xbytes都是已发的字节数。微软的策略后面没发完的不要了,直接复位设备。没有按usb spec来做。Windows要接受16个才复位。
USB ep0 irq
Host: bRequest = 05 bRequestType = 00 wValue = 0x4wIndex=0x0 wLength=0x0
//主机的请求又来了,这次是设置地址
// bRequest = 05 就是设置地址请求,
// bRequestType = 00 方向out,地址0
//wValue=0x4 设备地址4,在驱动中看不到它的使用,应该是硬件来判断
USB_REQ_SET_ADDRESS
USB ep0 irq//这个我调试是数据没准备好
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x12
//这次是真的获取设备描述符,长度18
USB_REQ_GET_DESCRIPTOR
USB_DT_DEVICE
s3c2440_udc_queue
Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1//上面已解释
8bytes USB ep0 irq
16bytes USB ep0irq
18bytes USB ep0irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0x9
/*
wValue = 0x200: 这个前面的2表示配置描述符,后面是引索
*/
//获取配置描述符
USB_REQ_GET_DESCRIPTOR
USB_DT_CONFIG
s3c2440_udc_queue
Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250
/*
length = 9描述符长度为9
TotalLength = 32配置信息的长度--包括配置描述符、接口描述符、端点描述符长度的总和
NumInterfaces = 1 接口数
ConfigurationValue = 3用于表示 USB设备的配置值,主机就是根据这个选
iConfiguration = 4字符串描述符的索引值
bMaxPower = 250用于表示 USB设备运行时所需要消耗的总线电流,单位以2mA 为基准。USB设备可以从USB总线上获得最大的电流为500mA,因此 bMaxPower 字段的最大值可以设置为250。
还有个bmAttributes,没打印
*/
8bytes USB ep0 irq
9bytes USB ep0irq
USB ep0 irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff
/*
wValue = 0x300: 这个前面的3表示字符串描述符,后面是引索0
之前并没有看到0引索的字符串描述符,不过看一下composite.c就知道了
/* 0 == report all availablelanguage codes */
可用的语言数,不细说
*/
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 4
4bytes USB ep0irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x303wIndex=0x409 wLength=0xff
//3是上面的iSerialNumber = 3
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 66
0123456789.0123456789.0123456789
//可以在上一篇的驱动开的这个,static charserial[] = "0123456789.0123456789.0123456789";
8bytes USB ep0 irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
48bytes USB ep0irq
56bytes USB ep0irq
64bytes USB ep0irq
66bytes USB ep0irq
//下面又来一次,就是获得的字符串描述不同而已
Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_CONFIG
s3c2440_udc_queue
Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
32bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 4
4bytes USB ep0irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x302wIndex=0x409 wLength=0xff
// iProduct = 2
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 46
Gadget gadget_transfer// static const char longname[]= "Gadget gadget_transfer";
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
46bytes USB ep0irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff//再一次,不清楚为什么
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 4
4bytes USB ep0irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x302wIndex=0x409 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 46
Gadget gadget_transfer
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
46bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x12
USB_REQ_GET_DESCRIPTOR
USB_DT_DEVICE//获取设备描述符
s3c2440_udc_queue
Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1
8bytes USB ep0irq
16bytes USB ep0irq
18bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x40//这里有用64长度请求一次
USB_REQ_GET_DESCRIPTOR
USB_DT_DEVICE
s3c2440_udc_queue
Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1
8bytes USB ep0irq
16bytes USB ep0irq
18bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x100wIndex=0x0 wLength=0x12
USB_REQ_GET_DESCRIPTOR
USB_DT_DEVICE//又来一次
s3c2440_udc_queue
Slave: length = 18 Vendor = ff0 Product = ff0 Device =212 iManufacturer = 1 iProduct = 2 iSerialNumber = 3 bNumConfigurations = 1
8bytes USB ep0irq
16bytes USB ep0irq
18bytes USB ep0irq
USB ep0 irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x600wIndex=0x0 wLength=0xa
USB_REQ_GET_DESCRIPTOR
USB_DT_DEVICE_QUALIFIER
//设备限定描述符用于指定另一传输速率下该设备的总体信息,如果高速USB设备既需要采用高速传//输又需要全速传输,则它必须支持设备限定描述符(Device_Qualifier)。全速设备不支持
//我的是全速设备用不倒QUALIFIER,composite.c用gadget_is_dualspeed()这个判断
USB ep0 irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x600wIndex=0x0 wLength=0xa
USB_REQ_GET_DESCRIPTOR
USB_DT_DEVICE_QUALIFIER
USB ep0 irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x600wIndex=0x0 wLength=0xa
USB_REQ_GET_DESCRIPTOR
USB_DT_DEVICE_QUALIFIER
USB ep0 irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0x9
USB_REQ_GET_DESCRIPTOR
USB_DT_CONFIG//这次你会看到到wValue & 0xff 是0到3
// iManufacturer = 1 iProduct = 2iSerialNumber = 3
s3c2440_udc_queue
Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250
8bytes USB ep0irq
9bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x200wIndex=0x0 wLength=0x20
USB_REQ_GET_DESCRIPTOR
USB_DT_CONFIG
s3c2440_udc_queue
Slave: length = 9 TotalLength = 32 NumInterfaces = 1ConfigurationValue = 3 iConfiguration = 4 bMaxPower = 250
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x300wIndex=0x0 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 4
4bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x302wIndex=0x409 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 46
Gadget gadget_transfer
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
46bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x301wIndex=0x409 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 58
Linux 3.2.0 with s3c2440_udc
/*
还记得我的gadget驱动开始
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
*/
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
48bytes USB ep0irq
56bytes USB ep0irq
58bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x303wIndex=0x409 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 66
0123456789.0123456789.0123456789
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
48bytes USB ep0irq
56bytes USB ep0irq
64bytes USB ep0irq
66bytes USB ep0irq
USB ep0 irq
Host: bRequest = 09 bRequestType = 00 wValue = 0x3wIndex=0x0 wLength=0x0
USB_REQ_SET_CONFIGURATION//设置配置
// wValue = 0x3对应上面的ConfigurationValue = 3程序中是
/*
static struct usb_configurationsourcesink_driver = {
.label = "source/sink",
.strings = sourcesink_strings,
.setup = sourcesink_setup,
.bConfigurationValue = 3,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
/* .iConfiguration = DYNAMIC */
};
*/
gadget_transfer gadget: full-speed config#3: source/sink//上面的.label ="source/sink",
s3c2440_udc_ep_enable
s3c2440_udc_ep_enable
s3c2440_udc_queue
USB ep0 irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x304wIndex=0x409 wLength=0xff
USB_REQ_GET_DESCRIPTOR
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 42
source and sink data9
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
42bytes USB ep0irq
USB ep0 irq
Host: bRequest = 06 bRequestType = 80 wValue = 0x304wIndex=0x409 wLength=0xff
USB_REQ_GET_DESCRIPTOR//这里引索是4就是iConfiguration = 4
USB_DT_STRING
s3c2440_udc_queue
Slave: length = 42
source and sink data9
/*代码
static struct usb_stringstrings_sourcesink[] = {
[0].s ="source and sink data",
{ } /* end of list */
};
*/
8bytes USB ep0irq
16bytes USB ep0irq
24bytes USB ep0irq
32bytes USB ep0irq
40bytes USB ep0irq
42bytes USB ep0irq
有些重复的请求就是为了确认。
下面是代码:(现在只能枚举),可以把内核自带的驱动和我的比较一下,我的宗旨是应用我的模板。
s3c2440_udc.h
- /***********************************
- 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
- //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__
s3c2440.c
- /***********************************
- 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>//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 <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: 接收者
- */
- printk( "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 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 fifo_count, avail, len, bufspace;
- int ret = 0;
- printk( "%s\n", __func__);
- idx = ep->bEndpointAddress & 0x7f;
- if (idx > 4)
- {
- idx = 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;
- WRITE_REG(dev, INDEX_REG, idx);
- s3c2440_usb_fifocnt(dev, &fifo_count);
- 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要接收的总长度
- printk( "read: req->req.actual = %d, req->req.length = %d\n", req->req.actual, req->req.length);
- 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
- {
- }
- s3c2440_udc_done(ep, req, 0);
- } else
- {
- if (idx == 0)
- {
- EP0_CLROPR(dev);
- }
- else
- {
- }
- }
- return ret;
- }
- static int printDesc = 0;
- 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;
- //printk( "%s\n", __func__);
- struct usb_device_descriptor *desc;
- struct usb_string_descriptor *string;
- struct usb_config_descriptor *config;
- u16 language;
- u32 n;
- u8 *tmp;
- switch (printDesc)
- {
- case USB_DT_DEVICE:
- desc = (struct usb_device_descriptor*)req->req.buf;
- printk( "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;
- printk( "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;
- printk( "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
- {
- printk( "%c", *tmp);//没考虑大小端
- }
- }
- printk("\n");
- break;
- case USB_DT_BOS:
- break;
- default:
- break;
- }
- printDesc = 0;
- 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已发送长度
- printk( " %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;
- //printk( \
- "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
- {
- }
- s3c2440_udc_done(ep, req, 0);
- }
- else
- {
- if (idx == 0)
- {
- if (!GETB(dev, USB_INT_REG, 2))
- {
- EP0_SETIPR(dev);
- }
- }
- else
- {
- }
- }
- return ret;
- }
- static void s3c2440_dequeue_all(struct s3c2440_ep *ep, int status);
- 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;
- //printk( "%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: printk( "USB_REQ_GET_STATUS\n");
- EP0_CLROPR(dev);
- if ((ctrlq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
- {
- }
- break;
- case USB_REQ_CLEAR_FEATURE: printk( "USB_REQ_CLEAR_FEATURE\n");
- EP0_CLROPR(dev);
- break;
- case USB_REQ_SET_FEATURE: printk( "USB_REQ_SET_FEATURE\n");
- EP0_CLROPR(dev);
- break;
- case USB_REQ_SET_ADDRESS: printk( "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: printk( "USB_REQ_GET_DESCRIPTOR\n");
- switch (ctrlq.wValue >> 8)
- {
- case USB_DT_DEVICE: printk( "USB_DT_DEVICE\n");
- break;
- //设备限定描述符用于指定另一传输速率下该设备的总体信息,如果高速 USB设备既需要采用高速传输又需
- //要全速传输,则它必须支持设备限定描述符(Device_Qualifier)。全速设备不支持
- case USB_DT_DEVICE_QUALIFIER: printk( "USB_DT_DEVICE_QUALIFIER\n");
- break;
- case USB_DT_OTHER_SPEED_CONFIG: printk( "USB_DT_OTHER_SPEED_CONFIG\n");
- break;
- case USB_DT_CONFIG: printk( "USB_DT_CONFIG\n");
- break;
- case USB_DT_STRING: printk( "USB_DT_STRING\n");
- break;
- //其他速率配置描述符用于指定另一传输速率下该设备的配置信息,如果高速USB设备既需要采用高速传输
- //又需要全速传输,则它必须支持其他速率配置描述符
- case USB_DT_BOS: printk( "USB_DT_BOS\n");
- break;
- }
- EP0_CLROPR(dev);
- break;
- case USB_REQ_SET_DESCRIPTOR: printk( "USB_REQ_SET_DESCRIPTOR\n");
- EP0_CLROPR(dev);
- break;
- case USB_REQ_GET_CONFIGURATION: printk( "USB_REQ_GET_CONFIGURATION\n");
- EP0_CLROPR(dev);
- break;
- case USB_REQ_SET_CONFIGURATION:
- if (ctrlq.bRequestType == USB_RECIP_DEVICE)
- {
- printk( "USB_REQ_SET_CONFIGURATION\n");
- config = 1;
- EP0_CLROPR(dev);
- EP0_SETDE(dev);
- }
- break;
- case USB_REQ_GET_INTERFACE: printk( "USB_REQ_GET_INTERFACE\n");
- EP0_CLROPR(dev);
- break;
- case USB_REQ_SET_INTERFACE:
- if (ctrlq.bRequestType == USB_RECIP_INTERFACE)
- {
- printk( "SB_REQ_SET_INTERFACE\n");
- config = 1;
- EP0_CLROPR(dev);
- EP0_SETDE(dev);
- }
- break;
- case USB_REQ_SYNCH_FRAME: printk( "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;
- //为了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;
- }
- 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;
- }
- }
- //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;
- //printk( "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);
- //printk( "USB_INT_REG = 0x%x\n", usb_status);
- //printk( "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))
- {
- printk( "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))
- {
- printk( "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)
- {
- printk( "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)
- {
- printk( "USB ep0 irq\n");
- SETB(dev, EP_INT_REG, 0);
- s3c2440_handle_ep0(dev);
- }
- 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;
- printk( "%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_CSR2_REG, 0);//关闭in dma中断,先不用dma
- SETB(dev, IN_CSR2_REG, 5);//in
- SETB(dev, IN_CSR2_REG, 6);//批量端点
- }
- else
- {
- CLRB(dev, IN_CSR2_REG, 5);//out
- SETB(dev, OUT_CSR1_REG, 0);//清楚IN_PKT_RDY
- SETB(dev, OUT_CSR1_REG, 4);//FLUSH fifo
- SETB(dev, OUT_CSR2_REG, 5);//批量端点
- SETB(dev, OUT_CSR2_REG, 6);//关闭out dma中断,先不用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;
- printk( "%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;
- printk( "%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);
- printk( "%s\n", __func__);
- if (!_ep || !_req)
- return;
- WARN_ON (!list_empty (&req->queue));
- kfree(req);
- }
- //下面的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 fifo_count, ep_csr;
- printk( "%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 == 0)
- {
- ep_csr = READ_REG(dev, EP0_CSR);
- }
- else
- {
- ep_csr = READ_REG(dev, (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) == 1)
- {
- dev->ep0state = EP0_IDLE;
- req = NULL;
- }
- }
- break;
- default:
- local_irq_restore(flags);
- return -EL2HLT;
- }
- }
- else if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
- {
- }
- else
- {
- }
- }
- 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;
- printk( "%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;
- printk( "%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;
- printk( "%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;
- 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;
- 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 {
- /* 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 s3c2440_udc_set_halt(struct usb_ep *_ep, int value)
- {
- printk( "%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)
- {
- printk( "%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)
- {
- printk( "%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;
- printk( "%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);
- printk( "%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;
- printk( "%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;
- printk( "%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;
- printk( "%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;
- printk( "%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;
- printk( "%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;
- printk( "%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)) {
- printk( "%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");
次我说的是三星平台的dma,不是三星的某款芯片的dma使用。这主要得益于三星公司统一了接口。比如我后有的例子是在s3c2440上做的但是我是参考s3c64xx的spi驱动。
当然内核还是linux-3.2.36,我们看dma-ops.h
- /* arch/arm/plat-samsung/include/plat/dma-ops.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Samsung DMA support
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #ifndef __SAMSUNG_DMA_OPS_H_
- #define __SAMSUNG_DMA_OPS_H_ __FILE__
- #include <linux/dmaengine.h>
- struct samsung_dma_prep_info {
- enum dma_transaction_type cap;//dma处理类型
- enum dma_data_direction direction;//dma传输方向
- dma_addr_t buf;//内存地址
- unsigned long period;//
- unsigned long len;//buf长度,sizeof(buf) * width,width在下面struct samsung_dma_info
- /*
- .c中调用
- int len = (info->cap == DMA_CYCLIC) ? info->period : info->len;
- ...
- 我不是太清楚period和len区别
- */
- void (*fp)(void *data);//dma中断时会调用,一般作为dma传输完成的接口
- void *fp_param;//传入上面fp的参数
- };
- struct samsung_dma_info {
- enum dma_transaction_type cap;//dma处理类型
- /*
- if (info->cap == DMA_CYCLIC)
- s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR);//chan->flags设置
- 这个可能和芯片有点关系
- 我的plat-s3c24xx中,通道请求函数
- if (chan->flags & S3C2410_DMAF_AUTOSTART) {//如果设置为自动运行,就调用start函数
- s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
- S3C2410_DMAOP_START);
- }
- 没有判断S3C2410_DMAF_CIRCULAR标志
- */
- enum dma_data_direction direction;//dma传输方向
- enum dma_slave_buswidth width;//要传输的数据宽度,就是(字节、半字、字)
- dma_addr_t fifo;//外设地址
- struct s3c2410_dma_client *client;
- /*
- struct s3c2410_dma_client {
- char *name;
- };
- 就是一个name,你申请通道时命名就可以了,主要dma中断注册是用、free通道时判断
- 是不是正确通道
- */
- };
- struct samsung_dma_ops {
- unsigned (*request)(enum dma_ch ch, struct samsung_dma_info *info);//请求会有些限制
- //1.总是认为我们的外围设备是一个固定的地址
- //2.总是认为我们的内存地址增加
- //3.硬件触发
- //4.所有传输完成产生中断
- //上面这个从数据手册上看是可以设的,但是三星写的驱动代码没有选的可能
- int (*release)(unsigned ch, struct s3c2410_dma_client *client);//释放
- int (*prepare)(unsigned ch, struct samsung_dma_prep_info *info);//准备
- //准备会把内存数据加入链表中,如果有数据在传输,会打开再加载开关
- int (*trigger)(unsigned ch);//触发
- int (*started)(unsigned ch);//再次开始,实际就是再次载入数据
- int (*flush)(unsigned ch);//清除通道数据
- int (*stop)(unsigned ch);//停止
- };
- extern void *samsung_dmadev_get_ops(void);
- extern void *s3c_dma_get_ops(void);
- //去获取一个struct samsung_dma_ops全局变量,
- //然后调用驱动,这个倒是给我们提供了一种驱动之间调用的方法
- static inline void *__samsung_dma_get_ops(void)
- {
- if (samsung_dma_is_dmadev())
- return samsung_dmadev_get_ops();
- else
- return s3c_dma_get_ops();
- }
- /*
- * samsung_dma_get_ops
- * get the set of samsung dma operations
- */
- //在驱动中调用这个
- #define samsung_dma_get_ops() __samsung_dma_get_ops()
- #endif /* __SAMSUNG_DMA_OPS_H_ */
如果你只是想看看应用接口,你可以在此打住,直接看《自娱自乐10》中dma使用的例子,增加你的理解。
如果你和我一样死脑筋,你可以看看下面的源码分析:主要有三个文件
下面这个文件,看的时候不要纠结,主要是为第二个通道使用的,知道就行。
- /* linux/arch/arm/plat-samsung/dma.c
- *
- * Copyright (c) 2003-2009 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- * http://armlinux.simtec.co.uk/
- *
- * S3C DMA core
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- struct s3c2410_dma_buf;
- /*
- struct s3c2410_dma_buf {
- struct s3c2410_dma_buf *next;
- int magic; // magic
- int size; // buffer size in bytes
- dma_addr_t data; // start of DMA data
- dma_addr_t ptr; // where the DMA got to [1]
- void *id; // client's id
- };
- */
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/errno.h>
- #include <mach/dma.h>
- #include <mach/irqs.h>
- /* dma channel state information */
- struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];
- struct s3c2410_dma_chan *s3c_dma_chan_map[DMACH_MAX];
- /* s3c_dma_lookup_channel
- *
- * change the dma channel number given into a real dma channel id
- */
- //查找对应通道的struct s3c2410_dma_chan
- //这个通道不是dma每个通道,是指外设对应的。
- struct s3c2410_dma_chan *s3c_dma_lookup_channel(unsigned int channel)
- {
- if (channel & DMACH_LOW_LEVEL)
- return &s3c2410_chans[channel & ~DMACH_LOW_LEVEL];
- else
- return s3c_dma_chan_map[channel];
- }
- /* do we need to protect the settings of the fields from
- * irq?
- */
- int s3c2410_dma_set_opfn(enum dma_ch channel, s3c2410_dma_opfn_t rtn)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: chan=%p, op rtn=%p\n", __func__, chan, rtn);
- chan->op_fn = rtn;
- //从下面可以看出在开始和结束时会调用这个函数
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_set_opfn);
- int s3c2410_dma_set_buffdone_fn(enum dma_ch channel, s3c2410_dma_cbfn_t rtn)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: chan=%p, callback rtn=%p\n", __func__, chan, rtn);
- //在s3c2410_dma_flush和中断函数中调用
- chan->callback_fn = rtn;
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_set_buffdone_fn);
- //设置标志
- int s3c2410_dma_setflags(enum dma_ch channel, unsigned int flags)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- if (chan == NULL)
- return -EINVAL;
- chan->flags = flags;
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_setflags);
下面是plat-s3c24xx中的,应该只适用s3c24xx芯片
文件有点大,我提供一个看的方法
1. 看init函数。
2. 看request函数。
3. 看s3c2410_dma_ctrl,里面有停止、开始等。
4. 看中断函数。
5. 看电源管理。
- /* linux/arch/arm/plat-s3c24xx/dma.c
- *
- * Copyright 2003-2006 Simtec Electronics
- * Ben Dooks <ben@simtec.co.uk>
- *
- * S3C2410 DMA core
- *
- * http://armlinux.simtec.co.uk/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #ifdef CONFIG_S3C2410_DMA_DEBUG
- #define DEBUG
- #endif
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/sched.h>
- #include <linux/spinlock.h>
- #include <linux/interrupt.h>
- #include <linux/syscore_ops.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/io.h>
- #include <asm/system.h>
- #include <asm/irq.h>
- #include <mach/hardware.h>
- #include <mach/dma.h>
- #include <mach/map.h>
- #include <plat/dma-s3c24xx.h>
- #include <plat/regs-dma.h>
- /* io map for dma */
- static void __iomem *dma_base;
- static struct kmem_cache *dma_kmem;
- static int dma_channels;
- static struct s3c24xx_dma_selection dma_sel;
- //调试功能就不看了
- /* debugging functions */
- #define BUF_MAGIC (0xcafebabe)
- #define dmawarn(fmt...) printk(KERN_DEBUG fmt)
- #define dma_regaddr(chan, reg) ((chan)->regs + (reg))
- #if 1
- #define dma_wrreg(chan, reg, val) writel((val), (chan)->regs + (reg))
- #else
- static inline void
- dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val)
- {
- pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg);
- writel(val, dma_regaddr(chan, reg));
- }
- #endif
- #define dma_rdreg(chan, reg) readl((chan)->regs + (reg))
- /* captured register state for debug */
- struct s3c2410_dma_regstate {
- unsigned long dcsrc; //源
- unsigned long disrc; //目标
- unsigned long dstat; //状态
- unsigned long dcon; //配置
- unsigned long dmsktrig; //触发屏蔽
- };
- //下面还是调试,不看了
- #ifdef CONFIG_S3C2410_DMA_DEBUG
- /* dmadbg_showregs
- *
- * simple debug routine to print the current state of the dma registers
- */
- static void
- dmadbg_capture(struct s3c2410_dma_chan *chan, struct s3c2410_dma_regstate *regs)
- {
- regs->dcsrc = dma_rdreg(chan, S3C2410_DMA_DCSRC);
- regs->disrc = dma_rdreg(chan, S3C2410_DMA_DISRC);
- regs->dstat = dma_rdreg(chan, S3C2410_DMA_DSTAT);
- regs->dcon = dma_rdreg(chan, S3C2410_DMA_DCON);
- regs->dmsktrig = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
- }
- static void
- dmadbg_dumpregs(const char *fname, int line, struct s3c2410_dma_chan *chan,
- struct s3c2410_dma_regstate *regs)
- {
- printk(KERN_DEBUG "dma%d: %s:%d: DCSRC=%08lx, DISRC=%08lx, DSTAT=%08lx DMT=%02lx, DCON=%08lx\n",
- chan->number, fname, line,
- regs->dcsrc, regs->disrc, regs->dstat, regs->dmsktrig,
- regs->dcon);
- }
- static void
- dmadbg_showchan(const char *fname, int line, struct s3c2410_dma_chan *chan)
- {
- struct s3c2410_dma_regstate state;
- dmadbg_capture(chan, &state);
- printk(KERN_DEBUG "dma%d: %s:%d: ls=%d, cur=%p, %p %p\n",
- chan->number, fname, line, chan->load_state,
- chan->curr, chan->next, chan->end);
- dmadbg_dumpregs(fname, line, chan, &state);
- }
- static void
- dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan)
- {
- struct s3c2410_dma_regstate state;
- dmadbg_capture(chan, &state);
- dmadbg_dumpregs(fname, line, chan, &state);
- }
- #define dbg_showregs(chan) dmadbg_showregs(__func__, __LINE__, (chan))
- #define dbg_showchan(chan) dmadbg_showchan(__func__, __LINE__, (chan))
- #else
- #define dbg_showregs(chan) do { } while(0)
- #define dbg_showchan(chan) do { } while(0)
- #endif /* CONFIG_S3C2410_DMA_DEBUG */
- /* s3c2410_dma_stats_timeout
- *
- * Update DMA stats from timeout info
- */
- //记录dma传输用时
- static void
- s3c2410_dma_stats_timeout(struct s3c2410_dma_stats *stats, int val)
- {
- if (stats == NULL)
- return;
- if (val > stats->timeout_longest)
- stats->timeout_longest = val;
- if (val < stats->timeout_shortest)
- stats->timeout_shortest = val;
- stats->timeout_avg += val;
- }
- /* s3c2410_dma_waitforload
- *
- * wait for the DMA engine to load a buffer, and update the state accordingly
- */
- //等待DMA引擎载入一个缓冲
- static int
- s3c2410_dma_waitforload(struct s3c2410_dma_chan *chan, int line)
- {
- int timeout = chan->load_timeout;//1 << 18
- int took;
- if (chan->load_state != S3C2410_DMALOAD_1LOADED) {
- printk(KERN_ERR "dma%d: s3c2410_dma_waitforload() called in loadstate %d from line %d\n", chan->number, chan->load_state, line);
- return 0;
- }
- if (chan->stats != NULL)
- chan->stats->loads++;//载入加一
- while (--timeout > 0) {
- if ((dma_rdreg(chan, S3C2410_DMA_DSTAT) << (32-20)) != 0) {//左移11位,如果等于0,就表示DMA控制器就绪且传输计数值为0
- took = chan->load_timeout - timeout;//已计时间
- s3c2410_dma_stats_timeout(chan->stats, took);//在上面
- switch (chan->load_state) {
- case S3C2410_DMALOAD_1LOADED:
- chan->load_state = S3C2410_DMALOAD_1RUNNING;
- break;
- default:
- printk(KERN_ERR "dma%d: unknown load_state in s3c2410_dma_waitforload() %d\n", chan->number, chan->load_state);
- }
- return 1;
- }
- }
- if (chan->stats != NULL) {
- chan->stats->timeout_failed++;//超时,错误加一
- }
- return 0;
- }
- /* s3c2410_dma_loadbuffer
- *
- * load a buffer, and update the channel state
- */
- //载入
- static inline int
- s3c2410_dma_loadbuffer(struct s3c2410_dma_chan *chan,
- struct s3c2410_dma_buf *buf)
- {
- unsigned long reload;
- if (buf == NULL) {
- dmawarn("buffer is NULL\n");
- return -EINVAL;
- }
- pr_debug("s3c2410_chan_loadbuffer: loading buff %p (0x%08lx,0x%06x)\n",
- buf, (unsigned long)buf->data, buf->size);
- /* check the state of the channel before we do anything */
- //状态检查,并警告
- if (chan->load_state == S3C2410_DMALOAD_1LOADED) {//有一个载入
- dmawarn("load_state is S3C2410_DMALOAD_1LOADED\n");
- }
- if (chan->load_state == S3C2410_DMALOAD_1LOADED_1RUNNING) {//一个载入一个运行
- dmawarn("state is S3C2410_DMALOAD_1LOADED_1RUNNING\n");
- }
- /* it would seem sensible if we are the last buffer to not bother
- * with the auto-reload bit, so that the DMA engine will not try
- * and load another transfer after this one has finished...
- */
- if (chan->load_state == S3C2410_DMALOAD_NONE) {
- pr_debug("load_state is none, checking for noreload (next=%p)\n",
- buf->next);
- //next为空说明是最后一个,就不需要再次载入
- reload = (buf->next == NULL) ? S3C2410_DCON_NORELOAD : 0;
- } else {
- //pr_debug("load_state is %d => autoreload\n", chan->load_state);
- reload = S3C2410_DCON_AUTORELOAD;
- }
- if ((buf->data & 0xf0000000) != 0x30000000) {//内存物理地址从0x30000000开始
- dmawarn("dmaload: buffer is %p\n", (void *)buf->data);
- }
- writel(buf->data, chan->addr_reg);//数据写入
- dma_wrreg(chan, S3C2410_DMA_DCON,
- chan->dcon | reload | (buf->size/chan->xfer_unit));//配置
- chan->next = buf->next;
- /* update the state of the channel */
- switch (chan->load_state) {
- case S3C2410_DMALOAD_NONE:
- chan->load_state = S3C2410_DMALOAD_1LOADED;
- break;
- case S3C2410_DMALOAD_1RUNNING:
- chan->load_state = S3C2410_DMALOAD_1LOADED_1RUNNING;
- break;
- default:
- dmawarn("dmaload: unknown state %d in loadbuffer\n",
- chan->load_state);
- break;
- }
- return 0;
- }
- /* s3c2410_dma_call_op
- *
- * small routine to call the op routine with the given op if it has been
- * registered
- */
- static void
- s3c2410_dma_call_op(struct s3c2410_dma_chan *chan, enum s3c2410_chan_op op)
- {
- //通过s3c2410_dma_set_opfn函数赋值
- if (chan->op_fn != NULL) {
- (chan->op_fn)(chan, op);
- }
- }
- /* s3c2410_dma_buffdone
- *
- * small wrapper to check if callback routine needs to be called, and
- * if so, call it
- */
- static inline void
- s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf,
- enum s3c2410_dma_buffresult result)
- {
- #if 0
- pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n",
- chan->callback_fn, buf, buf->id, buf->size, result);
- #endif
- if (chan->callback_fn != NULL) {
- (chan->callback_fn)(chan, buf->id, buf->size, result);
- }
- }
- /* s3c2410_dma_start
- *
- * start a dma channel going
- */
- static int s3c2410_dma_start(struct s3c2410_dma_chan *chan)
- {
- unsigned long tmp;
- unsigned long flags;
- pr_debug("s3c2410_start_dma: channel=%d\n", chan->number);
- local_irq_save(flags);
- if (chan->state == S3C2410_DMA_RUNNING) {
- pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state);
- local_irq_restore(flags);
- return 0;
- }
- //记录状态
- chan->state = S3C2410_DMA_RUNNING;
- /* check wether there is anything to load, and if not, see
- * if we can find anything to load
- */
- //判断是否已载入载入
- if (chan->load_state == S3C2410_DMALOAD_NONE) {
- if (chan->next == NULL) {
- printk(KERN_ERR "dma%d: channel has nothing loaded\n",
- chan->number);
- chan->state = S3C2410_DMA_IDLE;//没有可载入的state记为空闲
- local_irq_restore(flags);
- return -EINVAL;
- }
- s3c2410_dma_loadbuffer(chan, chan->next);//载入缓存
- }
- dbg_showchan(chan);//调试信息
- /* enable the channel */
- if (!chan->irq_enabled) {
- enable_irq(chan->irq);//如果中断关闭,开启中断
- chan->irq_enabled = 1;
- }
- /* start the channel going */
- tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
- tmp &= ~S3C2410_DMASKTRIG_STOP;
- tmp |= S3C2410_DMASKTRIG_ON;
- dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);//开始运行
- pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp);
- #if 0
- /* the dma buffer loads should take care of clearing the AUTO
- * reloading feature */
- tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
- tmp &= ~S3C2410_DCON_NORELOAD;
- dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
- #endif
- s3c2410_dma_call_op(chan, S3C2410_DMAOP_START);//回调
- dbg_showchan(chan);
- /* if we've only loaded one buffer onto the channel, then chec
- * to see if we have another, and if so, try and load it so when
- * the first buffer is finished, the new one will be loaded onto
- * the channel */
- if (chan->next != NULL) {
- if (chan->load_state == S3C2410_DMALOAD_1LOADED) {//已有一个正在载入
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {//等待...
- pr_debug("%s: buff not yet loaded, no more todo\n",
- __func__);
- } else {
- //这里还有下面的,可以看出在s3c2410_dma_loadbuffer中
- //S3C2410_DMALOAD_1RUNNING会使用AUTORELOAD模式
- chan->load_state = S3C2410_DMALOAD_1RUNNING;
- s3c2410_dma_loadbuffer(chan, chan->next);
- }
- } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) {
- s3c2410_dma_loadbuffer(chan, chan->next);
- }
- }
- local_irq_restore(flags);
- return 0;
- }
- /* s3c2410_dma_canload
- *
- * work out if we can queue another buffer into the DMA engine
- */
- static int
- s3c2410_dma_canload(struct s3c2410_dma_chan *chan)
- {
- if (chan->load_state == S3C2410_DMALOAD_NONE ||
- chan->load_state == S3C2410_DMALOAD_1RUNNING)
- return 1;
- return 0;
- }
- /* s3c2410_dma_enqueue
- *
- * queue an given buffer for dma transfer.
- *
- * id the device driver's id information for this buffer
- * data the physical address of the buffer data
- * size the size of the buffer in bytes
- *
- * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART
- * is checked, and if set, the channel is started. If this flag isn't set,
- * then an error will be returned.
- *
- * It is possible to queue more than one DMA buffer onto a channel at
- * once, and the code will deal with the re-loading of the next buffer
- * when necessary.
- */
- //把数据加入链表
- int s3c2410_dma_enqueue(unsigned int channel, void *id,
- dma_addr_t data, int size)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- struct s3c2410_dma_buf *buf;
- unsigned long flags;
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: id=%p, data=%08x, size=%d\n",
- __func__, id, (unsigned int)data, size);
- buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC);//申请内存
- if (buf == NULL) {
- pr_debug("%s: out of memory (%ld alloc)\n",
- __func__, (long)sizeof(*buf));
- return -ENOMEM;
- }
- //pr_debug("%s: new buffer %p\n", __func__, buf);
- //dbg_showchan(chan);
- //给buf赋值
- buf->next = NULL;
- buf->data = buf->ptr = data;
- buf->size = size;
- buf->id = id;
- buf->magic = BUF_MAGIC;
- local_irq_save(flags);
- //chan->curr赋值并把buf加入链表中
- if (chan->curr == NULL) {
- /* we've got nothing loaded... */
- pr_debug("%s: buffer %p queued onto empty channel\n",
- __func__, buf);
- chan->curr = buf;
- chan->end = buf;
- chan->next = NULL;
- } else {
- pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n",
- chan->number, __func__, buf);
- if (chan->end == NULL)
- pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n",
- chan->number, __func__, chan);
- chan->end->next = buf;
- chan->end = buf;
- }
- /* if necessary, update the next buffer field */
- if (chan->next == NULL)
- chan->next = buf;
- /* check to see if we can load a buffer */
- if (chan->state == S3C2410_DMA_RUNNING) {
- if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) {//当前是否有一个在传输
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {//等待
- printk(KERN_ERR "dma%d: loadbuffer:"
- "timeout loading buffer\n",
- chan->number);
- dbg_showchan(chan);
- local_irq_restore(flags);
- return -EINVAL;
- }
- }
- while (s3c2410_dma_canload(chan) && chan->next != NULL) {//可加载且chan->next不为空
- s3c2410_dma_loadbuffer(chan, chan->next);
- }
- } else if (chan->state == S3C2410_DMA_IDLE) {
- if (chan->flags & S3C2410_DMAF_AUTOSTART) {//如果设置为自动运行,就调用start函数
- s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
- S3C2410_DMAOP_START);
- }
- }
- local_irq_restore(flags);
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_enqueue);
- static inline void
- s3c2410_dma_freebuf(struct s3c2410_dma_buf *buf)
- {
- int magicok = (buf->magic == BUF_MAGIC);
- buf->magic = -1;
- if (magicok) {
- kmem_cache_free(dma_kmem, buf);
- } else {
- printk("s3c2410_dma_freebuf: buff %p with bad magic\n", buf);
- }
- }
- /* s3c2410_dma_lastxfer
- *
- * called when the system is out of buffers, to ensure that the channel
- * is prepared for shutdown.
- */
- static inline void
- s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan)
- {
- #if 0
- pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n",
- chan->number, chan->load_state);
- #endif
- switch (chan->load_state) {
- case S3C2410_DMALOAD_NONE:
- break;
- case S3C2410_DMALOAD_1LOADED:
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
- /* flag error? */
- printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n",
- chan->number, __func__);
- return;
- }
- break;
- case S3C2410_DMALOAD_1LOADED_1RUNNING:
- /* I believe in this case we do not have anything to do
- * until the next buffer comes along, and we turn off the
- * reload */
- return;
- default:
- pr_debug("dma%d: lastxfer: unhandled load_state %d with no next\n",
- chan->number, chan->load_state);
- return;
- }
- /* hopefully this'll shut the damned thing up after the transfer... */
- dma_wrreg(chan, S3C2410_DMA_DCON, chan->dcon | S3C2410_DCON_NORELOAD);
- }
- #define dmadbg2(x...)
- static irqreturn_t
- s3c2410_dma_irq(int irq, void *devpw)
- {
- struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw;
- struct s3c2410_dma_buf *buf;
- buf = chan->curr;
- dbg_showchan(chan);
- /* modify the channel state */
- //更新状态
- switch (chan->load_state) {
- case S3C2410_DMALOAD_1RUNNING:
- /* TODO - if we are running only one buffer, we probably
- * want to reload here, and then worry about the buffer
- * callback */
- chan->load_state = S3C2410_DMALOAD_NONE;
- break;
- case S3C2410_DMALOAD_1LOADED:
- /* iirc, we should go back to NONE loaded here, we
- * had a buffer, and it was never verified as being
- * loaded.
- */
- chan->load_state = S3C2410_DMALOAD_NONE;
- break;
- case S3C2410_DMALOAD_1LOADED_1RUNNING:
- /* we'll worry about checking to see if another buffer is
- * ready after we've called back the owner. This should
- * ensure we do not wait around too long for the DMA
- * engine to start the next transfer
- */
- chan->load_state = S3C2410_DMALOAD_1LOADED;
- break;
- case S3C2410_DMALOAD_NONE:
- printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n",
- chan->number);
- break;
- default:
- printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n",
- chan->number, chan->load_state);
- break;
- }
- if (buf != NULL) {
- /* update the chain to make sure that if we load any more
- * buffers when we call the callback function, things should
- * work properly */
- //更新链表
- chan->curr = buf->next;
- buf->next = NULL;
- if (buf->magic != BUF_MAGIC) {
- printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n",
- chan->number, __func__, buf);
- return IRQ_HANDLED;
- }
- s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);//回调,使一些事情工作正常
- /* free resouces */
- s3c2410_dma_freebuf(buf);//释放空间
- } else {
- }
- /* only reload if the channel is still running... our buffer done
- * routine may have altered the state by requesting the dma channel
- * to stop or shutdown... */
- /* todo: check that when the channel is shut-down from inside this
- * function, we cope with unsetting reload, etc */
- if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) {
- unsigned long flags;
- switch (chan->load_state) {
- case S3C2410_DMALOAD_1RUNNING:
- /* don't need to do anything for this state */
- break;
- case S3C2410_DMALOAD_NONE:
- /* can load buffer immediately */
- break;
- case S3C2410_DMALOAD_1LOADED:
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {//等待传输完
- /* flag error? */
- printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n",
- chan->number, __func__);
- return IRQ_HANDLED;
- }
- break;
- case S3C2410_DMALOAD_1LOADED_1RUNNING:
- goto no_load;
- default:
- printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n",
- chan->number, chan->load_state);
- return IRQ_HANDLED;
- }
- local_irq_save(flags);
- s3c2410_dma_loadbuffer(chan, chan->next);//再次载入
- local_irq_restore(flags);
- } else {
- s3c2410_dma_lastxfer(chan);//最后一个缓冲区
- /* see if we can stop this channel.. */
- if (chan->load_state == S3C2410_DMALOAD_NONE) {
- pr_debug("dma%d: end of transfer, stopping channel (%ld)\n",
- chan->number, jiffies);
- s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL,
- S3C2410_DMAOP_STOP);//停止
- }
- }
- no_load:
- return IRQ_HANDLED;
- }
- static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel);
- /* s3c2410_request_dma
- *
- * get control of an dma channel
- */
- //获取一个dma通道
- //下面你会看到,获取一个通道信息并注册中断
- int s3c2410_dma_request(enum dma_ch channel,
- struct s3c2410_dma_client *client,
- void *dev)
- {
- struct s3c2410_dma_chan *chan;
- unsigned long flags;
- int err;
- pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n",
- channel, client->name, dev);
- local_irq_save(flags);
- //请求一个通道,下面有详细说明
- chan = s3c2410_dma_map_channel(channel);
- if (chan == NULL) {
- local_irq_restore(flags);
- return -EBUSY;
- }
- dbg_showchan(chan);
- chan->client = client;
- chan->in_use = 1;//标志为使用
- if (!chan->irq_claimed) {
- pr_debug("dma%d: %s : requesting irq %d\n",
- channel, __func__, chan->irq);
- chan->irq_claimed = 1;//中断声明标志为1
- local_irq_restore(flags);
- err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED,
- client->name, (void *)chan);
- local_irq_save(flags);
- if (err) {
- chan->in_use = 0;
- chan->irq_claimed = 0;
- local_irq_restore(flags);
- printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n",
- client->name, chan->irq, chan->number);
- return err;
- }
- chan->irq_enabled = 1;
- }
- local_irq_restore(flags);
- /* need to setup */
- pr_debug("%s: channel initialised, %p\n", __func__, chan);
- return chan->number | DMACH_LOW_LEVEL;
- }
- EXPORT_SYMBOL(s3c2410_dma_request);
- /* s3c2410_dma_free
- *
- * release the given channel back to the system, will stop and flush
- * any outstanding transfers, and ensure the channel is ready for the
- * next claimant.
- *
- * Note, although a warning is currently printed if the freeing client
- * info is not the same as the registrant's client info, the free is still
- * allowed to go through.
- */
- int s3c2410_dma_free(enum dma_ch channel, struct s3c2410_dma_client *client)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- unsigned long flags;
- if (chan == NULL)
- return -EINVAL;
- local_irq_save(flags);
- if (chan->client != client) {//检查是否为正确通道
- printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n",
- channel, chan->client, client);
- }
- /* sort out stopping and freeing the channel */
- if (chan->state != S3C2410_DMA_IDLE) {
- pr_debug("%s: need to stop dma channel %p\n",
- __func__, chan);
- /* possibly flush the channel */
- s3c2410_dma_ctrl(channel, S3C2410_DMAOP_STOP);//不是空闲,就停止通道
- }
- chan->client = NULL;
- chan->in_use = 0;
- if (chan->irq_claimed)
- free_irq(chan->irq, (void *)chan);//如果注册的中断就释放
- chan->irq_claimed = 0;
- if (!(channel & DMACH_LOW_LEVEL))
- s3c_dma_chan_map[channel] = NULL;//对应的数据置空
- local_irq_restore(flags);
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_free);
- static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan)
- {
- unsigned long flags;
- unsigned long tmp;
- pr_debug("%s:\n", __func__);
- dbg_showchan(chan);//调试不看了
- local_irq_save(flags);
- s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP);
- //(chan->op_fn)(chan, op); 回调函数
- tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);//读对应通道的寄存器
- tmp |= S3C2410_DMASKTRIG_STOP;//DMASKTRIG的第2位是STOP
- //tmp &= ~S3C2410_DMASKTRIG_ON;
- dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp);
- #if 0 //为了适合WinCE,关闭再加载
- /* should also clear interrupts, according to WinCE BSP */
- tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
- tmp |= S3C2410_DCON_NORELOAD;
- dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
- #endif
- /* should stop do this, or should we wait for flush? */
- chan->state = S3C2410_DMA_IDLE;//dma状态
- chan->load_state = S3C2410_DMALOAD_NONE;//dma数据传输状态
- local_irq_restore(flags);
- return 0;
- }
- static void s3c2410_dma_waitforstop(struct s3c2410_dma_chan *chan)
- {
- unsigned long tmp;
- unsigned int timeout = 0x10000;
- while (timeout-- > 0) {
- tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG);
- if (!(tmp & S3C2410_DMASKTRIG_ON))//判断DMASKTRIG寄存器第1位是否为0
- return;
- }
- pr_debug("dma%d: failed to stop?\n", chan->number);
- }
- /* s3c2410_dma_flush
- *
- * stop the channel, and remove all current and pending transfers
- */
- //停止通道,并移除当前和即将发生的传输
- static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan)
- {
- struct s3c2410_dma_buf *buf, *next;
- unsigned long flags;
- pr_debug("%s: chan %p (%d)\n", __func__, chan, chan->number);
- dbg_showchan(chan);
- local_irq_save(flags);
- if (chan->state != S3C2410_DMA_IDLE) {
- pr_debug("%s: stopping channel...\n", __func__ );
- //不是空闲,调用s3c2410_dma_dostop
- s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP);
- }
- buf = chan->curr;
- if (buf == NULL)
- buf = chan->next;
- //清除数据
- chan->curr = chan->next = chan->end = NULL;
- //释放空间
- if (buf != NULL) {
- for ( ; buf != NULL; buf = next) {
- next = buf->next;
- pr_debug("%s: free buffer %p, next %p\n",
- __func__, buf, buf->next);
- s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT);//回调函数调用
- s3c2410_dma_freebuf(buf);//就是调用kmem_cache_free释放
- }
- }
- dbg_showregs(chan);
- s3c2410_dma_waitforstop(chan);//等待停止
- #if 0
- /* should also clear interrupts, according to WinCE BSP */
- {
- unsigned long tmp;
- tmp = dma_rdreg(chan, S3C2410_DMA_DCON);
- tmp |= S3C2410_DCON_NORELOAD;
- dma_wrreg(chan, S3C2410_DMA_DCON, tmp);
- }
- #endif
- dbg_showregs(chan);
- local_irq_restore(flags);
- return 0;
- }
- static int s3c2410_dma_started(struct s3c2410_dma_chan *chan)
- {
- unsigned long flags;
- local_irq_save(flags);
- dbg_showchan(chan);
- /* if we've only loaded one buffer onto the channel, then chec
- * to see if we have another, and if so, try and load it so when
- * the first buffer is finished, the new one will be loaded onto
- * the channel */
- //下面的判断可以查看s3c2410_dma_start()
- if (chan->next != NULL) {
- if (chan->load_state == S3C2410_DMALOAD_1LOADED) {
- if (s3c2410_dma_waitforload(chan, __LINE__) == 0) {
- pr_debug("%s: buff not yet loaded, no more todo\n",
- __func__);
- } else {
- chan->load_state = S3C2410_DMALOAD_1RUNNING;
- s3c2410_dma_loadbuffer(chan, chan->next);
- }
- } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) {
- s3c2410_dma_loadbuffer(chan, chan->next);
- }
- }
- local_irq_restore(flags);
- return 0;
- }
- //dma 控制函数
- int
- s3c2410_dma_ctrl(enum dma_ch channel, enum s3c2410_chan_op op)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- if (chan == NULL)
- return -EINVAL;
- switch (op) {
- case S3C2410_DMAOP_START://开始
- return s3c2410_dma_start(chan);
- case S3C2410_DMAOP_STOP://停止
- return s3c2410_dma_dostop(chan);
- case S3C2410_DMAOP_PAUSE://暂停
- case S3C2410_DMAOP_RESUME:
- return -ENOENT;
- case S3C2410_DMAOP_FLUSH://冲掉
- return s3c2410_dma_flush(chan);
- case S3C2410_DMAOP_STARTED://再次开始
- return s3c2410_dma_started(chan);
- case S3C2410_DMAOP_TIMEOUT://超时
- return 0;
- }
- return -ENOENT; /* unknown, don't bother */
- }
- EXPORT_SYMBOL(s3c2410_dma_ctrl);
- /* DMA configuration for each channel
- *
- * DISRCC -> source of the DMA (AHB,APB)
- * DISRC -> source address of the DMA
- * DIDSTC -> destination of the DMA (AHB,APD)
- * DIDST -> destination address of the DMA
- */
- /* s3c2410_dma_config
- *
- * xfersize: size of unit in bytes (1,2,4)
- */
- int s3c2410_dma_config(enum dma_ch channel,
- int xferunit)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- unsigned int dcon;
- pr_debug("%s: chan=%d, xfer_unit=%d\n", __func__, channel, xferunit);
- if (chan == NULL)
- return -EINVAL;
- dcon = chan->dcon & dma_sel.dcon_mask;
- pr_debug("%s: dcon is %08x\n", __func__, dcon);
- //DCON寄存器的24~26位选择请求源
- switch (chan->req_ch) {
- case DMACH_I2S_IN:
- case DMACH_I2S_OUT:
- case DMACH_PCM_IN:
- case DMACH_PCM_OUT:
- case DMACH_MIC_IN:
- default:
- dcon |= S3C2410_DCON_HANDSHAKE;
- dcon |= S3C2410_DCON_SYNC_PCLK;
- break;
- case DMACH_SDI:
- /* note, ensure if need HANDSHAKE or not */
- dcon |= S3C2410_DCON_SYNC_PCLK;
- break;
- case DMACH_XD0:
- case DMACH_XD1:
- dcon |= S3C2410_DCON_HANDSHAKE;
- dcon |= S3C2410_DCON_SYNC_HCLK;
- break;
- }
- //上面的HANDSHAKE是选择握手模式
- //除了SDI要选,其他的都是握手模式,chan->dcon来记录你的dcon的配置
- //传输大小
- switch (xferunit) {
- case 1:
- dcon |= S3C2410_DCON_BYTE;
- break;
- case 2:
- dcon |= S3C2410_DCON_HALFWORD;
- break;
- case 4:
- dcon |= S3C2410_DCON_WORD;
- break;
- default:
- pr_debug("%s: bad transfer size %d\n", __func__, xferunit);
- return -EINVAL;
- }
- dcon |= S3C2410_DCON_HWTRIG;//硬件触发
- dcon |= S3C2410_DCON_INTREQ;//所有传输完成产生中断
- pr_debug("%s: dcon now %08x\n", __func__, dcon);
- //记录下配置
- chan->dcon = dcon;
- chan->xfer_unit = xferunit;
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_config);
- /* s3c2410_dma_devconfig
- *
- * configure the dma source/destination hardware type and address
- *
- * source: DMA_FROM_DEVICE: source is hardware
- * DMA_TO_DEVICE: source is memory
- *
- * devaddr: physical address of the source
- */
- int s3c2410_dma_devconfig(enum dma_ch channel,
- enum dma_data_direction source,
- unsigned long devaddr)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- unsigned int hwcfg;
- if (chan == NULL)
- return -EINVAL;
- pr_debug("%s: source=%d, devaddr=%08lx\n",
- __func__, (int)source, devaddr);
- chan->source = source;//源类型,内存或者硬件
- chan->dev_addr = devaddr;//
- switch (chan->req_ch) {
- case DMACH_XD0:
- case DMACH_XD1:
- hwcfg = 0; /* AHB */
- break;
- default:
- hwcfg = S3C2410_DISRCC_APB;
- }
- //除了XD0和XD1在AHB总线上,其他都是APB
- /* always assume our peripheral desintation is a fixed
- * address in memory. */
- //总是认为我们的外围设备是一个固定的地址
- hwcfg |= S3C2410_DISRCC_INC;//在传输后地址不改变
- switch (source) {
- case DMA_FROM_DEVICE://源是硬件
- /* source is hardware */
- pr_debug("%s: hw source, devaddr=%08lx, hwcfg=%d\n",
- __func__, devaddr, hwcfg);
- dma_wrreg(chan, S3C2410_DMA_DISRCC, hwcfg & 3);//源控制
- dma_wrreg(chan, S3C2410_DMA_DISRC, devaddr);//源地址
- dma_wrreg(chan, S3C2410_DMA_DIDSTC, (0<<1) | (0<<0));//目标控制:系统总线,地址增加
- chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DIDST);//记下内存地址
- break;
- case DMA_TO_DEVICE://源是内存
- /* source is memory */
- pr_debug("%s: mem source, devaddr=%08lx, hwcfg=%d\n",
- __func__, devaddr, hwcfg);
- dma_wrreg(chan, S3C2410_DMA_DISRCC, (0<<1) | (0<<0));//源:系统总线,地址增加
- dma_wrreg(chan, S3C2410_DMA_DIDST, devaddr);//目标地址地址
- dma_wrreg(chan, S3C2410_DMA_DIDSTC, hwcfg & 3);//目标控制
- chan->addr_reg = dma_regaddr(chan, S3C2410_DMA_DISRC);//记下内存地址
- break;
- default:
- printk(KERN_ERR "dma%d: invalid source type (%d)\n",
- channel, source);
- return -EINVAL;
- }
- if (dma_sel.direction != NULL)//s3c2440为空
- (dma_sel.direction)(chan, chan->map, source);
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_devconfig);
- /* s3c2410_dma_getposition
- *
- * returns the current transfer points for the dma source and destination
- */
- //当前源或目标地址
- int s3c2410_dma_getposition(enum dma_ch channel, dma_addr_t *src, dma_addr_t *dst)
- {
- struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel);
- if (chan == NULL)
- return -EINVAL;
- if (src != NULL)
- *src = dma_rdreg(chan, S3C2410_DMA_DCSRC);
- if (dst != NULL)
- *dst = dma_rdreg(chan, S3C2410_DMA_DCDST);
- return 0;
- }
- EXPORT_SYMBOL(s3c2410_dma_getposition);
- /* system core operations */
- #ifdef CONFIG_PM
- static void s3c2410_dma_suspend_chan(struct s3c2410_dma_chan *cp)
- {
- printk(KERN_DEBUG "suspending dma channel %d\n", cp->number);
- if (dma_rdreg(cp, S3C2410_DMA_DMASKTRIG) & S3C2410_DMASKTRIG_ON) {
- /* the dma channel is still working, which is probably
- * a bad thing to do over suspend/resume. We stop the
- * channel and assume that the client is either going to
- * retry after resume, or that it is broken.
- */
- printk(KERN_INFO "dma: stopping channel %d due to suspend\n",
- cp->number);
- s3c2410_dma_dostop(cp);
- }
- }
- static int s3c2410_dma_suspend(void)
- {
- struct s3c2410_dma_chan *cp = s3c2410_chans;
- int channel;
- for (channel = 0; channel < dma_channels; cp++, channel++)
- s3c2410_dma_suspend_chan(cp);
- return 0;
- }
- static void s3c2410_dma_resume_chan(struct s3c2410_dma_chan *cp)
- {
- unsigned int no = cp->number | DMACH_LOW_LEVEL;
- /* restore channel's hardware configuration */
- if (!cp->in_use)
- return;
- printk(KERN_INFO "dma%d: restoring configuration\n", cp->number);
- s3c2410_dma_config(no, cp->xfer_unit);
- s3c2410_dma_devconfig(no, cp->source, cp->dev_addr);
- /* re-select the dma source for this channel */
- if (cp->map != NULL)
- dma_sel.select(cp, cp->map);
- }
- static void s3c2410_dma_resume(void)
- {
- struct s3c2410_dma_chan *cp = s3c2410_chans + dma_channels - 1;
- int channel;
- for (channel = dma_channels - 1; channel >= 0; cp++, channel--)
- s3c2410_dma_resume_chan(cp);
- }
- #else
- #define s3c2410_dma_suspend NULL
- #define s3c2410_dma_resume NULL
- #endif /* CONFIG_PM */
- /*
- struct syscore_ops {
- struct list_head node;
- int (*suspend)(void);
- void (*resume)(void);
- void (*shutdown)(void);
- };
- 这个syscore是通过一个全局的list管理的,如下
- LIST_HEAD(syscore_ops_list);
- */
- struct syscore_ops dma_syscore_ops = {
- .suspend = s3c2410_dma_suspend,
- .resume = s3c2410_dma_resume,
- };
- /* kmem cache implementation */
- //下面看到
- static void s3c2410_dma_cache_ctor(void *p)
- {
- memset(p, 0, sizeof(struct s3c2410_dma_buf));
- }
- /* initialisation code */
- static int __init s3c24xx_dma_syscore_init(void)
- {
- //注册的实质就是
- //list_add_tail(&ops->node, &syscore_ops_list);
- //加入链表
- register_syscore_ops(&dma_syscore_ops);
- return 0;
- }
- //late_initcall启动时会执行,在那个阶段我们就不管了
- late_initcall(s3c24xx_dma_syscore_init);
- /*
- 下面这个函数告诉我们这些__init函数的调用位置
- static int __init s3c2440_dma_add(struct sys_device *sysdev)
- {
- s3c2410_dma_init();
- s3c24xx_dma_order_set(&s3c2440_dma_order);
- return s3c24xx_dma_init_map(&s3c2440_dma_sel);
- }
- static struct sysdev_driver s3c2440_dma_driver = {
- .add = s3c2440_dma_add,
- };
- static int __init s3c2440_dma_init(void)
- {
- return sysdev_driver_register(&s3c2440_sysclass, &s3c2440_dma_driver);
- }
- struct sysdev_class s3c2440_sysclass = {
- .name = "s3c2440-core",
- };
- static struct sys_device s3c2440_sysdev = {
- .cls = &s3c2440_sysclass,
- };
- 在s3c2440_init()中会调用
- sysdev_register(&s3c2440_sysdev);
- 我们从这可以看到设备与驱动的概念,当然这不是我们的重点,我只说想说
- s3c2440_dma_add初始化会被调用,我们就安初始化调用过程来看代码
- 启动时调用
- arch_initcall(s3c2440_dma_init);
- */
- //s3c24xx_dma_init在下面s3c2410_dma_init()被调用
- //s3c2440或s3c2410平台s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
- //4个通道,中断号为IRQ_DMA0,0x40 * 4就是io要映射的大小
- int __init s3c24xx_dma_init(unsigned int channels, unsigned int irq,
- unsigned int stride)
- {
- struct s3c2410_dma_chan *cp;
- int channel;
- int ret;
- printk("S3C24XX DMA Driver, Copyright 2003-2006 Simtec Electronics\n");
- dma_channels = channels;
- //寄存器映射
- dma_base = ioremap(S3C24XX_PA_DMA, stride * channels);
- if (dma_base == NULL) {
- printk(KERN_ERR "dma failed to remap register block\n");
- return -ENOMEM;
- }
- //申请后备缓存
- dma_kmem = kmem_cache_create("dma_desc",
- sizeof(struct s3c2410_dma_buf), 0,
- SLAB_HWCACHE_ALIGN,
- s3c2410_dma_cache_ctor);
- /*
- 原型:
- struct kmem_cache *kmem_cache_create(const char *name, size_t size,
- size_t align, unsigned long flags, void (*ctor)(void *))
- SLAB_HWCACHE_ALIGN:
- 这个标志需要每个数据对象被对齐到一个缓存行; 实际对齐依赖主机平
- 台的缓存分布. 这个选项可以是一个好的选择, 如果在 SMP 机器上你的
- 缓存包含频繁存取的项. 但是, 用来获得缓存行对齐的填充可以浪费可
- 观的内存量.
- s3c2410_dma_cache_ctor:
- 构造函数,上面可以看到就是把申请的内存空间清零。
- */
- if (dma_kmem == NULL) {
- printk(KERN_ERR "dma failed to make kmem cache\n");
- ret = -ENOMEM;
- goto err;
- }
- /*
- 在mach-s3c2410/include/mach/dma.h下定义的结构体
- struct s3c2410_dma_chan s3c2410_chans[S3C_DMA_CHANNELS];
- */
- for (channel = 0; channel < channels; channel++) {
- cp = &s3c2410_chans[channel];
- memset(cp, 0, sizeof(struct s3c2410_dma_chan));
- /* dma channel irqs are in order.. */
- cp->number = channel; //通道号
- cp->irq = channel + irq; //中断号
- cp->regs = dma_base + (channel * stride); //寄存器基地址
- /* point current stats somewhere */
- /*
- struct s3c2410_dma_stats *stats;
- struct s3c2410_dma_stats stats_store;
- 下面就是个指针初始化
- */
- cp->stats = &cp->stats_store;
- cp->stats_store.timeout_shortest = LONG_MAX;//#define LONG_MAX ((long)(~0UL>>1))
- /* basic channel configuration */
- cp->load_timeout = 1<<18;//向dma载入缓冲的超时时间
- printk("DMA channel %d at %p, irq %d\n",
- cp->number, cp->regs, cp->irq);
- }
- return 0;
- err:
- kmem_cache_destroy(dma_kmem);
- iounmap(dma_base);
- dma_base = NULL;
- return ret;
- }
- int __init s3c2410_dma_init(void)
- {
- return s3c24xx_dma_init(4, IRQ_DMA0, 0x40);
- }
- static inline int is_channel_valid(unsigned int channel)
- {
- //#define DMA_CH_VALID (1<<31)
- return (channel & DMA_CH_VALID);
- }
- static struct s3c24xx_dma_order *dma_order;
- /* s3c2410_dma_map_channel()
- *
- * turn the virtual channel number into a real, and un-used hardware
- * channel.
- *
- * first, try the dma ordering given to us by either the relevant
- * dma code, or the board. Then just find the first usable free
- * channel
- */
- //根据虚拟通道号,找到一个可以的通道
- //实际就是根据channel从数组s3c2440_dma_order和
- //s3c2440_dma_mapping和中找到对应且可用的元素
- //s3c2410_dma_request()会调用这个函数
- static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel)
- {
- struct s3c24xx_dma_order_ch *ord = NULL;
- struct s3c24xx_dma_map *ch_map;
- struct s3c2410_dma_chan *dmach;
- int ch;
- //dma_sel.map就是s3c2440_dma_mappings[]
- //dma_sel.map_size就是ARRAY_SIZE(s3c2440_dma_mappings)
- if (dma_sel.map == NULL || channel > dma_sel.map_size)
- return NULL;
- ch_map = dma_sel.map + channel;
- //从上面这句可以看出这个channel不是指dma的4个通道
- //而是s3c2440_dma_mappings[]对应的数组元素,下面有
- /*channel的值如下
- enum dma_ch {
- DMACH_XD0,
- DMACH_XD1,
- DMACH_SDI,
- DMACH_SPI0,
- DMACH_SPI1,
- DMACH_UART0,
- DMACH_UART1,
- ...
- 和s3c2440_dma_mappings[]对应
- */
- /* first, try the board mapping */
- if (dma_order) {
- ord = &dma_order->channels[channel];//找到对应的
- for (ch = 0; ch < dma_channels; ch++) {
- int tmp;
- if (!is_channel_valid(ord->list[ch]))//判断通道可用
- continue;
- tmp = ord->list[ch] & ~DMA_CH_VALID;//标志为不可用
- //s3c2410_dma_request函数会使in_use变为1
- if (s3c2410_chans[tmp].in_use == 0) {
- ch = tmp;
- goto found;
- }
- }
- if (ord->flags & DMA_CH_NEVER)
- return NULL;
- }
- /* second, search the channel map for first free */
- for (ch = 0; ch < dma_channels; ch++) {
- if (!is_channel_valid(ch_map->channels[ch]))//判断可用
- continue;
- if (s3c2410_chans[ch].in_use == 0) {
- printk("mapped channel %d to %d\n", channel, ch);
- break;
- }
- }
- /*
- 上面的is_channel_valid是在平台提供的数组中的标记
- in_use是此代码中的标记。
- */
- if (ch >= dma_channels)
- return NULL;
- /* update our channel mapping */
- found:
- dmach = &s3c2410_chans[ch];
- dmach->map = ch_map; //对应的struct s3c24xx_dma_map结构体
- dmach->req_ch = channel;
- s3c_dma_chan_map[channel] = dmach;//放入struct s3c2410_dma_chan结构体中
- /* select the channel */
- (dma_sel.select)(dmach, ch_map);//下面有s3c2440的select函数s3c2440_dma_select()
- return dmach;
- }
- static int s3c24xx_dma_check_entry(struct s3c24xx_dma_map *map, int ch)
- {
- return 0;
- }
- int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel)
- {
- //传入的是s3c2440_dma_sel
- /*
- struct s3c24xx_dma_map {
- const char *name;
- unsigned long channels[S3C_DMA_CHANNELS];
- unsigned long channels_rx[S3C_DMA_CHANNELS];
- };
- struct s3c24xx_dma_selection {
- struct s3c24xx_dma_map *map;
- unsigned long map_size;
- unsigned long dcon_mask;
- void (*select)(struct s3c2410_dma_chan *chan,
- struct s3c24xx_dma_map *map);
- void (*direction)(struct s3c2410_dma_chan *chan,
- struct s3c24xx_dma_map *map,
- enum dma_data_direction dir);
- };
- static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[] = {
- [DMACH_XD0] = {
- .name = "xdreq0",
- .channels[0] = S3C2410_DCON_CH0_XDREQ0 | DMA_CH_VALID,
- },
- [DMACH_XD1] = {
- .name = "xdreq1",
- .channels[1] = S3C2410_DCON_CH1_XDREQ1 | DMA_CH_VALID,
- },
- [DMACH_SDI] = {
- .name = "sdi",
- .channels[0] = S3C2410_DCON_CH0_SDI | DMA_CH_VALID,
- .channels[1] = S3C2440_DCON_CH1_SDI | DMA_CH_VALID,
- .channels[2] = S3C2410_DCON_CH2_SDI | DMA_CH_VALID,
- .channels[3] = S3C2410_DCON_CH3_SDI | DMA_CH_VALID,
- },
- //太长没贴完,就是支持的外设信息
- static void s3c2440_dma_select(struct s3c2410_dma_chan *chan,
- struct s3c24xx_dma_map *map)
- {
- chan->dcon = map->channels[chan->number] & ~DMA_CH_VALID;//选择一个通道并置为不可用
- }
- static struct s3c24xx_dma_selection __initdata s3c2440_dma_sel = {
- .select = s3c2440_dma_select,
- .dcon_mask = 7 << 24, //dcon寄存器的24~26位 源选择
- .map = s3c2440_dma_mappings,
- .map_size = ARRAY_SIZE(s3c2440_dma_mappings),
- };
- */
- struct s3c24xx_dma_map *nmap;
- size_t map_sz = sizeof(*nmap) * sel->map_size;
- int ptr;
- nmap = kmalloc(map_sz, GFP_KERNEL);
- if (nmap == NULL)
- return -ENOMEM;
- memcpy(nmap, sel->map, map_sz);
- memcpy(&dma_sel, sel, sizeof(*sel));
- dma_sel.map = nmap;
- //上面一段就是,申请内存,把s3c2440_dma_sel的信息放入dma_sel中
- for (ptr = 0; ptr < sel->map_size; ptr++)
- s3c24xx_dma_check_entry(nmap+ptr, ptr);//在上面,空函数
- return 0;
- }
- int __init s3c24xx_dma_order_set(struct s3c24xx_dma_order *ord)
- {
- //传入的就是s3c2440_dma_order
- /*
- struct s3c24xx_dma_order_ch {
- unsigned int list[S3C_DMA_CHANNELS]; // list of channels
- unsigned int flags; // flags
- };
- struct s3c24xx_dma_order {
- struct s3c24xx_dma_order_ch channels[DMACH_MAX];
- };
- DMACH_MAX指的是外设的最大ID,通过类似DMACH_UART0_TX来表示外设
- S3C_DMA_CHANNELS是最大通道数,s3c2440是4个通道
- static struct s3c24xx_dma_order __initdata s3c2440_dma_order = {
- .channels = {
- [DMACH_SDI] = {
- .list = {
- [0] = 3 | DMA_CH_VALID,
- [1] = 2 | DMA_CH_VALID,
- [2] = 1 | DMA_CH_VALID,
- [3] = 0 | DMA_CH_VALID,
- },
- },
- [DMACH_I2S_IN] = {
- .list = {
- [0] = 1 | DMA_CH_VALID,
- [1] = 2 | DMA_CH_VALID,
- },
- },
- [DMACH_I2S_OUT] = {
- .list = {
- [0] = 2 | DMA_CH_VALID,
- [1] = 1 | DMA_CH_VALID,
- },
- },
- [DMACH_PCM_IN] = {
- .list = {
- [0] = 2 | DMA_CH_VALID,
- [1] = 1 | DMA_CH_VALID,
- ...
- 太多,没贴完。
- */
- struct s3c24xx_dma_order *nord = dma_order;
- if (nord == NULL)//为空的话重分配
- nord = kmalloc(sizeof(struct s3c24xx_dma_order), GFP_KERNEL);
- if (nord == NULL) {
- printk(KERN_ERR "no memory to store dma channel order\n");
- return -ENOMEM;
- }
- dma_order = nord;
- memcpy(nord, ord, sizeof(struct s3c24xx_dma_order));
- return 0;
- }
如果你看懂了上面的代码,下面的plat-samsung中的代码基本不用分析了
- /* linux/arch/arm/plat-samsung/s3c-dma-ops.c
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Samsung S3C-DMA Operations
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/slab.h>
- #include <linux/types.h>
- #include <linux/export.h>
- #include <mach/dma.h>
- struct cb_data {
- void (*fp) (void *);
- void *fp_param;
- unsigned ch;
- struct list_head node;
- };
- static LIST_HEAD(dma_list);
- //s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT);
- //s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK);
- /*
- if (chan->callback_fn != NULL) {
- (chan->callback_fn)(chan, buf->id, buf->size, result);
- }
- */
- static void s3c_dma_cb(struct s3c2410_dma_chan *channel, void *param,
- int size, enum s3c2410_dma_buffresult res)
- {
- struct cb_data *data = param;
- data->fp(data->fp_param);
- }
- static unsigned s3c_dma_request(enum dma_ch dma_ch,
- struct samsung_dma_info *info)
- {
- struct cb_data *data;
- if (s3c2410_dma_request(dma_ch, info->client, NULL) < 0) {
- s3c2410_dma_free(dma_ch, info->client);
- return 0;
- }
- data = kzalloc(sizeof(struct cb_data), GFP_KERNEL);
- data->ch = dma_ch;
- list_add_tail(&data->node, &dma_list);
- s3c2410_dma_devconfig(dma_ch, info->direction, info->fifo);
- if (info->cap == DMA_CYCLIC)
- s3c2410_dma_setflags(dma_ch, S3C2410_DMAF_CIRCULAR);
- s3c2410_dma_config(dma_ch, info->width);
- return (unsigned)dma_ch;
- }
- static int s3c_dma_release(unsigned ch, struct s3c2410_dma_client *client)
- {
- struct cb_data *data;
- list_for_each_entry(data, &dma_list, node)
- if (data->ch == ch)
- break;
- list_del(&data->node);
- s3c2410_dma_free(ch, client);
- kfree(data);
- return 0;
- }
- static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep_info *info)
- {
- struct cb_data *data;
- int len = (info->cap == DMA_CYCLIC) ? info->period : info->len;
- list_for_each_entry(data, &dma_list, node)
- if (data->ch == ch)
- break;
- if (!data->fp) {
- s3c2410_dma_set_buffdone_fn(ch, s3c_dma_cb);
- data->fp = info->fp;
- data->fp_param = info->fp_param;
- }
- s3c2410_dma_enqueue(ch, (void *)data, info->buf, len);
- return 0;
- }
- static inline int s3c_dma_trigger(unsigned ch)
- {
- return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_START);
- }
- static inline int s3c_dma_started(unsigned ch)
- {
- return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STARTED);
- }
- static inline int s3c_dma_flush(unsigned ch)
- {
- return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_FLUSH);
- }
- static inline int s3c_dma_stop(unsigned ch)
- {
- return s3c2410_dma_ctrl(ch, S3C2410_DMAOP_STOP);
- }
- static struct samsung_dma_ops s3c_dma_ops = {
- .request = s3c_dma_request,
- .release = s3c_dma_release,
- .prepare = s3c_dma_prepare,
- .trigger = s3c_dma_trigger,
- .started = s3c_dma_started,
- .flush = s3c_dma_flush,
- .stop = s3c_dma_stop,
- };
- void *s3c_dma_get_ops(void)
- {
- return &s3c_dma_ops;
- }
- EXPORT_SYMBOL(s3c_dma_get_ops);