Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1189926
  • 博文数量: 221
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2139
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-27 19:53
个人简介

JustForFun

文章分类

全部博文(221)

文章存档

2024年(6)

2023年(8)

2022年(2)

2021年(2)

2020年(29)

2019年(11)

2018年(23)

2017年(41)

2016年(76)

2015年(23)

我的朋友
最近访客

分类: LINUX

2017-10-26 14:56:41

原文http://blog.chinaunix.net/uid-20754930-id-1877584.html
/drivers/usb/gadget/arcotg_udc.c (refer to freescale iMX31 datasheet)
module_init, module_exit注册和注销模块就不多说了。
从platform driver说起:
static struct platform_driver udc_driver = {
       .probe = arcotg_udc_probe,
       .remove = __exit_p(arcotg_udc_remove),
       .driver     = {
              .name = driver_name,
       },
};
platform driver遵循linux系统的driver model,原理是很大的一块,可以参考相关资料。这里只说USB。这里只填了probe和remove两个函数,remove还不易定有(__exit_p就是不一定有的意思)。
static int __init arcotg_udc_probe(struct platform_device *pdev)
具体代码参考原文件。
先初始化平台相关设备      
if (board_init(&pdev->dev) != 0)
              return -EINVAL;
在board_init中得到配置指针,config = (struct arc_usb_config *)dev->platform_data;
       if (config->platform_init() != 0)
              return -EINVAL;
初始化平台。具体干的事情就是注释的那样:check the clock, grab/config pins, etc.
在具体呢,可以去arch\arm\mach*下去找相关的平台设备,根据开发板的不同,这个肯定是各有千秋了,但做的事情逃不过check the clock, grab/config pins, etc。
       /* Initialize the udc structure including QH member and other member */
       udc_controller = (struct arcotg_udc *)struct_udc_setup(pdev);
初始化一些控制用的资源,就是开辟写队列空间,DMA空间之类的。
udc_controller->transceiver = otg_get_transceiver();
我们假设用OTG那么上面这个函数得到了OTG传输相关的数据结构。
Probe中还要得到resource,做io地址映射,以便访问寄存器,得到中断号,申请中断。
tmp_status = request_irq(pdev->resource[1].start, arcotg_udc_irq,
                             SA_SHIRQ, driver_name, udc_controller);
这里申请的中断处理函数是arcotg_udc_irq,这是这个驱动的核心,该USB device驱动的正常工作都围绕它展开。
dr_controller_setup
这个函数得参考以下iMX31的文档了,都是与寄存器相关的设置。
ep0_dr_and_qh_setup
struct_ep_setup
初始化各个端点,准备传输用的空间,联表,同样需要参考芯片的datasheet。
所有的释放,注销相关的函数不做介绍了,这是常识吗,不用的东西当然要还给系统了。
继续往上看,如果调试相关的也略过。那么就该:
/*----------------------------------------------------------------*
 * tell the controller driver about gadget layer driver
 * The driver's bind function will be called to bind it to a gadget.
 * @driver: for example fsg_driver from file_storage.c
*----------------------------------------------------------------*/
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
注释的很清楚,usb设备最终会映射成一个具体的设备,那么互通有无的函数就是它了。这里以mass storage为例,那么该函数会被/drivers/usb/gadget/file_storage.c调用。
先看看这个模块的kconfig和makefile:
config USB_FILE_STORAGE
       tristate "File-backed Storage Gadget"
       help
         The File-backed Storage Gadget acts as a USB Mass Storage
         disk drive.  As its storage repository it can use a regular
         file or a block device (in much the same way as the "loop"
         device driver), specified as a module parameter.
 
         Say "y" to link the driver statically, or "m" to build a
         dynamically linked module called "g_file_storage".
Makefile:
g_file_storage-objs        := file_storage.o usbstring.o config.o \
                                   epautoconf.o
