下一個函數,usb_add_hcd,drivers/usb/core/hcd.c中:
1548
1558 int usb_add_hcd(struct usb_hcd *hcd,
1559 unsigned int irqnum, unsigned long irqflags)
1560 {
1561 int retval;
1562 struct usb_device *rhdev;
1563
1564 dev_info(hcd->self.controller, "%s/n", hcd->product_desc);
1565
1566 set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
1567
1568
1572 if ((retval = hcd_buffer_create(hcd)) != 0) {
1573 dev_dbg(hcd->self.controller, "pool alloc failed/n");
1574 return retval;
1575 }
1576
1577 if ((retval = usb_register_bus(&hcd->self)) < 0)
1578 goto err_register_bus;
1579
1580 if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) {
1581 dev_err(hcd->self.controller, "unable to allocate root hub/n");
1582 retval = -ENOMEM;
1583 goto err_allocate_root_hub;
1584 }
1585 rhdev->speed = (hcd->driver->flags & HCD_USB2) ? USB_SPEED_HIGH :
1586 USB_SPEED_FULL;
1587 hcd->self.root_hub = rhdev;
1588
1589
1593 device_init_wakeup(&rhdev->dev, 1);
1594
1595
1598 if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) {
1599 dev_err(hcd->self.controller, "can't setup/n");
1600 goto err_hcd_driver_setup;
1601 }
1602
1603
1604 if (device_can_wakeup(hcd->self.controller)
1605 && device_can_wakeup(&hcd->self.root_hub->dev))
1606 dev_dbg(hcd->self.controller, "supports USB remote wakeup/n");
1607
1608
1609 if (hcd->driver->irq) {
1610 snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
1611 hcd->driver->description, hcd->self.busnum);
1612 if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
1613 hcd->irq_descr, hcd)) != 0) {
1614 dev_err(hcd->self.controller,
1615 "request interrupt %d failed/n", irqnum);
1616 goto err_request_irq;
1617 }
1618 hcd->irq = irqnum;
1619 dev_info(hcd->self.controller, "irq %d, %s 0x%08llx/n", irqnum,
1620 (hcd->driver->flags & HCD_MEMORY) ?
1621 "io mem" : "io base",
1622 (unsigned long long)hcd->rsrc_start);
1623 } else {
1624 hcd->irq = -1;
1625 if (hcd->rsrc_start)
1626 dev_info(hcd->self.controller, "%s 0x%08llx/n",
1627 (hcd->driver->flags & HCD_MEMORY) ?
1628 "io mem" : "io base",
1629 (unsigned long long)hcd->rsrc_start);
1630 }
1631
1632 if ((retval = hcd->driver->start(hcd)) < 0) {
1633 dev_err(hcd->self.controller, "startup error %d/n", retval);
1634 goto err_hcd_driver_start;
1635 }
1636
1637
1638 rhdev->bus_mA = min(500u, hcd->power_budget);
1639 if ((retval = register_root_hub(hcd)) != 0)
1640 goto err_register_root_hub;
1641
1642 if (hcd->uses_new_polling && hcd->poll_rh)
1643 usb_hcd_poll_rh_status(hcd);
1644 return retval;
1645
1646 err_register_root_hub:
1647 hcd->driver->stop(hcd);
1648 err_hcd_driver_start:
1649 if (hcd->irq >= 0)
1650 free_irq(irqnum, hcd);
1651 err_request_irq:
1652 err_hcd_driver_setup:
1653 hcd->self.root_hub = NULL;
1654 usb_put_dev(rhdev);
1655 err_allocate_root_hub:
1656 usb_deregister_bus(&hcd->self);
1657 err_register_bus:
1658 hcd_buffer_destroy(hcd);
1659 return retval;
1660 }
1566行,設定一個flag,至于設了幹嘛用,等遇到了再說.
1572行,hcd_buffer_create,初始化一個buffer池.現在是時候說一說DMA了.我們知道一個USB主機控制器控制着一條USB總線,而USB主機控制器的一項重要工作是什麼呢?在記憶體和USB總線之間傳輸資料.這個過程可以使用DMA或者不使用DMA,不使用DMA的方式即所謂的PIO方式.DMA代表着Direct Memory Access,即直接記憶體通路.那麼使用DMA如何做呢?不需要CPU幹預對吧,記憶體總是需要的吧,我比如說我有一個UHCI控制器,我告訴它,記憶體中某個地方放了一堆資料,你去取吧,然後它就自己去取,取完了它就跟我說一聲,告訴我它取完了.
那麼在整個USB子系統中是如何處理這些事情的呢?好,苦等了這麼久,我終于有機會來向你解釋這個問題了,現在我終于可以說電視劇中男主角對女主角常說的那句話,你聽我解釋,你聽我解釋呀!回去看我們在usb-storage中,在hub driver中,我們調用過一個函數usb_buffer_alloc,當時很多網友問我這個函數究竟是如何處理DMA或不DMA的?
關于DMA,合理的做法是先建立一個記憶體池,然後每次都從池子裡要記憶體.具體說就是先由HCD這邊建池子,然後裝置驅動那邊就直接索取.我們來看代碼,來自drivers/usb/core/buffer.c中的hcd_buffer_create,
40
52 int hcd_buffer_create(struct usb_hcd *hcd)
53 {
54 char name[16];
55 int i, size;
56
57 if (!hcd->self.controller->dma_mask)
58 return 0;
59
60 for (i = 0; i < HCD_BUFFER_POOLS; i++) {
61 if (!(size = pool_max [i]))
62 continue;
63 snprintf(name, sizeof name, "buffer-%d", size);
64 hcd->pool[i] = dma_pool_create(name, hcd->self.controller,
65 size, size, 0);
66 if (!hcd->pool [i]) {
67 hcd_buffer_destroy(hcd);
68 return -ENOMEM;
69 }
70 }
71 return 0;
72 }
别的我們先不說,先看64行,調用了dma_pool_create函數,這個函數就是真正去建立記憶體池的函數,或者更準确地講,建立一個DMA池,核心中定義了一個結構體,struct dma_pool,就是專門代表一個DMA池的,而這個函數的傳回值就是生成的那個DMA池.如果建立失敗就調用hcd_buffer_destroy,還是來自同一個檔案,
75
82 void hcd_buffer_destroy(struct usb_hcd *hcd)
83 {
84 int i;
85
86 for (i = 0; i < HCD_BUFFER_POOLS; i++) {
87 struct dma_pool *pool = hcd->pool[i];
88 if (pool) {
89 dma_pool_destroy(pool);
90 hcd->pool[i] = NULL;
91 }
92 }
93 }
看得出這裡調用的是dma_pool_destroy,其作用不言自明.
那麼建立池子和銷毀池子的函數我們知道了,如何從池子裡索取或者把索取的釋放回去呢?對應的兩個函數分别是,dma_pool_alloc和dma_pool_free,而這兩個函數正是與我們說的usb_buffer_alloc以及usb_buffer_free相聯系的.于是我們來看這兩個函數的代碼,來自drivers/usb/core/usb.c:
568
589 void *usb_buffer_alloc(
590 struct usb_device *dev,
591 size_t size,
592 gfp_t mem_flags,
593 dma_addr_t *dma
594 )
595 {
596 if (!dev || !dev->bus)
597 return NULL;
598 return hcd_buffer_alloc(dev->bus, size, mem_flags, dma);
599 }
600
601
612 void usb_buffer_free(
613 struct usb_device *dev,
614 size_t size,
615 void *addr,
616 dma_addr_t dma
617 )
618 {
619 if (!dev || !dev->bus)
620 return;
621 if (!addr)
622 return;
623 hcd_buffer_free(dev->bus, size, addr, dma);
624 }
很顯然,它們調用的就是hcd_buffer_alloc和hcd_buffer_free,于是進一步跟蹤,來自drivers/usb/core/buffer.c:
96
99
100 void *hcd_buffer_alloc(
101 struct usb_bus *bus,
102 size_t size,
103 gfp_t mem_flags,
104 dma_addr_t *dma
105 )
106 {
107 struct usb_hcd *hcd = bus_to_hcd(bus);
108 int i;
109
110
111 if (!bus->controller->dma_mask) {
112 *dma = ~(dma_addr_t) 0;
113 return kmalloc(size, mem_flags);
114 }
115
116 for (i = 0; i < HCD_BUFFER_POOLS; i++) {
117 if (size <= pool_max [i])
118 return dma_pool_alloc(hcd->pool [i], mem_flags, dma);
119 }
120 return dma_alloc_coherent(hcd->self.controller, size, dma, 0);
121 }
122
123 void hcd_buffer_free(
124 struct usb_bus *bus,
125 size_t size,
126 void *addr,
127 dma_addr_t dma
128 )
129 {
130 struct usb_hcd *hcd = bus_to_hcd(bus);
131 int i;
132
133 if (!addr)
134 return;
135
136 if (!bus->controller->dma_mask) {
137 kfree(addr);
138 return;
139 }
140
141 for (i = 0; i < HCD_BUFFER_POOLS; i++) {
142 if (size <= pool_max [i]) {
143 dma_pool_free(hcd->pool [i], addr, dma);
144 return;
145 }
146 }
147 dma_free_coherent(hcd->self.controller, size, addr, dma);
148 }
看見了吧,最終調用的就是dma_pool_alloc和dma_pool_free.那麼主機控制器到底支援不支援DMA操作呢?看見上面這個dma_mask了麼?預設情況下,dma_mask在總線枚舉的時候被函數pci_scan_device中設定為了0xffffffff.struct device結構體有一個成員u64 *dma_mask,如果一個PCI裝置不能支援DMA,那麼應該在probe函數中調用pci_set_dma_mask把這個dma_mask設定為NULL.不過一個沒有精神分裂症的PCI裝置通常是支援DMA的.這個掩碼更多的作用是,比如你的裝置隻能支援24位的尋址,那你就得通過設定dma_mask來告訴PCI層,你需要把dma_mask設定為0x00ffffff.因為标準的PCI裝置都是32位的尋址的,是以标準情況就是設定的0xffffffff.不過開發者們的建議是不要直接使用這些數字,而是使用它們定義在include/linux/dma-mapping.h中的這些宏:
16 #define DMA_64BIT_MASK 0xffffffffffffffffULL
17 #define DMA_48BIT_MASK 0x0000ffffffffffffULL
18 #define DMA_40BIT_MASK 0x000000ffffffffffULL
19 #define DMA_39BIT_MASK 0x0000007fffffffffULL
20 #define DMA_32BIT_MASK 0x00000000ffffffffULL
21 #define DMA_31BIT_MASK 0x000000007fffffffULL
22 #define DMA_30BIT_MASK 0x000000003fffffffULL
23 #define DMA_29BIT_MASK 0x000000001fffffffULL
24 #define DMA_28BIT_MASK 0x000000000fffffffULL
25 #define DMA_24BIT_MASK 0x0000000000ffffffULL
不過目前在drivers/usb/目錄下面沒有哪個驅動會調用pci_set_dma_mask,因為現代總線上的大部分裝置都能夠處理32位位址,換句話說大家的裝置都還算是正經,但如果你們家生産出來一個不倫不類的裝置那麼你就别忘了在probe階段用pci_set_dma_mask設定一下,否則你就甭指望裝置能夠正确的進行DMA傳輸.關于這個函數的使用,可以參考drivers/net下面的那些驅動,很多網卡驅動都調用了這個函數,雖然其中很多其實就是設定32位.
要不,總結一下?以上這幾個DMA函數我們就不細講了.但需要對某些地方單獨拿出來講.
第一,hcd_buffer_alloc函數中,111行,判斷,如果dma_mask為NULL,說明這個主機控制器不支援DMA,那麼使用原始的方法申請記憶體,即kmalloc.然後申請好了就直接傳回,而不會繼續去執行下面的那個dma_pool_alloc函數.同樣的判斷在hcd_buffer_free中也是一樣的,沒有DMA的就直接調用kfree釋放記憶體,而不需要調用dma_pool_free了.
第二,你應該注意到這裡還有另外兩個函數我們根本沒提起, dma_alloc_coherent和dma_free_coherent,這兩個函數也是用來申請DMA記憶體的,但是它們适合申請比較大的記憶體,比如N個page的那種,而DMA池的作用本來就是提供給小打小鬧式的記憶體申請的.目前的USB子系統裡在drivers/usb/core/buffer.c中定義了一個數組:
25
26 static const size_t pool_max [HCD_BUFFER_POOLS] = {
27
30 32,
31 128,
32 512,
33 PAGE_SIZE / 2
34
35 };
HCD_BUFFER_POOLS這個宏的值為4.結合hcd_buffer_alloc函數裡面那個循環來看,可以知道,你要是申請個32個位元組以内,128個位元組以内,512個位元組以内,或者最多二分之一個PAGE_SIZE以内的,就直接使用這個記憶體池了,否則的話,就得用那個dma_alloc_coherent了.釋放的時候也一樣.
第三,struct usb_hcd結構體有這麼一個成員,struct dma_pool *pool [HCD_BUFFER_POOLS], 這個數組的值是在hcd_buffer_create中賦上的,即當時以這個pool_max為模型建立了4個池子,是以hcd_buffer_alloc裡就可以這樣用.
第四,至于像dma_pool_create/dma_pool_alloc/dma_alloc_coherent這些函數具體怎麼實作的我想任何一個寫裝置驅動程式的都不用關心吧,倒是我有兩個同僚會比較感興趣,因為他們是研究記憶體管理的.
Ok,講完了hcd_buffer_create讓我們還是回到usb_add_hcd中來,繼續往下走.