通用串行總線(USB)是主機和外圍設備之間的一種連接。USB總線規范有1.1版和2.0版,當然現在已經有了3.0版本。USB1.1支持兩種傳輸速度:低速為1.5Mbps,高速為12Mbps。USB2.0的傳輸速度可以高達480Mbps。USB2.0向下兼容USB1.1,可以將USB1.1的設備連接到USB2.0控制器上,也可以把USB2.0的設備連接到USB1.1控制器上。S3C2440的USB主機控制器支持USB1.1總線規范。
USB總線的拓撲結構如下圖所示:USB主機控制器(USB Host Controller)通過根集線器(Root Hub)與其他USB設備相連。集線器也屬于USB設備,通過它可以在一個USB接口上擴展出多個接口。除根集線器外,最多可以層疊5個根集線器。一條USB主線上最多可以外接127個設備,當然包括根集線器和其他集線器。整個結構圖是一個星型結構,一條USB總線上所有設備共享一條通往主機的數據通道,同一時刻只能有一個設備與主機通訊
通過USB主機控制器來管理外接的USB設備。USB控制器實際上由USB控制器硬件+USB控制器軟件組成的。USB主機控制器分為3種:UHCI、OHCI、EHCI。UHCI與OHCI支持USB1.1協議;EHCI支持USB2.0協議。UHCI,它的硬件比較簡單,所以軟件相對就比較復雜;而OHCI,它的硬件具備更多性能,相反的軟件做的事情就比較少。S3C2440的USB主機控制器支持OHCI主機控制器。HCI的英文全稱為(Host Controller Interface)。
根據以上知識可以知道其實USB驅動程序可以分為兩類:USB主機控制器驅動程序(Host Controller Drivers)、USB設備驅動程序(USB device drivers)。它們在內核中的層次如下圖所示
USB主機控制器驅動程序提供訪問USB設備的接口,它只是一個數據通道,至于這些數據有什么作用,這要靠上層的USB設備驅動程序來解釋。USB設備驅動程序使用下層的驅動提供的數據接口來訪問USB設備,不需要關心具體的傳輸細節。在Linux2.6中,USB主機控制器驅動程序已經幫我們寫好了,下面就簡單分析一下USB主機控制器驅動程序。
1、在分析USB主機控制器驅動程序之前先來看幾個問題:
當把你的安卓手機通過USB接口接到電腦上的USB口時,會有如下現象
1、右下角彈出“發現andrido phone”。
2、跳出一個對話框,提示你安裝驅動程序。
那么問題便來了
問題1、既然還沒有“驅動程序”,為何能知道是“andrido phone”
答1:windows里已經有了USB的總線設備驅動程序,接入USB設備后,是“總線驅動程序”知道你是“andrido phone”,提示你安裝的是“設備驅動程序”,USB總線驅動程序負責:識別USB設備,給USB設備找到對應的驅動程序
問題2、USB設備種類非常多,為什么一接入電腦就能識別出來?
答2:PC和USB設備都得遵守一些規范。
比如:USB設備接入電腦后,PC會發出“你是什么?”USB設備必須回答“我是XXX”,并且回答的語言必須是中文;
USB總線驅動程序會發出某些命令獲取設備信息(描述符),USB設備必須返回“描述符”給PC。
問題3、PC機上接有非常多的USB設備,怎么分辨它們?
答3:接在USB總線上的每個USB設備都有自己的編號(地址),每一個USB設備接入PC時,USB總線驅動程序都會給它分配一個編號,PC機想訪問某個USB設備時,要先發出的命令都含有對方的編號(地址)
問題4、USB設備剛接入PC時,還沒有編號,那么PC怎么把“分配的編號”告訴它?
答4:新接入的USB設備的默認編號是0,在未分配新的編號前,PC使用0編號和它通訊。
問題5、為什么一接入USB設備,PC機就能發現它(又沒有中斷)
答5:USB接口只有4條線:5V、GND、D-、D+。PC的USB口內部D-和D+接有15K的下拉電阻,未接USB設備時為低電平;USB設備的USB口內部,D-或D+接有1.5k的上拉電阻:它一接入就會把PC USB口D-(全速)或D+(高速)拉高,從硬件的角度通知PC有新設備接入。
從以上問答可以看出來USB總線驅動程序的作用可以總結為:(后面會再詳細說明)
1、識別設備
2、找到并安裝對應的USB設備驅動
3、提供USB讀寫函數(不了解數據含義)
2、接著再提出一些USB的概念
1、USB是主從結構的
所有的USB傳輸都是從USB主機這方發起,USB設備沒有主動通知USB主機的能力
例子:USB鼠標滑動一下,立刻產生數據,但是它沒有能力通知PC讀數據,只能被動的等待PC機被讀
2、USB的傳輸類型:
a、控制傳輸:可靠,時間有保證,比如USB設備的識別過程
b、批量傳輸:可靠,時間沒有保證,比如:U盤
c、中斷傳輸(還是查詢方式):可靠,實時,比如:USB鼠標
d、實時傳輸:數據不可靠,實時,比如:USB攝像頭
3、USB傳輸的對象:端點(endpoint)
我們說“讀U盤”、“寫U盤”可以細化為:把數據寫到U盤的端點1,把數據從U盤的端點2里讀出數據
除了端點0之外,每一個端點只支持一個方向的數據傳輸
端點0用于控制傳輸,既能輸出也能輸入
4、每一個端點都只有一個傳輸類型,傳輸方向
5、術語里、程序里說的輸入(IN)、輸出(OUT)“都是”基于USB主機的立場說的。
比如鼠標的數據是從鼠標傳入PC機的,對應的端點稱為“輸入端點”
3、通過前面的描述對于USB已經有了大致的了解了,接著通過程序分析來更深入的了解USB設備的識別過程:
把一個USB鼠標接到開發板上會彈出如下信息:
usb 1-1: new full speed USB device using s3c2410-ohci and address 3
usb 1-1: configuration #1 chosen from 1 choice
scsi1 : SCSI emulation for USB Mass Storage devices
拔掉
usb 1-1: USB disconnect, address 3
再接上
usb 1-1: USB disconnect, address 3usb 1-1: new full speed USB device using s3c2410-ohci and address 4
usb 1-1: configuration #1 chosen from 1 choice
scsi2 : SCSI emulation for USB Mass Storage devices
其中address3,address4就是USB核心程序給USB設備分配的地址。查找“USB device using ”關鍵字,在drivers/usb/core/hub.c的2186行找到了這句話:'%s %s speed %sUSB device using %s and address %dn'。它位于hub_port_init函數下,層層查找可以發現最終是hub_irq函數導致了hub_port_init被調用。hub_irq不是一個真正的中斷處理程序,它只是集線器USB設備的數據接收完成后的回調函數,其實drivers/usb/core/hub.c也不過是一個USB設備驅動程序而已。
drivers/usb/core/hub.c程序先放到一邊,待會再來分析它。先嘗試找一下USB控制器的驅動程序,在driversusbhostOhci-s3c2410.c找到了這個驅動程序, 它以平臺設備驅動程序為框架,driversusbhostOhci-s3c2410.c屬于driver層
static struct platform_driver ohci_hcd_s3c2410_driver = {
.probe = ohci_hcd_s3c2410_drv_probe,//一旦匹配devices則被調用
.remove = ohci_hcd_s3c2410_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_s3c2410_drv_suspend, */
/*.resume = ohci_hcd_s3c2410_drv_resume, */
.driver = {
.owner = THIS_MODULE,
.name = 's3c2410-ohci',
},
};
接著搜索“s3c2410-ohci”,找到了devices層,位于archarmplat-s3c24xxDevs.c
struct platform_device s3c_device_usb = {
.name = 's3c2410-ohci',
.id = -1,
.num_resources = ARRAY_SIZE(s3c_usb_resource),
.resource = s3c_usb_resource,
.dev = {
.dma_mask = &s3c_device_usb_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
到這里我們還沒有找到插上USB設備后第一個運行的函數,我們猜測這個函數必定是一個中斷,USB控制器接收到數據后將會產生一個中斷給MCU,然后MCU再進行處理。現在我們就是要找到這一個中斷函數,在drivers/usb/core/hub.c中找到了usb_add_hcd函數,它位于usb_hcd_s3c2410_probe函數下,這個函數一旦有USB控制器設備匹配就會被調用。
static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
struct platform_device *dev)
{
...
...
retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED);//注冊OHCI控制器中斷
if (retval != 0)
goto err_ioremap;
...
...
}
繼續往下看usb_add_hcd函數,它位于driversusbcorehcd.c下
int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
...
...
if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
hcd->irq_descr, hcd)) != 0) {//中斷USB中斷
dev_err(hcd->self.controller,
'request interrupt %d failedn', irqnum);
goto err_request_irq;
}
hcd->irq = irqnum;
...
...
}
在這里看到了調用注冊中斷函數,并且它的中斷處理回調函數為usb_hcd_irq,它同樣位于driversusbcorehcd.c下
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
int start = hcd->state;
if (unlikely(start == HC_STATE_HALT ||
!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
return IRQ_NONE;
if (hcd->driver->irq (hcd) == IRQ_NONE)
return IRQ_NONE;
set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);//設置中斷標志
if (unlikely(hcd->state == HC_STATE_HALT))
usb_hc_died (hcd);
return IRQ_HANDLED;
}
這樣,通過usb_hcd_irq 函數的作用,最終將會調用到drivers/usb/core/hub.c下的hub_irq函數。之前說過drivers/usb/core/hub.c其實也是一個USB設備驅動程序,它其實是一個根集線器驅動程序。這里直接列出匹配USB設備驅動程序的過程。
hub_irq
kick_khubd
hub_thread
hub_events
hub_port_connect_change
usb_alloc_dev(hdev, hdev->bus, port1);
dev->dev.type = &usb_device_type;
choose_address(udev); //給新設備分配編號(地址)
hub_port_init //usb 1-1: new full speed USB device using s3c2410-ohci and address 3
hub_set_address //把編號(地址)告訴USB設備
usb_get_device_descriptor(udev, 8);//獲取設備描述符
usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
usb_new_device(udev);
usb_get_configuration(udev);//把所有的描述符都讀出來并解析
usb_parse_configuration
device_add//把device放入usb_bus_type的dev鏈表,
//從usb_bus_type的driver鏈表中取出usb_driver
//把usb_interface和usb_driver的id_table比較
//如果能匹配,調用usb_driver的probe函數
大致總結一下:
1、當接上USB鼠標后,USB主機控制器檢查到D-或D+接口變高知道了有設備接入。
2、USB主機控制器把新分配的編號告訴USB設備,這時候還是以0地址通訊的。
3、USB主機控制器以0地址從USB設備獲得設備描述符的前8個字節,從這8個字節可以知道后面所有設備描述符總共的大小
4、知道了剩下設備描述符的大小,就可以將剩余的設備描述符都讀出來
5、接著把device放入usb_bus_type的dev鏈表
6、從usb_bus_type的driver鏈表中取出usb_driver
7、把usb_interface和usb_driver的id_table比較(對于USB鼠標最終比較的是接口描述符下的bInterfaceClass;bInterfaceSubClass;bInterfaceProtocol;三個信息,中文翻譯為設備類型,設備子類型,設備協議)
8、如果能匹配,調用usb_driver的probe函數
從上面總結可以看出,要寫USB設備的驅動程序就需要對usb_driver結構體進行初始。
USB各個描述符的關系如下,具體描述符結構將在下一節講解
以上就是USB總線驅動程序的框架,USB設備是一個非常復雜的東西,這里只是簡單的描述,并未深入理解。目的是為了編寫USB設備驅動程序。
上一篇:Linux驅動之LCD驅動編寫
下一篇:利用Crosstool-ng制作交叉編譯工具鏈
推薦閱讀最新更新時間:2025-03-25 01:09




設計資源 培訓 開發板 精華推薦
- STC8H1K08開發板
- DC1562B-H,用于 LTC6993-4 的演示板,100 ms,可重新觸發,下降沿,正輸出脈沖,單次觸發
- 2018電賽F題-無線話筒擴音系統(哈爾濱工程大學)
- 包租公2.4G小車底盤
- 【未迭代】PCB地鐵卡片
- STEVAL-EFUSE01,基于 STEF01 完全可編程通用電子保險絲的評估板
- AD9204-65EBZ,用于 AD9204BCPZ-65、2 通道、10 位、65 MSPS 模數轉換器的評估板
- 使用 Analog Devices 的 LT4276AHUFD 的參考設計
- FRDM-HB2000FEVM: FRDM-HB2000FEVM: Freedom Platform for MC33HB2000, HQFN32, 10 A H-Bridge, Programmable Brushed DC Motor Driver
- 使用 Microchip Technology 的 LX1676 的參考設計