obj-$(CONFIG_USB_FILE_STORAGE)     += g_file_storage.o
这说明这个模块由file_storage.c,usbstring.c, config.c, epautoconf.c,编译连接而成,当然主要是file_storage.c,模块的初始化同样是分配了模块重要数据结构的空间,然后就调用了usb_gadget_register_driver。并传了一个初始化相关的数据结构。
现在看看这个函数:
先锁起来,防止别人改写udc_controller->driver。
spin_lock_irqsave(&udc_controller->lock, flags);
获得传入的数据结构后调用retval = driver->bind(&udc_controller->gadget);
调用的就是file_storage.c中的static int __init fsg_bind(struct usb_gadget *gadget)
说句题外话,这个驱动结构个人感觉不好,bind这个词就决定了它就绑这个设备上了。别的设备以后就别想了。其实完全可以做成开放的,iMX31的endpoint多的是,可以同时绑多个设备吗。
言归正传,bind传入的数据结构是物理层的操作函数以及各种信息状态。这样大家互相知己知彼了。下面看bind()
略过系统相关的初始化直接到该逻辑设备相关的部分。从usb_ep_autoconfig_reset(gadget);开始,复位所有endpoint的配置。
usb_ep_autoconfig(gadget, &fs_bulk_in_desc);
usb_ep_autoconfig(gadget, &fs_bulk_out_desc);
usb_ep_autoconfig(gadget, &fs_intr_in_desc);
配置endpoint.一个BULK in, 一个BULK out ,和一个INT in(测试模式需要配置,一般不用) .
接着填充设备描述符,接口描述符,配置描述符等。
fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL);
为控制端点分配request空间。这个函数又调回到物理层的arcotg_alloc_request;
接着usb_ep_alloc_buffer为request信息分配buf调用物理层的arcotg_alloc_buffer;
req->complete = ep0_complete;
这是把EP0传输结束需要调用的函数传给物理层,如果你进这个函数看看,发现它又调用物理层的endpoint结束处理函数,而这个物理层驱动还没有这个函数。真是气愤,绕这么大一个圈子,原来啥也没干啊。这就是linux让人气愤的一个地方,太喜欢绕圈子了,以显示写代码人的水平高,而前人设计了这么变态的架构,后人只好跟着绕了,越绕越糊涂。
在往下就是再分配两个大buf为IN endpoint。如果物理层有电源管理函数的话,接着就该打开电源了。
fsg->thread_task = kthread_create(fsg_main_thread, fsg,
                     "file-storage-gadget");
建一个线程,处理本模块的一切日常事务。fsg_main_thread就是这个模块的核心了。
wake_up_process(fsg->thread_task);叫醒来,开工了,绑定完毕。
回到那个注册函数,下面虽然还有3,4十行,但只干了一件事情,那就是打开设备。无非就是区分一下是否是OTG,如果不是直接打开,如果是先置成DEVICE模式,然后传个挂起,唤醒的函数给OTG。
Arcotg_udc.c再往上看就是物理层的核心了static irqreturn_t arcotg_udc_irq(int irq, void *_udc, struct pt_regs *r);
一进来先关门上锁,读中断状态寄存器,清中断状态寄存器,看看哪出事了(一般出事了才会进来)。
下面就是一排IF语句,这里注意了,可能同时产生几个中断,所有不用switch,用if可以一次把这些中断都处理了。
物理层的驱动需要时时参考datasheet,又作这个的兄弟如果想详细讨论IMX31的usb可以再联系我专门讨论,这里略过。
if (irq_src & USB_STS_INT),如果出现这个中断,说明有某一个endpoint收到了数据,或者发送数据完毕了。碰到这种情况,肯定是要区分对待,而且肯定是EP0最重要了。果然:
              if (usb_slave_regs->endptsetupstat & cpu_to_le32(EP_SETUP_STATUS_EP0)) {
                     tripwire_handler(udc, 0,
                                    (u8 *) (&udc->local_setup_buff));
                     setup_received_irq(udc, &udc->local_setup_buff);
                     status = IRQ_HANDLED;
              }
如果是EP0收到setup包,先copy到local_setup_buff,然后进入setup_received_irq做处理,处理的原则是能本地处理本地处理,实在处理不了上报逻辑层。本地处理就不多说了,无非不过就是USB协议第九章的那些描述符发来发去的,本地处理的是部分的标准设备请求,上报用的函数是:(处理标准设备请求和设备类型请求)
if (udc->driver->setup(&udc->gadget, &udc->local_setup_buff)
这个函数最后调用的是file_storage.c中的static int fsg_setup(struct usb_gadget *gadget,
              const struct usb_ctrlrequest *ctrl);
这里无论哪层处理都把设备请求(request)分成两个类型,标准USB请求和类型请求,其实还有厂商请求,这里没做处理,如果你需要的话要在这里补充,freescale写的驱动不负责,这个地方提都没提。至少应该写行注释吗。
fsg_setup同样把请求分成两类分别交给class_setup_req(fsg, ctrl);和standard_setup_req(fsg, ctrl)处理。
先看设备类型处理函数:又根据设备是bulk only transport还是CBI transport设备分别处理,协议请参考:Universal Serial Bus Mass Storage Class Bulk-Only Transport和Universal Serial Bus Mass Storage Class CBI Transport。
根据协议,如果在bulk-only的情况下,有两个设备类型请求符:0xff,0xfe,分别是复位mass storage设备和相关接口,获得最大的逻辑块号;如果是CBI,则需要支持0x00这个设备请求描述符。
standard_setup_req:处理一些需要逻辑层处理的描述符,具体每个描述符的意思请参考USB协议的第九章。
都处理完了,需要返回的结果通过rc = ep0_queue(fsg)排在队列里,等待发给HOST。这个函数最终又调用了物理层的arcotg_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)。该函数的实现与芯片的usb实现是精密相关的,这里不作详细分析了,它作的事情就是如果有需要发送的包就排在该endpiont的队列里,接收的endpiont则准备一个接收队列,随时可以收到数据。
终于可以回到中断处理函数往下说了,该下面这个了:             
/* completion of dtd */
              if (usb_slave_regs->endptcomplete) {
                     dtd_complete_irq(udc);
                     status = IRQ_HANDLED;
              }
