Linux內(nèi)核USB主設(shè)備驅(qū)動(dòng)程序
目錄 [隱藏] 1 ehci-hcd控制器 1.1 EHCI構(gòu)架介紹1.2 EHCI驅(qū)動(dòng)程序分析2 Mass Storage主機(jī)驅(qū)動(dòng)程序 2.1 Mass Storage規(guī)范介紹2.2 Bulk-Only傳輸協(xié)議介紹2.3 SCSI命令描述塊結(jié)構(gòu)2.4 Mass Storage設(shè)備對(duì)象結(jié)構(gòu)2.5 Mass Storage設(shè)備初始化2.6 探測(cè)函數(shù)storage_probe分析 ehci-hcd控制器 EHCI構(gòu)架介紹
USB主控器規(guī)范包括USB1.1主控器規(guī)范和USB2.0主控器規(guī)范。USB1.1主控器規(guī)范有包括UHCI(Universal Host Controller Interface)和OHCI(Open Host Controller Interface Specification);USB2.0主控器規(guī)范為EHCI(Enhanced Host Controller Interface Specification)。UHCI和OHCI在硬件實(shí)現(xiàn)以及對(duì)底層軟件訪問上都有所不同,但二者又都完全USB 1.1中對(duì)主控制器的要求。
USB主控制器驅(qū)動(dòng)程序的整個(gè)系統(tǒng)框架圖如圖4所示,從圖中可以看出USB驅(qū)動(dòng)程序包括客戶驅(qū)動(dòng)、通用總線驅(qū)動(dòng)程序、EHCI驅(qū)動(dòng)程序等組 成。其中,客戶驅(qū)動(dòng)程序是特定USB設(shè)備的驅(qū)動(dòng)程序,提供了USB設(shè)備的功能操作及特定子類協(xié)議封裝;USB驅(qū)動(dòng)程序(USBD)是特定操作系統(tǒng)上抽象出 的主機(jī)控制器驅(qū)動(dòng)程序共有特性,對(duì)應(yīng)于Linux USB驅(qū)動(dòng)程序的HCD層;EHCI控制器驅(qū)動(dòng)程序(EHCD)是依賴于特定硬件寄存器接口定義的主控制器驅(qū)動(dòng)程序。USB設(shè)備是執(zhí)行終端用戶功能硬件設(shè) 備。
EHCI通用構(gòu)架如圖5所示。每個(gè)EHCI接口定義了三個(gè)接口空間,該個(gè)接口空間說明如下:
PCI配置空間包括PCI寄存器,它們用來系統(tǒng)部件枚舉和PCI電源管理。 寄存器空間,通常稱為I/O空間。它必須被用作內(nèi)存映射I/O空間。它包括特定應(yīng)用參數(shù)寄存器和能力寄存器、加上可選的控制和狀態(tài)寄存器。 調(diào)度接口空間是特殊分配的內(nèi)存并且被EHCI驅(qū)動(dòng)程序管理用來周期性或異步調(diào)度。EHCI支持兩種類型傳輸:異步類型和周期類型。周期類型包括同步傳輸和中斷傳輸,異步類型包括控制傳輸和批量傳輸。EHCI調(diào)度接口給兩種類型提 供了分開的調(diào)度。周期調(diào)度基于時(shí)間發(fā)起的幀鏈表,它代表主機(jī)控制器工作條目的滑動(dòng)窗口。所有的同步和中斷傳輸都通過周期調(diào)度來進(jìn)行。
異步調(diào)度是簡(jiǎn)單的調(diào)度工作條目的循環(huán)鏈表,它給所有異步傳輸提供了循環(huán)調(diào)度服務(wù)。
EHCI使用一個(gè)簡(jiǎn)單的buffer隊(duì)列數(shù)據(jù)結(jié)構(gòu)來管理所有的中斷、批量和控制傳輸類型。排隊(duì)的數(shù)據(jù)結(jié)構(gòu)提供了自動(dòng)的、排序的數(shù)據(jù)傳輸流。軟件能異步地加數(shù)據(jù)buffer到一個(gè)隊(duì)列并維護(hù)數(shù)據(jù)流。USB定義的短包語法在沒有軟件干預(yù)下完全支持所有的邊界條件處理。
USB總線的主機(jī)控制器要求應(yīng)用根集線器,主機(jī)控制器模擬了根集線器,它在操作寄存器空間裝有端口寄存器,寄存器含有在USB規(guī)范中需要管 理每個(gè)端口的最小硬件狀態(tài)和控制。事務(wù)通過根端口被廣播下流的USB設(shè)備,端口寄存器提供給系統(tǒng)軟件對(duì)端口的管理和端口的狀態(tài)信息,包括:設(shè)備的連接與斷 開、執(zhí)行設(shè)備復(fù)位、處理端口功率和端口電源管理。
EHCI控制器提供了兩套軟件可訪問的寄存器:內(nèi)存映射的主機(jī)控制器寄存器和可選的PCI配置寄存器。PCI配置寄存器僅是用到主機(jī)控制器的PCI設(shè)備需要的。
主機(jī)控制器能力寄存器定義了限制、主機(jī)控制器使用的能力,如:下行端口數(shù)、主機(jī)控制器的接口版本號(hào)、同步調(diào)度門限等。在代碼中使用結(jié)構(gòu)ehci_caps來描述。
主機(jī)控制器操作寄存器位于能力寄存器之后,是雙字對(duì)齊讀寫寄存器。這些寄存器分為兩套,第一套從地址00到3Fh,在主控制器核心電源好的 情況下使用,包括USB控制命令、狀態(tài)、中斷使能、幀序號(hào)寄存器。第二套寄存器從40h到可使用的寄存器空間結(jié)尾,在外圍輔助電源好的情況下使用,包括每 個(gè)端口的狀態(tài)與控制寄存器。在代碼中使用結(jié)構(gòu)ehci_regs來描述。
接口數(shù)據(jù)結(jié)構(gòu)在hcd軟件和ehci控制器硬件之間用于通信控制、數(shù)據(jù)和狀態(tài)。接口由周期調(diào)度、周期幀鏈表、異步調(diào)度、同步事務(wù)描述子 (iTD)、分離事務(wù)同步傳輸描述子(siTD)、隊(duì)列頭(QH)和隊(duì)列元素傳輸描述子(qTD)組成。在代碼中,qTD用結(jié)構(gòu)ehci_qtd描 述,QH用結(jié)構(gòu)ehci_qh描述。iTD用結(jié)構(gòu)ehci_itd描述,siTD用結(jié)構(gòu)ehci_sitd描述。
EHCI主機(jī)控制器帶有一個(gè)模擬操作的根集線器,通過寄存器可完成對(duì)根集線器的各個(gè)端口的狀態(tài)及連接控制,因此,它不會(huì)調(diào)用到USB核心層中有關(guān)HUB的操作函數(shù)。
EHCI主機(jī)控制器對(duì)于URB的提交排隊(duì)及傳輸、調(diào)度以及控制器的各種狀態(tài)轉(zhuǎn)移提供了控制。特別是寄存器級(jí)的控制函數(shù)與EHCI控制器本身結(jié)構(gòu)相關(guān),牽涉到對(duì)眾多寄存器值的理解,因而這里只說明了ehci控制器的上層功能函數(shù)。
EHCI驅(qū)動(dòng)程序分析EHCI驅(qū)動(dòng)程序的編寫思路是:EHCI驅(qū)動(dòng)程序是一個(gè)結(jié)構(gòu)hc_driver實(shí)例,它應(yīng)該實(shí)現(xiàn)結(jié)構(gòu)hc_driver中的函數(shù),另外,從硬件上層 來看,EHCI主控制從PCI總線橋接,應(yīng)是一個(gè)PCI驅(qū)動(dòng)程序?qū)嵗虼?,?yīng)實(shí)現(xiàn)結(jié)構(gòu)pci_driver中的函數(shù),并用PCI注冊(cè)函數(shù) pci_register_driver注冊(cè)此實(shí)例。
函數(shù)__init init注冊(cè)了&ehci_pci_driver控制器驅(qū)動(dòng)程序,由于ehci-hcd是通過PCI總線與CPU相連,因而,它被注冊(cè)成一個(gè)新的PCI驅(qū)動(dòng)程序。
函數(shù)__init init分析如下(在drivers/usb/host/ehci-hcd.c中):
#ifdef CONFIG_PCI #include "ehci-pci.c" #define PCI_DRIVER ehci_pci_driver #endif static int __init ehci_hcd_init(void) { int retval = 0; ? …… ? #ifdef PCI_DRIVER //注冊(cè)驅(qū)動(dòng)程序,初始化ehci_pci_driver并加到內(nèi)核對(duì)象體系中去 retval = pci_register_driver(&PCI_DRIVER); if (retval < 0) goto clean1; #endif …… clean1: #endif #ifdef PLATFORM_DRIVER platform_driver_unregister(&PLATFORM_DRIVER); …… #endif return retval; }
PCI驅(qū)動(dòng)程序結(jié)構(gòu)實(shí)例ehci_pci_driver的一些函數(shù)定義如下:
static const char hcd_name [] = "ehci_hcd"; /* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver ehci_pci_driver = { .name = (char *) hcd_name, .id_table = pci_ids, ? .probe = usb_hcd_pci_probe, //探測(cè)函數(shù) .remove = usb_hcd_pci_remove, //移去設(shè)備時(shí)的清除函數(shù) ? #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, .resume = usb_hcd_pci_resume, #endif };
pci_ids 是PCI驅(qū)動(dòng)程序選擇元數(shù)據(jù),PCI熱插拔使用到它,通過它來選擇驅(qū)動(dòng)程序ehci_driver。pci_ids列出如下:
static const struct pci_device_id pci_ids [] = { { //處理任何USB 2.0 EHCI控制器 PCI_DEVICE_CLASS(((PCI_CLASS_SERIAL_USB << 8) | 0x20), ~0), .driver_data = (unsigned long) &ehci_driver, }, { /* end: all zeroes */ } };
ehci_driver是主機(jī)控制器結(jié)構(gòu)hc_driver實(shí)例,它描述了EHCI控制器信息及各種操作函數(shù),每個(gè)主機(jī)控制器都有一個(gè)這樣的結(jié)構(gòu)。ehci_driver列出如下:
static const struct hc_driver ehci_driver = { .description = hcd_name, .product_desc = "EHCI Host Controller", .hcd_priv_size = sizeof(struct ehci_hcd), ? /* * 通用與硬件相關(guān)聯(lián)的成員 */ .irq = ehci_irq, //中斷處理函數(shù) .flags = HCD_MEMORY | HCD_USB2, //控制器寄存器使用內(nèi)存|usb2.0 ? /* * 基本的生命周期操作 */ //初始化HCD和root hub .reset = ehci_hc_reset, //HCD復(fù)位到停止?fàn)顟B(tài)。 //開始運(yùn)行,初始化ECHI設(shè)備的各種寄存器進(jìn)入運(yùn)行狀態(tài), //調(diào)用函數(shù)hcd_register_root注冊(cè)根集線器驅(qū)動(dòng)程序。 .start = ehci_start, #ifdef CONFIG_PM .suspend = ehci_suspend, //在所有的設(shè)備掛起后調(diào)用 .resume = ehci_resume, //在所有的設(shè)備恢復(fù)之前調(diào)用 #endif .stop = ehci_stop, //HCD停止寫內(nèi)存和I/O操作 ? /* * 管理i/o請(qǐng)求和相關(guān)的設(shè)備資源 */ .urb_enqueue = ehci_urb_enqueue, //提交URB的具體處理函數(shù) .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, ? /* * 調(diào)度支持 */ .get_frame_number = ehci_get_frame, //得到當(dāng)前的幀序號(hào) ? /* root hub支持,EHCI主機(jī)控制器使用了它自己的模擬根集線器,這個(gè)集線器通過對(duì)寄存器的設(shè)置提供了簡(jiǎn)單的端口狀態(tài)及連接控制功能。*/ .hub_status_data = ehci_hub_status_data,//端口狀態(tài)發(fā)生變化時(shí),直接控制端口 .hub_control = ehci_hub_control, //hub控制的狀態(tài)機(jī) .hub_suspend = ehci_hub_suspend, .hub_resume = ehci_hub_resume,//電源恢復(fù),初始化根集線器 };
下面分析只分析結(jié)構(gòu)實(shí)例ehci_pci_driver中的探測(cè)函數(shù)usb_hcd_pci_probe:
函數(shù) usb_hcd_pci_probe初始化基于PCI的HCD(主機(jī)控制器驅(qū)動(dòng)程序),參數(shù)dev是被探測(cè)的USB主機(jī)控制器,參數(shù)id是連接控制器到HCD構(gòu)架的pci熱插拔設(shè)備ID。這個(gè)函數(shù)不能從中斷上下文中調(diào)用。
函數(shù) usb_hcd_pci_probe作為probe()存在HCD的pci_driver結(jié)構(gòu)中,它分配基本的PCI資源給這個(gè)USB控制器:它分配一個(gè) PCI資源區(qū)域、進(jìn)行I/O映射、創(chuàng)建usb_hcd結(jié)構(gòu)實(shí)例并賦上設(shè)備操作函數(shù)集&usb_hcd_operations,給hcd創(chuàng)建DMA 緩沖池,申請(qǐng)中斷,注冊(cè)總線。通過與HCD相關(guān)的hotplug條目.driver_data為HCD觸發(fā)start()方法。
函數(shù) usb_hcd_pci_probe分析如下(在drivers/usb/core/hcd-pci.c中):
int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id) { struct hc_driver *driver; unsigned long resource, len; void __iomem *base; struct usb_hcd *hcd; int retval, region; char buf [8], *bufp = buf; ? if (usb_disabled()) //如果沒有USB設(shè)備 return -ENODEV; //結(jié)構(gòu)pci_device_id中存有供應(yīng)商和設(shè)備ID, //其成員driver_data指向具體設(shè)備驅(qū)動(dòng)程序結(jié)構(gòu)。 if (!id || !(driver = (struct hc_driver *) id->driver_data)) return -EINVAL; ? //使設(shè)備的I/O和設(shè)備內(nèi)存區(qū)有效,喚醒設(shè)備,在被驅(qū)動(dòng)程序使用前初始化設(shè)備 if (pci_enable_device (dev) < 0) return -ENODEV; dev->current_state = 0; dev->dev.power.power_state = 0; ? if (!dev->irq) {//沒有中斷 dev_err (&dev->dev, "Found HC with no IRQ. Check BIOS/PCI?%s setup!n", pci_name(dev)); retval = -ENODEV; goto done; } ? //HC寄存器使用內(nèi)存 if (driver->flags & HCD_MEMORY) { // EHCI, OHCI region = 0; resource = pci_resource_start (dev, 0); //得到PCI設(shè)備的0號(hào)區(qū)域資源 len = pci_resource_len (dev, 0); //申請(qǐng)名字為driver->description的I/O內(nèi)存區(qū)域, //從resource開始,長(zhǎng)度為len if (!request_mem_region (resource, len, driver->description)) { dev_dbg (&dev->dev, "controller already in usen"); retval = -EBUSY; goto done; } //映射resource開始的物理地址到CPU的虛擬地址base base = ioremap_nocache (resource, len); if (base == NULL) {//映射失敗 dev_dbg (&dev->dev, "error mapping memoryn"); retval = -EFAULT; clean_1: release_mem_region (resource, len); //釋放資源 dev_err (&dev->dev, "init?%s fail,?%dn", pci_name(dev), retval); goto done; } ? } else { // UHCI resource = len = 0; //標(biāo)準(zhǔn)PCI配置6個(gè)region(或說6個(gè)bar) for (region = 0; region < PCI_ROM_RESOURCE; region++) { //如果不是IO資源 if (!(pci_resource_flags (dev, region) & IORESOURCE_IO)) continue; ? resource = pci_resource_start (dev, region); len = pci_resource_len (dev, region); //申請(qǐng)名字為driver->description的資源 if (request_region (resource, len, driver->description)) break; } if (region == PCI_ROM_RESOURCE) {//如果是rom,則說明無資源可用 dev_dbg (&dev->dev, "no i/o regions availablen"); retval = -EBUSY; goto done; } base = (void __iomem *) resource; } ? //創(chuàng)建并初始化結(jié)構(gòu)hcd hcd = usb_create_hcd (driver); …… // hcd zeroed everything hcd->regs = base; hcd->region = region; ? //將hcd驅(qū)動(dòng)程序結(jié)構(gòu)賦給pci設(shè)備結(jié)構(gòu),即dev ->dev->driver_data = hcd pci_set_drvdata (dev, hcd); hcd->self.bus_name = pci_name(dev); #ifdef CONFIG_PCI_NAMES hcd->product_desc = dev->pretty_name; #endif hcd->self.controller = &dev->dev; ? //創(chuàng)建4個(gè)DMA池 if ((retval = hcd_buffer_create (hcd)) != 0) { clean_3: pci_set_drvdata (dev, NULL); usb_put_hcd (hcd); goto clean_2; } //打印信息 dev_info (hcd->self.controller, "%sn", hcd->product_desc); ? //到現(xiàn)在為止,HC已在一個(gè)不確定狀態(tài),調(diào)用驅(qū)動(dòng)程序的reset函數(shù)復(fù)位 if (driver->reset && (retval = driver->reset (hcd)) < 0) { dev_err (hcd->self.controller, "can't resetn"); goto clean_3; } //使能設(shè)備上的bus-mastering總線 pci_set_master (dev); … //申請(qǐng)共享中斷號(hào),中斷處理函數(shù)是usb_hcd_irq,設(shè)備名為description retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->driver->description, hcd); … hcd->irq = dev->irq; ? //注冊(cè)總線到sysfs和/proc文件系統(tǒng) usb_register_bus (&hcd->self); …… return retval; }
函數(shù) usb_create_hcd創(chuàng)建并初始化一個(gè)結(jié)構(gòu)usb_hcd實(shí)例,參數(shù)driver是此HCD使用的HCD驅(qū)動(dòng)程序。如果內(nèi)存不可用,返回NULL。
函數(shù) usb_create_hcd列出如下(在drivers/usb/core/hcd.c中):
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver) { struct usb_hcd *hcd; ? hcd = kcalloc(1, sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); if (!hcd) return NULL; ? usb_bus_init(&hcd->self);//初始化usb_bus結(jié)構(gòu) hcd->self.op = &usb_hcd_operations; hcd->self.hcpriv = hcd; hcd->self.release = &hcd_release; ? init_timer(&hcd->rh_timer); ? hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : "USB Host Controller"; hcd->state = USB_STATE_HALT; ? return hcd; }
USB大存儲(chǔ)(USB Mass Storage)工作組(CWG Class Working Group)規(guī)范包括:
USB Mass Storage Class Control/Bulk/Interrupt(CBI) Transport 即USB大存儲(chǔ)類控制/批量/中斷傳輸協(xié)議。 USB Mass Storage Class Bulk-Only Transport 即USB大存儲(chǔ)類批量傳輸協(xié)議。 USB Mass Storage Class UFI Command Specification 即USB大存儲(chǔ)類UFI命令規(guī)范。 USB Mass Storage Class Bootability Specfication 即USB大存儲(chǔ)類系統(tǒng)啟動(dòng)規(guī)范。 USB Mass Storage Class Compliance Test Specification 即USB大存儲(chǔ)類遵從測(cè)試規(guī)范。其中,CBI傳輸規(guī)范僅用于全速軟盤驅(qū)動(dòng)器,不能用于高速設(shè)備或其它非軟盤設(shè)備。
USB大存儲(chǔ)類使用幾種命令集規(guī)范,這些命令集的命令塊放在符合USB協(xié)議的USB包裹器中,USB大存儲(chǔ)類規(guī)范定義了下面幾種命令集:
軟驅(qū)、光驅(qū)和磁帶驅(qū)動(dòng)器使用的ATAPI規(guī)范(Advanced Technology Attachment Packet Interface)。 精簡(jiǎn)塊命令(Reduced Block Commands(RBC)) 多媒體命令集2(Multi-Media Command Set 2 (MMC-2)) SCSI主命令(SCSI Primary Commands-2(SPC-2)) USB規(guī)范(Universal Serial Bus Specification)USB大存儲(chǔ)類設(shè)備的接口描述子包含了一個(gè)bInterfaceSubClass和bInterfacePortocal的 域,bInterfaceSubClass描述了USB大存儲(chǔ)類支持的命令塊規(guī)范,如:它為06h時(shí)表示支持的是SCSI傳輸命令集。 bInterfacePortocal描述了USB大存儲(chǔ)類支持的接口傳輸協(xié)議,如:它為50h時(shí)表示支持的是Bulk-Only傳輸協(xié)議。
大存儲(chǔ)設(shè)備(Mass Storage)包括U盤、讀卡器及USB接口的光驅(qū)等其它塊存儲(chǔ)設(shè)備,它們看作是SCSI接口設(shè)備,當(dāng)用戶從設(shè)備上讀寫數(shù)據(jù)時(shí),文件系統(tǒng)將讀寫操作傳送 到SCSI協(xié)議層,SCSI協(xié)議層的讀寫請(qǐng)求封裝成USB請(qǐng)求塊(URB)通過USB接口傳遞給設(shè)備,USB設(shè)備從URB中解析出SCSI協(xié)議命令后再操 作塊設(shè)備。USB接口大存儲(chǔ)設(shè)備的操作流程圖如圖6所示。
USB接口大存儲(chǔ)設(shè)備驅(qū)動(dòng)程序的設(shè)計(jì)思路是:設(shè)計(jì)一個(gè)控制線程,這個(gè)線程被注冊(cè)為虛擬SCSI控制器,這個(gè)線程在設(shè)備插入/移去時(shí)一直作為SCSI 節(jié)點(diǎn)存在的。這樣,被移去的設(shè)備能在再插上時(shí)被給以與以前/dev中同一節(jié)點(diǎn)。當(dāng)一個(gè)設(shè)備被插上時(shí),控制線程從SCSI中間層代碼得到命令??刂凭€程接收 命令,在檢查后送命令到協(xié)議處理函數(shù)。這些處理函數(shù)負(fù)責(zé)再寫命令(如果必要)到設(shè)備得接受的形式。例如:ATAPI設(shè)備不能支持6byte命令,這樣,它 們必須被再寫成10byte變量。一旦協(xié)議處理函數(shù)已再寫了命令,它們被送到傳輸處理函數(shù)。傳輸處理函數(shù)負(fù)責(zé)送命令到設(shè)備、交換數(shù)據(jù)、并接著得到設(shè)備的狀 態(tài)。在協(xié)議處理函數(shù)和傳輸處理函數(shù)之間有一小段代碼,來決定REQUEST_SENSE命令是否應(yīng)該發(fā)出。在命令被處理后,scsi_done()被調(diào)用 來發(fā)信號(hào)給SCSI層命令已完成。我們準(zhǔn)備接收下一條命令。
作為具有操作系統(tǒng)的智能嵌入設(shè)備,它使用了SCSI命令塊集與Bulk-only傳輸協(xié)議。它既能作為主機(jī)來操作其它USB大存儲(chǔ)設(shè)備,稱 為大存儲(chǔ)設(shè)備主機(jī)。同時(shí),也能作為USB大存儲(chǔ)設(shè)備被其它主機(jī)控制。下面對(duì)具有l(wèi)inux操作系統(tǒng)的嵌入設(shè)備分別就兩種模式分別進(jìn)行分析。
Bulk-Only傳輸協(xié)議介紹Bulk-Only傳輸協(xié)議是USB大容量存貯器類中的USB批量數(shù)據(jù)傳輸協(xié)議,它定義了僅通過批量端點(diǎn)傳輸?shù)拿?、?shù)據(jù)和狀態(tài)。它使用命令塊數(shù)據(jù) 包裹器(CBW)發(fā)送命令,使用命令狀態(tài)數(shù)據(jù)包裹器(CSW)接收返回的狀態(tài)。命令塊數(shù)據(jù)包裹器(CBW)是一個(gè)包含命令塊和相關(guān)信息的數(shù)據(jù)包。 命令狀態(tài)數(shù)據(jù)包(CSW)裹器是一個(gè)包含命令塊狀態(tài)的數(shù)據(jù)包。命令塊數(shù)據(jù)包裹器(CBW)的格式如表1所示。
(08h-0Bh) dCBWDataTransferLength 12
(0Ch) bmCBWFlags 13
(0Dh) Reserved(0) bCBWLUN 14
(0Eh) Reserved(0) bCBWCBLength 15-30
(0Fh-1Eh) CBWCB
命令塊數(shù)據(jù)包裹器(CBW)用下述數(shù)據(jù)結(jié)構(gòu)描述(在drivers/usb/storage/transport.h中):
struct bulk_cb_wrap { __le32 Signature; //簽名'USBC' __u32 Tag; //每個(gè)命令唯一的ID __le32 DataTransferLength; //數(shù)據(jù)大小 __u8 Flags; //在bit 0中表示方向 __u8 Lun; //表示LUN(SCSI邏輯單元)正常為0 __u8 Length; //數(shù)據(jù)傳輸長(zhǎng)度 __u8 CDB[16]; //傳輸?shù)拿钭止?jié) };
命令狀態(tài)數(shù)據(jù)包裹器(CSW)的格式如表2所示。
命令狀態(tài)數(shù)據(jù)包裹器(CSW)用下述數(shù)據(jù)結(jié)構(gòu)描述(在drivers/usb/storage/transport.h中):
/* 命令狀態(tài)包裹器*/ struct bulk_cs_wrap { __le32 Signature; //簽名 'USBS' __u32 Tag; //與CBW中Tag一樣 __le32 Residue; //沒有傳輸完的數(shù)據(jù)量 __u8 Status; //操作狀態(tài)標(biāo)識(shí),如:成功、失敗等 __u8 Filler[18]; };
傳輸過程是:當(dāng)傳輸方向是從設(shè)備到主機(jī)時(shí),則當(dāng)CBW發(fā)送成功后,設(shè)備從設(shè)備的In端點(diǎn)讀取CBW中規(guī)定長(zhǎng)度的數(shù)據(jù)CBWCB;當(dāng)傳輸方向是從主機(jī)到設(shè) 備時(shí),則當(dāng)CBW發(fā)送成功后,向設(shè)備的Out端點(diǎn)發(fā)送CBW中規(guī)定長(zhǎng)度的數(shù)據(jù)CBWCB。CBWCB是命令塊數(shù)據(jù),是遵循某一規(guī)范的命令集, 如:SCSI-2命令集,最長(zhǎng)16字節(jié)。
當(dāng)主機(jī)與設(shè)備之間的數(shù)據(jù)傳送完畢后,主機(jī)還需從設(shè)備的In端點(diǎn)讀取傳送狀態(tài),主機(jī)根據(jù)接收的CSW數(shù)據(jù)包即可判斷出通信是否正常。若返回的結(jié)果有錯(cuò)誤,還須進(jìn)行相應(yīng)的出錯(cuò)處理。
樣例:從設(shè)備讀取數(shù)據(jù)的傳輸過程
下面是一個(gè)從設(shè)備讀取數(shù)據(jù)的傳輸過程的例子,主機(jī)先向端點(diǎn)1發(fā)出CBW命令,設(shè)備解析CBW解析命令后,從主機(jī)指定的端點(diǎn)2將數(shù)據(jù)傳回給主 機(jī)。在傳送成功后,主機(jī)又讀取端點(diǎn)2的狀態(tài)CSW。主機(jī)從設(shè)備讀到數(shù)據(jù)的流程圖如下圖。從圖中可看出,第0到第2包是發(fā)送CBW的過程,第3到第5包是讀 取數(shù)據(jù)的過程,下面接著的第0到第1包是讀取CSW的過程。令牌包和握手包是由控制管道(對(duì)應(yīng)ep0)來發(fā)送接收的。
第0到第5包的數(shù)據(jù)格式圖列出如圖7所示:
在第1包中,CBW傳輸了31(1FH)個(gè)字節(jié)的數(shù)據(jù)。內(nèi)容含義是:55 53 42 43 是CBW后面固有的特征碼;28 E8 31 FE 是由主機(jī)產(chǎn)生的CBWTag;00 02 00 00 是CBW數(shù)據(jù)傳輸長(zhǎng)度,在此情況下是0000,0200H=512字節(jié);80 是后面固有的標(biāo)志碼;00 是后面固有的CBWLUN;0A 是CBWCB長(zhǎng)度,意味著命令描述塊(CDB)長(zhǎng)度是10字節(jié),其中。28表示對(duì)應(yīng)SCSI協(xié)議28h讀命令。對(duì)于命令塊,看下節(jié)的SCSI命令描述塊的 結(jié)構(gòu)。
SCSI協(xié)議28h讀命令是Read(10),在這個(gè)CBW中,要求讀取0柱0道1扇區(qū)共512字節(jié)的MBR數(shù)據(jù),前446字節(jié)為主引導(dǎo)記錄,接著的64字節(jié)為DPT(Disk Partition Table盤分區(qū)表),最后的2字節(jié)"55 AA"為有效結(jié)束標(biāo)志。
在第4包中傳輸了512字節(jié)的數(shù)據(jù)。
CSW包的數(shù)據(jù)格式圖列出如圖8所示:
CSW數(shù)據(jù)包傳輸13(0DH)個(gè)字節(jié)的數(shù)據(jù)。內(nèi)容含義是:55 52 42 53是CSW后面固有的特征碼;28 E8 31 FF是主機(jī)產(chǎn)生的CSWTag;00 00 00 00是CSW的數(shù)據(jù)冗余;00 指示在此情況下CSW的狀態(tài),此例中為OK。
SCSI命令描述塊結(jié)構(gòu)各種SCSI命令描述塊具有相似的結(jié)構(gòu),SCSI命令描述塊的結(jié)構(gòu)如表8所示。
命令的指定參數(shù) … … n-1 n 控制字節(jié)
SCSI命令描述塊的結(jié)構(gòu)的各項(xiàng)說明如下:
操作碼(Opcode)每個(gè)命令的0號(hào)字節(jié)就是操作碼,它定義了命令的類型和長(zhǎng)度。它的高3位代表了命令所屬的命令組,低5位表示命令本身。每個(gè)命令組都有一個(gè)命令長(zhǎng)度。因而,對(duì)命令的第一個(gè)字節(jié)進(jìn)行解碼以后,目標(biāo)器就知道這個(gè)命令還剩下多少字節(jié)。操作碼在不同設(shè)備上含義是不同的。
SCSI常用命令塊有查詢、讀請(qǐng)求、測(cè)試單元準(zhǔn)備、禁止媒介刪除、讀緩沖、寫緩沖等。
命令組代表命令組的高3位可以有8個(gè)不同的組合,所以可以代表8個(gè)命令組,當(dāng)制造商實(shí)現(xiàn)自己的標(biāo)準(zhǔn)的時(shí)候,就必須使用6號(hào)組或者7號(hào)組,實(shí)際上,使用6號(hào)組或者7號(hào)組的情況很少發(fā)生。命令組的說明如表9所示。
控制字節(jié)的格式如表10所示。SCSI-2中,控制字節(jié)僅僅包含了在標(biāo)準(zhǔn)中定義的兩位,它們是連接位(Link bit)和標(biāo)志位(flag bit),而且這兩位都是可選的。連接位使你可以將幾個(gè)命令連接成一個(gè)命令鏈,命令鏈中的每一個(gè)命令被稱為連接的命令。從而這些連接的命令就形成了一個(gè)連 接的I/O過程。這就可以阻止其他I/O過程的命令插入這個(gè)已形成命令鏈的I/O過程,這就是在目標(biāo)器內(nèi)的優(yōu)化方法。舉個(gè)例子,當(dāng)一個(gè)邏輯數(shù)據(jù)塊需要被讀 取一修改一寫回時(shí),這個(gè)做法就變得十分有用。而且,連接的命令允許使用邏輯數(shù)據(jù)塊的相對(duì)地址。
標(biāo)志位必須和連接命令一起使用。這引起在連接的命令執(zhí)行結(jié)束之后發(fā)送服務(wù)響應(yīng)LINKED COMMAND COMPLETE(WITH FLAG)(0BH),而不是發(fā)送服務(wù)響應(yīng)LINKED COMMAND COMPLETE(OAH)。這樣,你就可以在一個(gè)命令鏈中標(biāo)出一個(gè)特定的命令。
在SCSI-3中出現(xiàn)了新的標(biāo)志位:ACA位。ACA是偶然事件自動(dòng)通信(auto contingent allegiance)的縮寫,它是在命令執(zhí)行過程中萬一發(fā)生錯(cuò)誤時(shí)LUN所采取的一種措施。如果ACA位沒有被置"1",那么只要下一個(gè)命令從同一個(gè)啟 動(dòng)器中發(fā)出時(shí),該錯(cuò)誤狀態(tài)就被取消。如果ACA位被置"1",它就會(huì)阻止取消錯(cuò)誤狀態(tài)的行動(dòng)并保持這種狀態(tài)。
Mass Storage設(shè)備對(duì)象結(jié)構(gòu)每個(gè)大存儲(chǔ)設(shè)備用一個(gè)對(duì)象結(jié)構(gòu)us_data來描述它的設(shè)備、管道、SCSI接口、傳輸、協(xié)議等各方面的信息及處理函數(shù)。
結(jié)構(gòu)us_data列出如下(在drivers/usb/storage/usb.h中):
/*我們提供了一個(gè)DMA映射I/O buffer給小USB傳輸使用。CB[I]需要12字節(jié)buffer,Bulk-only需要31字節(jié)buffer,但Freecom需要64字節(jié)buffer,因此,我們分配了64字節(jié)的buffer。*/ #define US_IOBUF_SIZE 64 ? typedef int (*trans_cmnd)(struct scsi_cmnd *, struct us_data*); typedef int (*trans_reset)(struct us_data*); typedef void (*proto_cmnd)(struct scsi_cmnd*, struct us_data*); typedef void (*extra_data_destructor)(void *); //格外的數(shù)據(jù)析構(gòu)函數(shù) ? struct us_data { //工作設(shè)備、接口結(jié)構(gòu)及各種管道 struct semaphore dev_semaphore; //保護(hù)pusb_dev struct usb_device *pusb_dev; //從類usb_device繼承 struct usb_interface *pusb_intf; //從類usb_interface繼承 struct us_unusual_dev *unusual_dev; //常用的設(shè)備鏈表定義 unsigned long flags; /* 最初來自過濾器的標(biāo)識(shí)*/ unsigned int send_bulk_pipe; /* 緩存的管道值*/ unsigned int recv_bulk_pipe; unsigned int send_ctrl_pipe; unsigned int recv_ctrl_pipe; unsigned int recv_intr_pipe; ? //設(shè)備的信息 char vendor[USB_STOR_STRING_LEN]; //供應(yīng)商信息 char product[USB_STOR_STRING_LEN]; //產(chǎn)品信息 char serial[USB_STOR_STRING_LEN]; //產(chǎn)品序列號(hào) char *transport_name; //傳輸協(xié)議名 char *protocol_name; //協(xié)議名 u8 subclass; //子類 u8 protocol; u8 max_lun; //最大的邏輯單元 ? u8 ifnum; //接口數(shù) u8 ep_bInterval; //中斷傳輸間隔 ? //設(shè)備的函數(shù)指針 trans_cmnd transport; //傳輸函數(shù) trans_reset transport_reset; //傳輸設(shè)備復(fù)位 proto_cmnd proto_handler; //協(xié)議處理函數(shù) ? //SCSI接口 struct Scsi_Host *host; //虛擬SCSI主機(jī)數(shù)據(jù)結(jié)構(gòu) struct scsi_cmnd *srb; //當(dāng)前SCSI命令描述塊 ? //線程信息 int pid; //控制線程 ? //控制和批量通信數(shù)據(jù) struct urb *current_urb; //USB請(qǐng)求 struct usb_ctrlrequest *cr; //USB控制請(qǐng)求的setup數(shù)據(jù) struct usb_sg_request current_sg; //碎片-收集請(qǐng)求 unsigned char *iobuf; //I/O buffer dma_addr_t cr_dma; //控制請(qǐng)求數(shù)據(jù)buffer的DMA地址 dma_addr_t iobuf_dma; // I/O buffer的DMA地址 ? //互斥保護(hù)和同步結(jié)構(gòu) struct semaphore sema; /* to sleep thread on */ struct completion notify; //線程開始/結(jié)束時(shí)發(fā)通知出去 wait_queue_head_t dev_reset_wait; //在復(fù)位期間等待 wait_queue_head_t scsi_scan_wait; //在SCSI掃描前等待 struct completion scsi_scan_done; //SCSI掃描線程結(jié)束時(shí)通知處理函數(shù) ? //子驅(qū)動(dòng)程序信息 void *extra; //任何格外的數(shù)據(jù) extra_data_destructor extra_destructor;//格外的數(shù)據(jù)析構(gòu)函數(shù) };
Mass Storage設(shè)備初始化
函數(shù)usb_stor_init注冊(cè)和初始化大存儲(chǔ)驅(qū)動(dòng)程序。函數(shù)usb_stor_init列出如下(在drivers/usb/storage/usb.c中):
static int __init usb_stor_init(void) { int retval; printk(KERN_INFO "Initializing USB Mass Storage driver...n"); ? //注冊(cè)驅(qū)動(dòng)程序,如果操作失敗,返回負(fù)值的錯(cuò)誤代碼 retval = usb_register(&usb_storage_driver); if (retval == 0) printk(KERN_INFO "USB Mass Storage support registered.n"); ? return retval; }
大存儲(chǔ)設(shè)備驅(qū)動(dòng)程序結(jié)構(gòu)實(shí)例usb_storage_driver列出如下:
struct usb_driver usb_storage_driver = { .owner = THIS_MODULE, .name = "usb-storage", .probe = storage_probe, //探測(cè)并初始化設(shè)備 .disconnect = storage_disconnect,//斷開連接處理函數(shù) .id_table = storage_usb_ids, };
在usb_device_id結(jié)構(gòu)類型數(shù)組中storage_usb_ids定義了設(shè)備類、子類及命令塊集的協(xié)議類型。部分列出如下:
static struct usb_device_id storage_usb_ids [] = { …… /* Bulk-only transport for all SubClass values */ { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_RBC, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8020, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_QIC, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_UFI, US_PR_BULK) }, { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_8070, US_PR_BULK) }, #if?!defined(CONFIG_BLK_DEV_UB) &&?!defined(CONFIG_BLK_DEV_UB_MODULE) { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, US_SC_SCSI, US_PR_BULK) }, #endif ? /* Terminating entry */ { } };
探測(cè)函數(shù)storage_probe分析
函數(shù)storage_probe 探測(cè)看是否能驅(qū)動(dòng)一個(gè)新連接的USB設(shè)備。創(chuàng)建了大存儲(chǔ)設(shè)備控制線程usb_stor_control_thread和SCSI設(shè)備后期掃描線程 usb_stor_scan_thread。函數(shù)storage_probe在控制線程中通過虛擬SCSI主機(jī)控制器發(fā)送SCSI命令,經(jīng)Bulk- Only協(xié)議封裝后,再填充為URB包,傳送給USB核心層來發(fā)送給設(shè)備。函數(shù)storage_probe調(diào)用層次圖如圖2所示。下面按照這個(gè)圖分析函數(shù) storage_probe。
函數(shù)storage_probe列出如下(在drivers/usb/storage/usb.c中):
static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct us_data *us; const int id_index = id - storage_usb_ids; int result; ? US_DEBUGP("USB Mass Storage device detectedn"); ? //分析us_data結(jié)構(gòu)對(duì)象空間 us = (struct us_data *) kmalloc(sizeof(*us), GFP_KERNEL); if (!us) { printk(KERN_WARNING USB_STORAGE "Out of memoryn"); return -ENOMEM; } memset(us, 0, sizeof(struct us_data)); init_MUTEX(&(us->dev_semaphore)); init_MUTEX_LOCKED(&(us->sema)); init_completion(&(us->notify)); init_waitqueue_head(&us->dev_reset_wait); init_waitqueue_head(&us->scsi_scan_wait); init_completion(&us->scsi_scan_done); ? //將USB設(shè)備與結(jié)構(gòu)us_data關(guān)聯(lián)起來 //設(shè)置 intf->dev ->driver_data = us,分配buffer result = associate_dev(us, intf); if (result) goto BadDevice; ? //得到unusual_devs條目和描述子,初始化us。 //id_index與usb_device_id表中序號(hào)匹配,找到表中對(duì)應(yīng)的條目。 get_device_info(us, id_index); ? #ifdef CONFIG_USB_STORAGE_SDDR09 if (us->protocol == US_PR_EUSB_SDDR09 || //SDDR-09 的SCM-SCSI橋 us->protocol == US_PR_DPCM_USB) { // CB/SDDR09混合體 //設(shè)置配置,STALL在這兒是一個(gè)可接受的反應(yīng) if (us->pusb_dev->actconfig->desc.bConfigurationValue != 1) { US_DEBUGP("active config #%d?!= 1???n", us->pusb_dev ->actconfig->desc.bConfigurationValue); goto BadDevice; } //重置配置,重新初始化端點(diǎn)及接口 result = usb_reset_configuration(us->pusb_dev); …… } #endif ? //將傳輸方式、協(xié)議和管道設(shè)置賦給us result = get_transport(us); if (result) goto BadDevice; result = get_protocol(us); if (result) goto BadDevice; result = get_pipes(us); if (result) goto BadDevice; ? //初始化所有需要的動(dòng)態(tài)資源 result = usb_stor_acquire_resources(us); if (result) goto BadDevice; result = scsi_add_host(us->host, &intf->dev); if (result) { printk(KERN_WARNING USB_STORAGE "Unable to add the scsi hostn"); goto BadDevice; } ? /*線程usb_stor_scan_thread執(zhí)行延遲的SCSI設(shè)備掃描工作,掃描給定的適配器us->host,掃描通道及目標(biāo),掃描探測(cè)LUN。*/ result = kernel_thread(usb_stor_scan_thread, us, CLONE_VM); if (result < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start the device-scanning threadn"); scsi_remove_host(us->host); goto BadDevice; } ? return 0; …… }
函數(shù)usb_stor_acquire_resources初始化所有的需要的動(dòng)態(tài)資源,啟動(dòng)控制線程,函數(shù)列出如下(在drivers/usb/storage/usb.c中):
static int usb_stor_acquire_resources(struct us_data *us) { int p; ? us->current_urb = usb_alloc_urb(0, GFP_KERNEL); //分配urb結(jié)構(gòu)對(duì)象空間 if (!us->current_urb) { US_DEBUGP("URB allocation failedn"); return -ENOMEM; } ? //當(dāng)我們執(zhí)行下兩個(gè)操作時(shí)鎖住設(shè)備?!? down(&us->dev_semaphore); ? //僅對(duì)于批量設(shè)備,得到最大邏輯單元值, //在SCSI協(xié)議模型中,每個(gè)邏輯單元用來操作SCSI設(shè)備。 if (us->protocol == US_PR_BULK) { p = usb_stor_Bulk_max_lun(us); if (p < 0) { up(&us->dev_semaphore); return p; } us->max_lun = p; } ? //如果設(shè)備需要初始化,在開始控制線程前初始化設(shè)備 if (us->unusual_dev->initFunction) us->unusual_dev->initFunction(us); ? up(&us->dev_semaphore); ? //因?yàn)檫@是一個(gè)新設(shè)備,我們需要注冊(cè)一個(gè)設(shè)備的虛擬SCSI控制器, //用來處理SCSI層高層協(xié)議。 us->host = scsi_host_alloc(&usb_stor_host_template, sizeof(us)); if (!us->host) { printk(KERN_WARNING USB_STORAGE "Unable to allocate the scsi hostn"); return -EBUSY; } ? //設(shè)置為SCSI掃描準(zhǔn)備的hostdata us->host->hostdata[0] = (unsigned long) us; ? //啟動(dòng)控制線程 p = kernel_thread(usb_stor_control_thread, us, CLONE_VM); if (p < 0) { printk(KERN_WARNING USB_STORAGE "Unable to start control threadn"); return p; } us->pid = p; ? //等待線程啟動(dòng) wait_for_completion(&(us->notify)); ? return 0; }
SCSI主機(jī)模板結(jié)構(gòu)被用來分配SCSI主機(jī),結(jié)構(gòu)實(shí)例usb_stor_host_template列出如下(在drivers/usb/storage/scsiglue.c中):
struct scsi_host_template usb_stor_host_template = { //基本的用戶使用的接口 .name = "usb-storage", .proc_name = "usb-storage", .proc_info = proc_info, .info = host_info, ? //命令接口,僅用于排隊(duì) .queuecommand = queuecommand, ? //錯(cuò)誤及錯(cuò)誤退出處理函數(shù) .eh_abort_handler = command_abort, .eh_device_reset_handler = device_reset, .eh_bus_reset_handler = bus_reset, ? //排隊(duì)命令數(shù),每個(gè)LUN僅一個(gè)命令?!? .can_queue = 1, .cmd_per_lun = 1, ? /* unknown initiator id */ .this_id = -1, ? .slave_alloc = slave_alloc, .slave_configure = slave_configure, //設(shè)置一些限制 ? //能被處理的碎片收集片斷數(shù) .sg_tablesize = SG_ALL, ? //一次傳輸?shù)目偞笮∠拗频?40扇區(qū),即120 KB .max_sectors = 240, ? //融合命令... .use_clustering = 1, ? //模擬HBA .emulated = 1, ? //當(dāng)設(shè)備或總線復(fù)位后做延遲操作。 .skip_settle_delay = 1, ? //sysfs設(shè)備屬性 .sdev_attrs = sysfs_device_attr_list, ? /* 用于內(nèi)核模塊管理 */ .module = THIS_MODULE };
線程函數(shù)usb_stor_control_thread分析處理SCSI命令請(qǐng)求描述塊srb后,調(diào)用協(xié)議處理函數(shù)us->proto_handler來進(jìn)行封裝傳輸。函數(shù)usb_stor_control_thread列出如下:
static int usb_stor_control_thread(void * __us) { struct us_data *us = (struct us_data *)__us; struct Scsi_Host *host = us->host; ? lock_kernel(); ? //線程后臺(tái)化,定向成從init進(jìn)程繼承,這樣就去掉了不需要的進(jìn)程資源 daemonize("usb-storage"); ? current->flags |= PF_NOFREEZE; ? unlock_kernel(); ? //發(fā)信號(hào)表示我們已開始了這個(gè)線程 complete(&(us->notify)); ? for(;;) { US_DEBUGP("*** thread sleeping.n"); if(down_interruptible(&us->sema)) break; ? US_DEBUGP("*** thread awakened.n"); ? /* lock the device pointers */ down(&(us->dev_semaphore)); ? //如果us->srb是NULL, 線程被請(qǐng)求退出。 if (us->srb == NULL) { US_DEBUGP("-- exit command receivedn"); up(&(us->dev_semaphore)); break; } ? //鎖住SCSI主機(jī)控制器 scsi_lock(host); ? //命令超時(shí) if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { us->srb->result = DID_ABORT << 16; goto SkipForAbort; } ? //如果USB總線是斷開狀態(tài),就不做任何事?!? if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { US_DEBUGP("No command during disconnectn"); goto SkipForDisconnect; } ? scsi_unlock(host); ? //如果方向標(biāo)識(shí)是未知的,拒絕命令。 if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) { US_DEBUGP("UNKNOWN data directionn"); us->srb->result = DID_ERROR << 16; } ? //如果target?!= 0 或LUN超過最大的已知LUN數(shù),拒絕命令。 else if (us->srb->device->id && !(us->flags & US_FL_SCM_MULT_TARG)) { US_DEBUGP("Bad target number (%d:%d)n", us->srb->device->id, us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } ? else if (us->srb->device->lun > us->max_lun) { US_DEBUGP("Bad LUN (%d:%d)n", us->srb->device->id, us->srb->device->lun); us->srb->result = DID_BAD_TARGET << 16; } ? //處理需要偽裝它們的查詢數(shù)據(jù)的設(shè)備 else if ((us->srb->cmnd[0] == INQUIRY) && (us->flags & US_FL_FIX_INQUIRY)) { unsigned char data_ptr[36] = { 0x00, 0x80, 0x02, 0x02, 0x1F, 0x00, 0x00, 0x00}; ? US_DEBUGP("Faking INQUIRY commandn"); fill_inquiry_response(us, data_ptr, 36); us->srb->result = SAM_STAT_GOOD; } ? //得到一個(gè)命令,按照功能設(shè)備支持的協(xié)議來轉(zhuǎn)換SCSI命令 else { US_DEBUG(usb_stor_show_command(us->srb)); us->proto_handler(us->srb, us); } ? /* 加鎖 */ scsi_lock(host); ? //指示命令執(zhí)行完成 if (us->srb->result != DID_ABORT << 16) { US_DEBUGP("scsi cmd done, result=0x%xn", us->srb->result); us->srb->scsi_done(us->srb); } else { SkipForAbort: US_DEBUGP("scsi command abortedn"); } ? /*如果一個(gè)錯(cuò)誤退出請(qǐng)求被收到,我們需要發(fā)信號(hào)表示退出完成了。應(yīng)該測(cè)試TIMED_OUT標(biāo)識(shí)而不是srb->result == DID_ABORT,因?yàn)閠imeout/abort請(qǐng)求可能在所有的USB處理完成后被收到的*/ if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) complete(&(us->notify)); ? //完成了在這個(gè)命令上的操作 SkipForDisconnect: us->srb = NULL; scsi_unlock(host); ? /* 解鎖*/ up(&(us->dev_semaphore)); } /* for (;;) */ ? //通知exit例程我們實(shí)際上正在退出操作。 complete_and_exit(&(us->notify), 0); }
對(duì)于支持SCSI協(xié)議的功能設(shè)備來說,us->proto_handler協(xié)議處理函數(shù)就是函數(shù) usb_stor_transparent_scsi_command,該函數(shù)把SCSI命令發(fā)送到傳輸層處理。該函數(shù)列出如下(在 drivers/usb/storage/protocol.c中):
void usb_stor_transparent_scsi_command(struct scsi_cmnd *srb, struct us_data *us) { //發(fā)送命令到傳輸層 usb_stor_invoke_transport(srb, us); ? if (srb->result == SAM_STAT_GOOD) { /* Fix the READ CAPACITY result if necessary */ if (us->flags & US_FL_FIX_CAPACITY) fix_read_capacity(srb); } }
函數(shù)usb_stor_invoke_transport是傳輸例程,它觸發(fā)傳輸和基本的錯(cuò)誤處理/恢復(fù)方法,它被協(xié)議層用來實(shí)際發(fā)送消息到設(shè)備并接收響應(yīng)。
函數(shù)usb_stor_invoke_transport列出如下(在drivers/usb/storage/transport.c中):
void usb_stor_invoke_transport(struct scsi_cmnd *srb, struct us_data *us) { int need_auto_sense; int result; ? //發(fā)送命令到傳輸層 srb->resid = 0; result = us->transport(srb, us); ? …… srb->result = SAM_STAT_GOOD; ? //決定是否需要auto-sense標(biāo)識(shí) need_auto_sense = 0; ? /*如果我們正在支持CB傳輸,它不能決定它自己的狀態(tài),我們將自動(dòng)感知(auto-sense),除非操作包括在一個(gè)data-in的傳輸中。設(shè)備能通過安裝bulk-in管道來發(fā)出大多關(guān)開data-in錯(cuò)誤的信號(hào)。*/ if ((us->protocol == US_PR_CB || us->protocol == US_PR_DPCM_USB) && srb->sc_data_direction != DMA_FROM_DEVICE) { US_DEBUGP("-- CB transport device requiring auto-sensen"); need_auto_sense = 1; } ? //如果有一個(gè)操作失敗,我們將自動(dòng)做REQUEST_SENSE。 //注意在傳輸機(jī)制中在“失敗”和“錯(cuò)誤”之間的命令是不同的。 if (result == USB_STOR_TRANSPORT_FAILED) { US_DEBUGP("-- transport indicates command failuren"); need_auto_sense = 1; } ? …… //做auto-sense if (need_auto_sense) { int temp_result; void* old_request_buffer; unsigned short old_sg; unsigned old_request_bufflen; unsigned char old_sc_data_direction; unsigned char old_cmd_len; unsigned char old_cmnd[MAX_COMMAND_SIZE]; unsigned long old_serial_number; int old_resid; ? US_DEBUGP("Issuing auto-REQUEST_SENSEn"); ? //存儲(chǔ)舊的命令 memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE); old_cmd_len = srb->cmd_len; ? //設(shè)置命令和LUN memset(srb->cmnd, 0, MAX_COMMAND_SIZE); srb->cmnd[0] = REQUEST_SENSE; srb->cmnd[1] = old_cmnd[1] & 0xE0; srb->cmnd[4] = 18; ? //在這兒必須做協(xié)議轉(zhuǎn)換 if (us->subclass == US_SC_RBC || us->subclass == US_SC_SCSI) srb->cmd_len = 6; else srb->cmd_len = 12; ? //設(shè)置傳輸方向 old_sc_data_direction = srb->sc_data_direction; srb->sc_data_direction = DMA_FROM_DEVICE; ? //存buffer中內(nèi)容 old_request_buffer = srb->request_buffer; srb->request_buffer = srb->sense_buffer; ? //設(shè)置buffer傳輸長(zhǎng)度 old_request_bufflen = srb->request_bufflen; srb->request_bufflen = 18; ? //存碎片收集鏈表中的碎片數(shù) old_sg = srb->use_sg; srb->use_sg = 0; ? //改變序號(hào) – 或非高位 old_serial_number = srb->serial_number; srb->serial_number ^= 0x80000000; ? //發(fā)生auto-sense命令 old_resid = srb->resid; srb->resid = 0; temp_result = us->transport(us->srb, us); ? //恢復(fù)命令 srb->resid = old_resid; srb->request_buffer = old_request_buffer; srb->request_bufflen = old_request_bufflen; srb->use_sg = old_sg; srb->serial_number = old_serial_number; srb->sc_data_direction = old_sc_data_direction; srb->cmd_len = old_cmd_len; memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE); ? …… /*設(shè)置result,讓上層得到此值*/ srb->result = SAM_STAT_CHECK_CONDITION; ? //如果ok,顯示它們,sense buffer清0,這樣不讓高層認(rèn)識(shí)到我們做了一個(gè)自愿的auto-sense if (result == USB_STOR_TRANSPORT_GOOD && /* Filemark 0, ignore EOM, ILI 0, no sense */ (srb->sense_buffer[2] & 0xaf) == 0 && /* 沒有ASC或ASCQ */ srb->sense_buffer[12] == 0 && srb->sense_buffer[13] == 0) { srb->result = SAM_STAT_GOOD; srb->sense_buffer[0] = 0x0; } } ? //我們傳輸小于所要求的最小數(shù)據(jù)量 if (srb->result == SAM_STAT_GOOD && srb->request_bufflen - srb->resid < srb->underflow) srb->result = (DID_ERROR << 16) | (SUGGEST_RETRY << 24); ? return; ? //出錯(cuò)退出處理:bulk-only傳輸在一個(gè)出錯(cuò)退出后請(qǐng)求一個(gè)復(fù)位操作 Handle_Abort: srb->result = DID_ABORT << 16; if (us->protocol == US_PR_BULK) us->transport_reset(us); }
對(duì)于USB Mass Storage相適應(yīng)的設(shè)備來說,us->transport(srb, us)調(diào)用的是函數(shù)usb_stor_Bulk_transport,該函數(shù)列出如下(在drivers/usb/storage/transport.c中):
int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) { struct bulk_cb_wrap *bcb = (struct bulk_cb_wrap *) us->iobuf; struct bulk_cs_wrap *bcs = (struct bulk_cs_wrap *) us->iobuf; unsigned int transfer_length = srb->request_bufflen; unsigned int residue; int result; int fake_sense = 0; unsigned int cswlen; unsigned int cbwlen = US_BULK_CB_WRAP_LEN; ? //對(duì)于BULK32設(shè)備,設(shè)置多余字節(jié)到0 if ( unlikely(us->flags & US_FL_BULK32)) {