`
javatome
  • 浏览: 825031 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是UHCI(11)一个函数引发的故事(二)

 
阅读更多

571行之前全是些初始化的代码,先飘过,用到了再回来看.

571行到582,上次我们看到DEBUG_CONFIGURED是在uhci的初始化代码中,uhci_hcd_init函数中,这是一个编译开关,打开开关就是1,不打开就是0,我们假设打开.因为我们有必要了解一下debugfs的更多,毕竟我们当初已经接触过debugfs.而且当时已经看到函数debugfs_create_dir/sys/kernel/debug下面建立了一个uhci的目录,而现在我们看到的这个debugfs_create_file很自然,就是在/sys/kernel/debug/uhci下面建立文件,比如:

localhost:~ # ls -l /sys/kernel/debug/uhci/

total 0

-rw-r--r-- 1 root root 0 Oct 8 13:18 0000:00:1d.0

-rw-r--r-- 1 root root 0 Oct 8 13:18 0000:00:1d.1

-rw-r--r-- 1 root root 0 Oct 8 13:18 0000:00:1d.2

-rw-r--r-- 1 root root 0 Oct 8 13:18 0000:00:1d.3

可以看见,在这个目录下面针对每个uhci主机控制器各建立了一个文件,文件名就是该设备的pci,即域名+总线名+插槽号+功能号.(domain,bus,device,function)

接下来从584行到628,全都是内存申请相关的,包括可恶的DMA.不过这些函数我们已经不再陌生,dma_alloc_coherent,dma_pool_create都已经讲过,但要说清楚这些实际的代码,则必须借助一张经典的图,来自UHCI spec:

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 414.75pt; HEIGHT: 294pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image001.emz"></imagedata></shape>

这张图叫做调度图.有了这张图,你就可以运筹帷幄之内决胜千里之外.这张图对于我们研究uhci的意义,就好比1993年那部何家劲版的少年张三丰中那张藏宝图,所有的人都想得到它,因为得到了它就意味着得到一切.而对于所有写uhci主机驱动的人来说,他们对于这幅图的共同心声是:输了你,赢了世界又如何?

之所以这幅图如此重要,是因为uhci主机控制器的原理完全集中在这幅图中.

Frame List,有人管它叫框架表,有人管它叫帧链表.我觉得怎么叫都不爽,所以还是用英文吧,就叫它Frame List,Frame我也不翻译过来了,理由是,我在伟大的微软拼音输入法里竟然没有找到这个字.主机控制器最重要的一个职责是调度.那么它如何调度呢?首先你得把这个Frame List的起始地址告诉它,由这个List将会牵出许多的TDQH.

首先Frame List是一个数组,最多1024个元素.每一个元素代表一个frame,时间上来说就是1ms.而和每一个Frame相联系的是transaction,即交易.比如说一次传输,就可以算作一笔交易.TDQH就是用来表征这些交易的.Frame List的每一个元素会包含指针指向TD或者QH,实际上Frame List的每一个元素被称作一个Frame List Pointer,它一共有32bit,其中bit31bit4则表示的是TD或者QH的地址,bit1则表示指向的到底是QH还是TD.

而从硬件上来说,访问这个Frame List的方法是使用一个基址寄存器和一个计数器.即图中我们看到的那个Frame List Base Address RegisterFrame Counter.下面这张图也许更能说明这两个东西的作用.

<shape id="_x0000_i1026" style="WIDTH: 414.75pt; HEIGHT: 299.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:/DOCUME~1/JASON_~1/LOCALS~1/Temp/msohtml1/01/clip_image003.emz"></imagedata></shape>

实际上在主机控制器中有一个时钟,用我们电子专业的术语来说就是主机控制器内部肯定有一个晶体振荡器,从而实现一个周期为1msec的信号,于是Frame List Base Address RegisterFrame Counter就去遍历这张Frame List,它们在这张表里一毫秒前进一格,走到1023了就又再次归零.

那么驱动程序的责任是什么呢?为上面那张调度图准备好相应的数据结构.填充Frame List Pointer,建立并填充TD,建立并填充QH.说了这么些,那么到底什么是TD什么是QH?

来看TD,TD实际上描述的就是最终要在USB总线上传输的数据包,它的学名叫Transaction Descriptor,它是主机控制器的基本执行单位.UHCI spec定义了两类TD,Iso TDnon-ISO TD.即等时TD和非等时TD.我们知道USB一共四种传输方式,中断,批量,控制,等时.这其中等时传输最帅,所以它的TD也会有所不同,虽然从数据结构的格式来说是一样的,但是作用不一样.从这张调度图来看,等时的TD也是专门被列出来了.主机控制器驱动程序负责填充TD,主机控制器将会去获取TD,然后执行相应的数据传输.

然后来看QH,QH就是队列头(Queue Head).从这张调度图里我们也能看到,QH实际上把各个非等时TD给连接了起来,组织成了若干队列.

从图中我们看到一个现象,对主机控制器来说,四种传输方式是有优先级的区别的,等时传输最帅,所以它总是最先被满足,最先被执行,然后是中断传输,再然后才是控制传输和Bulk传输.等时传输和中断传输都叫做周期传输,或者说定期传输.

再强调一下,驱动程序的任务就是填充这张图,然后硬件的作用就是按照这张图去执行,这种分工是很明确的.

Ok,现在让我们结合代码和结构体定义来看看.

首先584,使用dma_alloc_coherent申请内存,赋给uhci->frame,而与此同时建立了dma映射,frame_dma_handle,frame是以后我们从软件方面来指代这个frame list,frame_dma_handle因为是物理地址,我们要让它和真正的硬件联系起来,稍后在一个叫做configure_hc的函数中你会看到,我们会把它写入Frame List的基址寄存器.这样我们以后操作uhci->frame就等于真正的操作硬件了.这更意味着以后我们只要把申请的TD,QH,uhci->frame联系起来就可以了.这里我们也注意到,UHCI_NUMFRAMES就是一个宏,它的值正是1024.到目前为止,一切看起来都那么和谐.

594行这个frame_cpu则是和frame相对应的,它是一个纯粹软件意义上的frame list.frame身上承担着硬件的使命,frame_cpu则属于我们从软件角度来说记录这张frame list.

609行这两个dma_poll_create就是创建内存池,给我们在下面为TDQH申请内存带来方便.

616行调用的这个uhci_alloc_td以及623行调用的uhci_alloc_qh则都是来自drivers/usb/host/uhci-q.c,先看前者,uhci_alloc_td,顺便把它的搭档也一并贴出来.

106 static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)

107 {

108 dma_addr_t dma_handle;

109 struct uhci_td *td;

110

111 td = dma_pool_alloc(uhci->td_pool, GFP_ATOMIC, &dma_handle);

112 if (!td)

113 return NULL;

114

115 td->dma_handle = dma_handle;

116 td->frame = -1;

117

118 INIT_LIST_HEAD(&td->list);

119 INIT_LIST_HEAD(&td->fl_list);

120

121 return td;

122 }

123

124 static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)

125 {

126 if (!list_empty(&td->list)) {

127 dev_warn(uhci_dev(uhci), "td %p still in list!/n", td);

128 WARN_ON(1);

129 }

130 if (!list_empty(&td->fl_list)) {

131 dev_warn(uhci_dev(uhci), "td %p still in fl_list!/n", td);

132 WARN_ON(1);

133 }

134

135 dma_pool_free(uhci->td_pool, td, td->dma_handle);

136 }

这意思很明了.再来看后者,uhci_alloc_qh以及它的搭档uhci_free_qh.

247 static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,

248 struct usb_device *udev, struct usb_host_endpoint *hep)

249 {

250 dma_addr_t dma_handle;

251 struct uhci_qh *qh;

252

253 qh = dma_pool_alloc(uhci->qh_pool, GFP_ATOMIC, &dma_handle);

254 if (!qh)

255 return NULL;

256

257 memset(qh, 0, sizeof(*qh));

258 qh->dma_handle = dma_handle;

259

260 qh->element = UHCI_PTR_TERM;

261 qh->link = UHCI_PTR_TERM;

262

263 INIT_LIST_HEAD(&qh->queue);

264 INIT_LIST_HEAD(&qh->node);

265

266 if (udev) { /* Normal QH */

267 qh->type = hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;

268 if (qh->type != USB_ENDPOINT_XFER_ISOC) {

269 qh->dummy_td = uhci_alloc_td(uhci);

270 if (!qh->dummy_td) {

271 dma_pool_free(uhci->qh_pool, qh, dma_handle);

272 return NULL;

273 }

274 }

275 qh->state = QH_STATE_IDLE;

276 qh->hep = hep;

277 qh->udev = udev;

278 hep->hcpriv = qh;

279

280 if (qh->type == USB_ENDPOINT_XFER_INT ||

281 qh->type == USB_ENDPOINT_XFER_ISOC)

282 qh->load = usb_calc_bus_time(udev->speed,

283 usb_endpoint_dir_in(&hep->desc),

284 qh->type == USB_ENDPOINT_XFER_ISOC,

285 le16_to_cpu(hep->desc.wMaxPacketSize))

286 / 1000 + 1;

287

288 } else { /* Skeleton QH */

289 qh->state = QH_STATE_ACTIVE;

290 qh->type = -1;

291 }

292 return qh;

293 }

294

295 static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)

296 {

297 WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);

298 if (!list_empty(&qh->queue)) {

299 dev_warn(uhci_dev(uhci), "qh %p list not empty!/n", qh);

300 WARN_ON(1);

301 }

302

303 list_del(&qh->node);

304 if (qh->udev) {

305 qh->hep->hcpriv = NULL;

306 if (qh->dummy_td)

307 uhci_free_td(uhci, qh->dummy_td);

308 }

309 dma_pool_free(uhci->qh_pool, qh, qh->dma_handle);

310 }

这个就明显复杂一些了.关于uhci_alloc_qh,它的执行有两条路径,一种是udev有所指,一种是udev为空.我们这里传进来的是NULL,所以暂时可以不看另一种路径,到时候需要看的时候再去看.这么一来就意味着此时此刻我们不需要看266287这些行了.而这意味着这个函数我们现在能看到的就是一些简单的赋值操作而已.但是我们必须理解为什么这里有两种情况,因为这正是uhci-hcd这个驱动的精妙之处.

为了堆砌出那幅调度图,一个很简单的方法是,每次有传输任务,建立一个或者几个TD,urb转换成urb,然后把TD挂入frame list pointer,不就可以了么?朋友,如果生活真的是这么简单,如果世界真的是这么单纯,那么也许现在我依然是一张洁白的宣纸,绝不是现在这张被上海的梅雨湿润过的废纸.
分享到:
评论

相关推荐

    Linux那些事儿之我是UHCI

    写一下UHCI吧,也顺便怀念一下Intel,以及Intel的那几个女同事们,好久没联系了,你们可好? UHCI是 Intel提出来的.虽然离开 Intel一年多了,但我总觉得也许有一天我还会回到 Intel.所 以关于 Intel的东西,我多少会关注...

    Linux那些事儿

    Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 Linux那些事儿之我是Sysfs 今天本人将9个单独的文档整理出来,做成了一...

    Linux那些事儿1-9合集

    由复旦fudan_abc写的,风趣的文笔,深入浅...linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 linux那些事儿之我是Sysfs

    Linux那些事儿系列.rar

    》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...

    linux那些事儿(EHCI Block SCSI Sysfs PCI USB U 盘 UHCI Hub)

    Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Block层.pdf Linux那些事儿之我是SCSI硬盘.pdf Linux那些事儿之我是Sysfs.pdf Linux那些事儿之我是PCI.pdf ...Linux那些事儿之我是UHCI.pdf

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI ...Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘

    linux那些事全集

    Linux那些事儿之我是UHCI Linux那些事儿之我是Sysfs Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是PCI Linux那些事儿之我是Hub Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Block层

    Linux那些事儿.rar

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    Linux那些事儿系列

    本人整理的fudan_abc的专栏中以完结的文章,在此向原作者表示感谢,给...内容包括:linux那些事儿之我是U盘,linux那些事儿之我是USB,linux那些事儿之我是HUB,linux那些事儿之我是UHCI,linux那些事儿之我是Sysfs。

    Linux那些事儿之我是XXX全集.rar

    Linux那些事儿之我是XXX全集 包含USB core U盘 UHCI PCI SCSI硬盘 Block Hub EHCI 。 想学驱动的童鞋,不妨看看。该书主要是进行源代码的分析

    Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI

    Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是PCI.pdf Linux那些事儿之我是SCSI硬盘.pdf 注: 之前有人已经上传了《Linux那些事儿 系列》,其已经包含了:hub,sysfs...

    linux那些事儿之我是USB.zip

    里面包含Linux那些事的九个文档,Block层,ECHI主机控制,HUB,PCI,SCSI硬盘,Sysfs,UHCI,USB+core,U盘等九个文档,内容详细,而且全面都有书签,适合系统学习!

    Linux那些事系列

    Linux那些事儿之我是Hub Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘 Linux那些事之我是HUB1

    微信生活缴费商业项目标准版

    Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是UHCI,...

    LINUX那些事儿 linux经典之作

    然后是usb core(也就是戏说USB),这一阶段是个承上启下的角色,承的是U盘/HUB,启的是UHCI/EHCI,主要描述一个usb设备连接到hub上之后,在HUB和UHCI/EHCI之间也就是usb core里发生的故事. 再然后可以开始看UHCI/EHCI.这...

    Linux那些事儿[完整版]自己整理

    我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内核 戏说USB 说的是2.6.22的内核 我是Hub/UHCI/EHCI 说的是2.6.22.1的内核

    usb那些事的全集

    usb那些事的全集,包含: 1 Linux那些事儿之我是Block层 2 Linux那些事儿之我是EHCI主机控制器 3 Linux那些事儿之我是HUB ...7 Linux那些事儿之我是UHCI 8 Linux那些事儿之我是USB+core 9 Linux那些事儿之我是U盘

    UHCI11D.pdf

    UHCI11D.pdf

    Linux那些事儿(包括Hub, Sysfs, UHCI, USB, U盘5个部分)

    一个很经典的东东,里面的很多东西都很有启发性,值得看看

Global site tag (gtag.js) - Google Analytics