从解释可以看出这个中断说明有事请做完了,进dtd_complete_irq看看,先通过分析寄存器得到是哪个EP做完了接收还是发送的任务,根据相应的原则处理一下(参考芯片datasheet),
其中EP0比较特殊,得特别对待一下,专门搞一个ep0_req_complete处理,里面就是碰到了地址改变就设置一下,状态改变记录一下。最后需要调用一个done函数。里面值得一提的是调用了  /* this complete() should a func implemented by gadget layer,
        * eg fsg->bulk_in_complete() */
       if (req->req.complete) {
              VDBG("calling gadget's complete()  req=0x%p", req);
              req->req.complete(&ep->ep, &req->req);
              VDBG("back from gadget's complete()");
       }
这个req->req.complete(&ep->ep, &req->req)就又回到了逻辑层了,根据不同的EP对应的分别是ep0_complete(啥也没干);bulk_in_complete; bulk_out_complete; intr_in_complete。
这几个函数都回县flush fifo,但物理层不需要,所以就跳过。接着smp_wmb(),这个函数设置一个写屏障,保证写操作的顺序,接着各个函数根据实际情况改变状态后唤醒逻辑层的核心线程。这个线程先不表,还回到物理层的中断处理函数,发现最重要的中断已经没了,剩下的中断有速度改变,复位,挂起,恢复都是纯粹的物理层的事,就不分析了,还有一个需要提醒的,要想通过USB标准测试,需要支持USB测试接口,这个驱动没有支持,大家自己写的时候应该支持上。
物理层的应该没了,现在看看逻辑层的那个线程吧。
       /* Allow the thread to be killed by a signal, but set the signal mask
        * to block everything but INT, TERM, KILL, and USR1. */
       siginitsetinv(&fsg->thread_signal_mask, sigmask(SIGINT) |
                     sigmask(SIGTERM) | sigmask(SIGKILL) |
                     sigmask(SIGUSR1));
       sigprocmask(SIG_SETMASK, &fsg->thread_signal_mask, NULL);
 
       /* Arrange for userspace references to be interpreted as kernel
        * pointers.  That way we can pass a kernel pointer to a routine
        * that expects a __user pointer and it will work okay. */
       set_fs(get_ds());
这几句看了看注释,一两句解释不清楚,linux信号相关的东西,可以参考内核注解那篇文章。往下就是主循环了
/* The main loop */
       while (fsg->state != FSG_STATE_TERMINATED) {……}
FSG_STATE_TERMINATED这个状态只有在bind函数失败时才有,所以说只要bind过了,这种可能性就没了,说白了就是出不去的循环了。
抛开点灯灭灯不说,先查一下是不是有异常或者有人发什么信号了,如果有处理之:
这个异常处理函数先处理系统发来的信号,具体会有什么东东就不太懂了。接着清理一些buf,如果有中断端点忙得话,其他端点都得放下手头的活候着。然后检查一下状态,并作相应的处理。回想一下先前看过的代码,在好多地方看到过raise_exception,尤其是setup时,进去看看发现就是设置了一个新状态,然后发了个信号给这个模块的主线程。所以才有处理异常的时候检查状态并作相应的处理。
接着       if (!fsg->running) {
                     sleep_thread(fsg);
                     continue;
              }
如果设备都不run了,线程当然要睡了
get_next_command,获得命令,usb如果收到的命令,把它放到fsg->cmnd中,并且根据命令给fsg->data_dir赋值。
if (do_scsi_command(fsg) || finish_reply(fsg))
处理命令和请求。具体请参考协议。
最后发送设备的状态send_status。
代码分析就到这里了。可能表述的不是很清楚,再画个图吧。
 
  
                                   图一

其实结构很简单,只有两层,物理层和逻辑层。两层都有接口开放给对方,一加载就开始互通有无,物理层以中断驱动动整个设备,直接和物理设备交互,如果接到自己可以处理的信息就直接处理,接到无法处理的通过逻辑层提供的接口通知逻辑层,逻辑层用一个线程监控整个运行过程,如果没事就先睡会,有事立马起来分析处理,处理后一般还需要把结果通过物理层发送出去,如果物理层完成了任务在通知逻辑层。结构比WINCE的复杂些,但功能好像比CE的弱些。没有CE的结构清晰。写USB mass storage 驱动个人觉得必须先看懂协议,至少了解基本的东西。
写的不对的地方还望来往过客指正。
阅读(1492) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~