Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1071381
  • 博文数量: 277
  • 博客积分: 8313
  • 博客等级: 中将
  • 技术积分: 2976
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 11:25
文章分类

全部博文(277)

文章存档

2013年(17)

2012年(66)

2011年(104)

2010年(90)

我的朋友

分类: LINUX

2011-01-26 16:08:20

/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。这里只填了proberemove两个函数,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调用。

先看看这个模块的kconfigmakefile:

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.cusbstring.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这个词就决定了它就绑这个设备上了。别的设备以后就别想了。其实完全可以做成开放的,iMX31endpoint多的是,可以同时绑多个设备吗。

言归正传,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_bufferrequest信息分配buf调用物理层的arcotg_alloc_buffer

req->complete = ep0_complete;

这是把EP0传输结束需要调用的函数传给物理层,如果你进这个函数看看,发现它又调用物理层的endpoint结束处理函数,而这个物理层驱动还没有这个函数。真是气愤,绕这么大一个圈子,原来啥也没干啊。这就是linux让人气愤的一个地方,太喜欢绕圈子了,以显示写代码人的水平高,而前人设计了这么变态的架构,后人只好跟着绕了,越绕越糊涂。

在往下就是再分配两个大bufIN endpoint。如果物理层有电源管理函数的话,接着就该打开电源了。

fsg->thread_task = kthread_create(fsg_main_thread, fsg,

                     "file-storage-gadget");

建一个线程,处理本模块的一切日常事务。fsg_main_thread就是这个模块的核心了。

wake_up_process(fsg->thread_task);叫醒来,开工了,绑定完毕。

回到那个注册函数,下面虽然还有34十行,但只干了一件事情,那就是打开设备。无非就是区分一下是否是OTG,如果不是直接打开,如果是先置成DEVICE模式,然后传个挂起,唤醒的函数给OTG

Arcotg_udc.c再往上看就是物理层的核心了static irqreturn_t arcotg_udc_irq(int irq, void *_udc, struct pt_regs *r)

一进来先关门上锁,读中断状态寄存器,清中断状态寄存器,看看哪出事了(一般出事了才会进来)。

下面就是一排IF语句,这里注意了,可能同时产生几个中断,所有不用switch,用if可以一次把这些中断都处理了。

物理层的驱动需要时时参考datasheet,又作这个的兄弟如果想详细讨论IMX31usb可以再联系我专门讨论,这里略过。

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包,先copylocal_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 TransportUniversal Serial Bus Mass Storage Class CBI Transport

根据协议,如果在bulk-only的情况下,有两个设备类型请求符:0xff0xfe,分别是复位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 驱动个人觉得必须先看懂协议,至少了解基本的东西。

写的不对的地方还望来往过客指正。

阅读(1832) | 评论(1) | 转发(1) |
给主人留下些什么吧!~~

chinaunix网友2011-03-09 13:13:28

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com