Linux內(nèi)核USB主設(shè)備驅(qū)動程序
目錄 [隱藏] 1 ehci-hcd控制器 1.1 EHCI構(gòu)架介紹1.2 EHCI驅(qū)動程序分析2 Mass Storage主機驅(qū)動程序 2.1 Mass Storage規(guī)范介紹2.2 Bulk-Only傳輸協(xié)議介紹2.3 SCSI命令描述塊結(jié)構(gòu)2.4 Mass Storage設(shè)備對象結(jié)構(gòu)2.5 Mass Storage設(shè)備初始化2.6 探測函數(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在硬件實現(xiàn)以及對底層軟件訪問上都有所不同,但二者又都完全USB 1.1中對主控制器的要求。
USB主控制器驅(qū)動程序的整個系統(tǒng)框架圖如圖4所示,從圖中可以看出USB驅(qū)動程序包括客戶驅(qū)動、通用總線驅(qū)動程序、EHCI驅(qū)動程序等組 成。其中,客戶驅(qū)動程序是特定USB設(shè)備的驅(qū)動程序,提供了USB設(shè)備的功能操作及特定子類協(xié)議封裝;USB驅(qū)動程序(USBD)是特定操作系統(tǒng)上抽象出 的主機控制器驅(qū)動程序共有特性,對應(yīng)于Linux USB驅(qū)動程序的HCD層;EHCI控制器驅(qū)動程序(EHCD)是依賴于特定硬件寄存器接口定義的主控制器驅(qū)動程序。USB設(shè)備是執(zhí)行終端用戶功能硬件設(shè) 備。
EHCI通用構(gòu)架如圖5所示。每個EHCI接口定義了三個接口空間,該個接口空間說明如下:
PCI配置空間包括PCI寄存器,它們用來系統(tǒng)部件枚舉和PCI電源管理。 寄存器空間,通常稱為I/O空間。它必須被用作內(nèi)存映射I/O空間。它包括特定應(yīng)用參數(shù)寄存器和能力寄存器、加上可選的控制和狀態(tài)寄存器。 調(diào)度接口空間是特殊分配的內(nèi)存并且被EHCI驅(qū)動程序管理用來周期性或異步調(diào)度。EHCI支持兩種類型傳輸:異步類型和周期類型。周期類型包括同步傳輸和中斷傳輸,異步類型包括控制傳輸和批量傳輸。EHCI調(diào)度接口給兩種類型提 供了分開的調(diào)度。周期調(diào)度基于時間發(fā)起的幀鏈表,它代表主機控制器工作條目的滑動窗口。所有的同步和中斷傳輸都通過周期調(diào)度來進行。
異步調(diào)度是簡單的調(diào)度工作條目的循環(huán)鏈表,它給所有異步傳輸提供了循環(huán)調(diào)度服務(wù)。
EHCI使用一個簡單的buffer隊列數(shù)據(jù)結(jié)構(gòu)來管理所有的中斷、批量和控制傳輸類型。排隊的數(shù)據(jù)結(jié)構(gòu)提供了自動的、排序的數(shù)據(jù)傳輸流。軟件能異步地加數(shù)據(jù)buffer到一個隊列并維護數(shù)據(jù)流。USB定義的短包語法在沒有軟件干預(yù)下完全支持所有的邊界條件處理。
USB總線的主機控制器要求應(yīng)用根集線器,主機控制器模擬了根集線器,它在操作寄存器空間裝有端口寄存器,寄存器含有在USB規(guī)范中需要管 理每個端口的最小硬件狀態(tài)和控制。事務(wù)通過根端口被廣播下流的USB設(shè)備,端口寄存器提供給系統(tǒng)軟件對端口的管理和端口的狀態(tài)信息,包括:設(shè)備的連接與斷 開、執(zhí)行設(shè)備復(fù)位、處理端口功率和端口電源管理。
EHCI控制器提供了兩套軟件可訪問的寄存器:內(nèi)存映射的主機控制器寄存器和可選的PCI配置寄存器。PCI配置寄存器僅是用到主機控制器的PCI設(shè)備需要的。
主機控制器能力寄存器定義了限制、主機控制器使用的能力,如:下行端口數(shù)、主機控制器的接口版本號、同步調(diào)度門限等。在代碼中使用結(jié)構(gòu)ehci_caps來描述。
主機控制器操作寄存器位于能力寄存器之后,是雙字對齊讀寫寄存器。這些寄存器分為兩套,第一套從地址00到3Fh,在主控制器核心電源好的 情況下使用,包括USB控制命令、狀態(tài)、中斷使能、幀序號寄存器。第二套寄存器從40h到可使用的寄存器空間結(jié)尾,在外圍輔助電源好的情況下使用,包括每 個端口的狀態(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)、隊列頭(QH)和隊列元素傳輸描述子(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主機控制器帶有一個模擬操作的根集線器,通過寄存器可完成對根集線器的各個端口的狀態(tài)及連接控制,因此,它不會調(diào)用到USB核心層中有關(guān)HUB的操作函數(shù)。
EHCI主機控制器對于URB的提交排隊及傳輸、調(diào)度以及控制器的各種狀態(tài)轉(zhuǎn)移提供了控制。特別是寄存器級的控制函數(shù)與EHCI控制器本身結(jié)構(gòu)相關(guān),牽涉到對眾多寄存器值的理解,因而這里只說明了ehci控制器的上層功能函數(shù)。
EHCI驅(qū)動程序分析EHCI驅(qū)動程序的編寫思路是:EHCI驅(qū)動程序是一個結(jié)構(gòu)hc_driver實例,它應(yīng)該實現(xiàn)結(jié)構(gòu)hc_driver中的函數(shù),另外,從硬件上層 來看,EHCI主控制從PCI總線橋接,應(yīng)是一個PCI驅(qū)動程序?qū)嵗?,因此,?yīng)實現(xiàn)結(jié)構(gòu)pci_driver中的函數(shù),并用PCI注冊函數(shù) pci_register_driver注冊此實例。
函數(shù)__init init注冊了&ehci_pci_driver控制器驅(qū)動程序,由于ehci-hcd是通過PCI總線與CPU相連,因而,它被注冊成一個新的PCI驅(qū)動程序。
函數(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 //注冊驅(qū)動程序,初始化ehci_pci_driver并加到內(nèi)核對象體系中去 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ū)動程序結(jié)構(gòu)實例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, //探測函數(shù) .remove = usb_hcd_pci_remove, //移去設(shè)備時的清除函數(shù) ? #ifdef CONFIG_PM .suspend = usb_hcd_pci_suspend, .resume = usb_hcd_pci_resume, #endif };
pci_ids 是PCI驅(qū)動程序選擇元數(shù)據(jù),PCI熱插拔使用到它,通過它來選擇驅(qū)動程序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是主機控制器結(jié)構(gòu)hc_driver實例,它描述了EHCI控制器信息及各種操作函數(shù),每個主機控制器都有一個這樣的結(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ù)位到停止狀態(tài)。 //開始運行,初始化ECHI設(shè)備的各種寄存器進入運行狀態(tài), //調(diào)用函數(shù)hcd_register_root注冊根集線器驅(qū)動程序。 .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請求和相關(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, //得到當前的幀序號 ? /* root hub支持,EHCI主機控制器使用了它自己的模擬根集線器,這個集線器通過對寄存器的設(shè)置提供了簡單的端口狀態(tài)及連接控制功能。*/ .hub_status_data = ehci_hub_status_data,//端口狀態(tài)發(fā)生變化時,直接控制端口 .hub_control = ehci_hub_control, //hub控制的狀態(tài)機 .hub_suspend = ehci_hub_suspend, .hub_resume = ehci_hub_resume,//電源恢復(fù),初始化根集線器 };
下面分析只分析結(jié)構(gòu)實例ehci_pci_driver中的探測函數(shù)usb_hcd_pci_probe:
函數(shù) usb_hcd_pci_probe初始化基于PCI的HCD(主機控制器驅(qū)動程序),參數(shù)dev是被探測的USB主機控制器,參數(shù)id是連接控制器到HCD構(gòu)架的pci熱插拔設(shè)備ID。這個函數(shù)不能從中斷上下文中調(diào)用。
函數(shù) usb_hcd_pci_probe作為probe()存在HCD的pci_driver結(jié)構(gòu)中,它分配基本的PCI資源給這個USB控制器:它分配一個 PCI資源區(qū)域、進行I/O映射、創(chuàng)建usb_hcd結(jié)構(gòu)實例并賦上設(shè)備操作函數(shù)集&usb_hcd_operations,給hcd創(chuàng)建DMA 緩沖池,申請中斷,注冊總線。通過與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ū)動程序結(jié)構(gòu)。 if (!id || !(driver = (struct hc_driver *) id->driver_data)) return -EINVAL; ? //使設(shè)備的I/O和設(shè)備內(nèi)存區(qū)有效,喚醒設(shè)備,在被驅(qū)動程序使用前初始化設(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號區(qū)域資源 len = pci_resource_len (dev, 0); //申請名字為driver->description的I/O內(nèi)存區(qū)域, //從resource開始,長度為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; //標準PCI配置6個region(或說6個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); //申請名字為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ū)動程序結(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個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已在一個不確定狀態(tài),調(diào)用驅(qū)動程序的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); … //申請共享中斷號,中斷處理函數(shù)是usb_hcd_irq,設(shè)備名為description retval = request_irq (dev->irq, usb_hcd_irq, SA_SHIRQ, hcd->driver->description, hcd); … hcd->irq = dev->irq; ? //注冊總線到sysfs和/proc文件系統(tǒng) usb_register_bus (&hcd->self); …… return retval; }
函數(shù) usb_create_hcd創(chuàng)建并初始化一個結(jié)構(gòu)usb_hcd實例,參數(shù)driver是此HCD使用的HCD驅(qū)動程序。如果內(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大存儲(USB Mass Storage)工作組(CWG Class Working Group)規(guī)范包括:
USB Mass Storage Class Control/Bulk/Interrupt(CBI) Transport 即USB大存儲類控制/批量/中斷傳輸協(xié)議。 USB Mass Storage Class Bulk-Only Transport 即USB大存儲類批量傳輸協(xié)議。 USB Mass Storage Class UFI Command Specification 即USB大存儲類UFI命令規(guī)范。 USB Mass Storage Class Bootability Specfication 即USB大存儲類系統(tǒng)啟動規(guī)范。 USB Mass Storage Class Compliance Test Specification 即USB大存儲類遵從測試規(guī)范。其中,CBI傳輸規(guī)范僅用于全速軟盤驅(qū)動器,不能用于高速設(shè)備或其它非軟盤設(shè)備。
USB大存儲類使用幾種命令集規(guī)范,這些命令集的命令塊放在符合USB協(xié)議的USB包裹器中,USB大存儲類規(guī)范定義了下面幾種命令集:
軟驅(qū)、光驅(qū)和磁帶驅(qū)動器使用的ATAPI規(guī)范(Advanced Technology Attachment Packet Interface)。 精簡塊命令(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大存儲類設(shè)備的接口描述子包含了一個bInterfaceSubClass和bInterfacePortocal的 域,bInterfaceSubClass描述了USB大存儲類支持的命令塊規(guī)范,如:它為06h時表示支持的是SCSI傳輸命令集。 bInterfacePortocal描述了USB大存儲類支持的接口傳輸協(xié)議,如:它為50h時表示支持的是Bulk-Only傳輸協(xié)議。
大存儲設(shè)備(Mass Storage)包括U盤、讀卡器及USB接口的光驅(qū)等其它塊存儲設(shè)備,它們看作是SCSI接口設(shè)備,當用戶從設(shè)備上讀寫數(shù)據(jù)時,文件系統(tǒng)將讀寫操作傳送 到SCSI協(xié)議層,SCSI協(xié)議層的讀寫請求封裝成USB請求塊(URB)通過USB接口傳遞給設(shè)備,USB設(shè)備從URB中解析出SCSI協(xié)議命令后再操 作塊設(shè)備。USB接口大存儲設(shè)備的操作流程圖如圖6所示。
USB接口大存儲設(shè)備驅(qū)動程序的設(shè)計思路是:設(shè)計一個控制線程,這個線程被注冊為虛擬SCSI控制器,這個線程在設(shè)備插入/移去時一直作為SCSI 節(jié)點存在的。這樣,被移去的設(shè)備能在再插上時被給以與以前/dev中同一節(jié)點。當一個設(shè)備被插上時,控制線程從SCSI中間層代碼得到命令。控制線程接收 命令,在檢查后送命令到協(xié)議處理函數(shù)。這些處理函數(shù)負責再寫命令(如果必要)到設(shè)備得接受的形式。例如:ATAPI設(shè)備不能支持6byte命令,這樣,它 們必須被再寫成10byte變量。一旦協(xié)議處理函數(shù)已再寫了命令,它們被送到傳輸處理函數(shù)。傳輸處理函數(shù)負責送命令到設(shè)備、交換數(shù)據(jù)、并接著得到設(shè)備的狀 態(tài)。在協(xié)議處理函數(shù)和傳輸處理函數(shù)之間有一小段代碼,來決定REQUEST_SENSE命令是否應(yīng)該發(fā)出。在命令被處理后,scsi_done()被調(diào)用 來發(fā)信號給SCSI層命令已完成。我們準備接收下一條命令。
作為具有操作系統(tǒng)的智能嵌入設(shè)備,它使用了SCSI命令塊集與Bulk-only傳輸協(xié)議。它既能作為主機來操作其它USB大存儲設(shè)備,稱 為大存儲設(shè)備主機。同時,也能作為USB大存儲設(shè)備被其它主機控制。下面對具有l(wèi)inux操作系統(tǒng)的嵌入設(shè)備分別就兩種模式分別進行分析。
Bulk-Only傳輸協(xié)議介紹Bulk-Only傳輸協(xié)議是USB大容量存貯器類中的USB批量數(shù)據(jù)傳輸協(xié)議,它定義了僅通過批量端點傳輸?shù)拿?、?shù)據(jù)和狀態(tài)。它使用命令塊數(shù)據(jù) 包裹器(CBW)發(fā)送命令,使用命令狀態(tài)數(shù)據(jù)包裹器(CSW)接收返回的狀態(tài)。命令塊數(shù)據(jù)包裹器(CBW)是一個包含命令塊和相關(guān)信息的數(shù)據(jù)包。 命令狀態(tài)數(shù)據(jù)包(CSW)裹器是一個包含命令塊狀態(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; //每個命令唯一的ID __le32 DataTransferLength; //數(shù)據(jù)大小 __u8 Flags; //在bit 0中表示方向 __u8 Lun; //表示LUN(SCSI邏輯單元)正常為0 __u8 Length; //數(shù)據(jù)傳輸長度 __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)標識,如:成功、失敗等 __u8 Filler[18]; };
傳輸過程是:當傳輸方向是從設(shè)備到主機時,則當CBW發(fā)送成功后,設(shè)備從設(shè)備的In端點讀取CBW中規(guī)定長度的數(shù)據(jù)CBWCB;當傳輸方向是從主機到設(shè) 備時,則當CBW發(fā)送成功后,向設(shè)備的Out端點發(fā)送CBW中規(guī)定長度的數(shù)據(jù)CBWCB。CBWCB是命令塊數(shù)據(jù),是遵循某一規(guī)范的命令集, 如:SCSI-2命令集,最長16字節(jié)。
當主機與設(shè)備之間的數(shù)據(jù)傳送完畢后,主機還需從設(shè)備的In端點讀取傳送狀態(tài),主機根據(jù)接收的CSW數(shù)據(jù)包即可判斷出通信是否正常。若返回的結(jié)果有錯誤,還須進行相應(yīng)的出錯處理。
樣例:從設(shè)備讀取數(shù)據(jù)的傳輸過程
下面是一個從設(shè)備讀取數(shù)據(jù)的傳輸過程的例子,主機先向端點1發(fā)出CBW命令,設(shè)備解析CBW解析命令后,從主機指定的端點2將數(shù)據(jù)傳回給主 機。在傳送成功后,主機又讀取端點2的狀態(tài)CSW。主機從設(shè)備讀到數(shù)據(jù)的流程圖如下圖。從圖中可看出,第0到第2包是發(fā)送CBW的過程,第3到第5包是讀 取數(shù)據(jù)的過程,下面接著的第0到第1包是讀取CSW的過程。令牌包和握手包是由控制管道(對應(yīng)ep0)來發(fā)送接收的。
第0到第5包的數(shù)據(jù)格式圖列出如圖7所示:
在第1包中,CBW傳輸了31(1FH)個字節(jié)的數(shù)據(jù)。內(nèi)容含義是:55 53 42 43 是CBW后面固有的特征碼;28 E8 31 FE 是由主機產(chǎn)生的CBWTag;00 02 00 00 是CBW數(shù)據(jù)傳輸長度,在此情況下是0000,0200H=512字節(jié);80 是后面固有的標志碼;00 是后面固有的CBWLUN;0A 是CBWCB長度,意味著命令描述塊(CDB)長度是10字節(jié),其中。28表示對應(yīng)SCSI協(xié)議28h讀命令。對于命令塊,看下節(jié)的SCSI命令描述塊的 結(jié)構(gòu)。
SCSI協(xié)議28h讀命令是Read(10),在這個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é)束標志。
在第4包中傳輸了512字節(jié)的數(shù)據(jù)。
CSW包的數(shù)據(jù)格式圖列出如圖8所示:
CSW數(shù)據(jù)包傳輸13(0DH)個字節(jié)的數(shù)據(jù)。內(nèi)容含義是:55 52 42 53是CSW后面固有的特征碼;28 E8 31 FF是主機產(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)的各項說明如下:
操作碼(Opcode)每個命令的0號字節(jié)就是操作碼,它定義了命令的類型和長度。它的高3位代表了命令所屬的命令組,低5位表示命令本身。每個命令組都有一個命令長度。因而,對命令的第一個字節(jié)進行解碼以后,目標器就知道這個命令還剩下多少字節(jié)。操作碼在不同設(shè)備上含義是不同的。
SCSI常用命令塊有查詢、讀請求、測試單元準備、禁止媒介刪除、讀緩沖、寫緩沖等。
命令組代表命令組的高3位可以有8個不同的組合,所以可以代表8個命令組,當制造商實現(xiàn)自己的標準的時候,就必須使用6號組或者7號組,實際上,使用6號組或者7號組的情況很少發(fā)生。命令組的說明如表9所示。
控制字節(jié)的格式如表10所示。SCSI-2中,控制字節(jié)僅僅包含了在標準中定義的兩位,它們是連接位(Link bit)和標志位(flag bit),而且這兩位都是可選的。連接位使你可以將幾個命令連接成一個命令鏈,命令鏈中的每一個命令被稱為連接的命令。從而這些連接的命令就形成了一個連 接的I/O過程。這就可以阻止其他I/O過程的命令插入這個已形成命令鏈的I/O過程,這就是在目標器內(nèi)的優(yōu)化方法。舉個例子,當一個邏輯數(shù)據(jù)塊需要被讀 取一修改一寫回時,這個做法就變得十分有用。而且,連接的命令允許使用邏輯數(shù)據(jù)塊的相對地址。
標志位必須和連接命令一起使用。這引起在連接的命令執(zhí)行結(jié)束之后發(fā)送服務(wù)響應(yīng)LINKED COMMAND COMPLETE(WITH FLAG)(0BH),而不是發(fā)送服務(wù)響應(yīng)LINKED COMMAND COMPLETE(OAH)。這樣,你就可以在一個命令鏈中標出一個特定的命令。
在SCSI-3中出現(xiàn)了新的標志位:ACA位。ACA是偶然事件自動通信(auto contingent allegiance)的縮寫,它是在命令執(zhí)行過程中萬一發(fā)生錯誤時LUN所采取的一種措施。如果ACA位沒有被置"1",那么只要下一個命令從同一個啟 動器中發(fā)出時,該錯誤狀態(tài)就被取消。如果ACA位被置"1",它就會阻止取消錯誤狀態(tài)的行動并保持這種狀態(tài)。
Mass Storage設(shè)備對象結(jié)構(gòu)每個大存儲設(shè)備用一個對象結(jié)構(gòu)us_data來描述它的設(shè)備、管道、SCSI接口、傳輸、協(xié)議等各方面的信息及處理函數(shù)。
結(jié)構(gòu)us_data列出如下(在drivers/usb/storage/usb.h中):
/*我們提供了一個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; //保護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; /* 最初來自過濾器的標識*/ 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)品序列號 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主機數(shù)據(jù)結(jié)構(gòu) struct scsi_cmnd *srb; //當前SCSI命令描述塊 ? //線程信息 int pid; //控制線程 ? //控制和批量通信數(shù)據(jù) struct urb *current_urb; //USB請求 struct usb_ctrlrequest *cr; //USB控制請求的setup數(shù)據(jù) struct usb_sg_request current_sg; //碎片-收集請求 unsigned char *iobuf; //I/O buffer dma_addr_t cr_dma; //控制請求數(shù)據(jù)buffer的DMA地址 dma_addr_t iobuf_dma; // I/O buffer的DMA地址 ? //互斥保護和同步結(jié)構(gòu) struct semaphore sema; /* to sleep thread on */ struct completion notify; //線程開始/結(jié)束時發(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ù) ? //子驅(qū)動程序信息 void *extra; //任何格外的數(shù)據(jù) extra_data_destructor extra_destructor;//格外的數(shù)據(jù)析構(gòu)函數(shù) };
Mass Storage設(shè)備初始化
函數(shù)usb_stor_init注冊和初始化大存儲驅(qū)動程序。函數(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"); ? //注冊驅(qū)動程序,如果操作失敗,返回負值的錯誤代碼 retval = usb_register(&usb_storage_driver); if (retval == 0) printk(KERN_INFO "USB Mass Storage support registered.n"); ? return retval; }
大存儲設(shè)備驅(qū)動程序結(jié)構(gòu)實例usb_storage_driver列出如下:
struct usb_driver usb_storage_driver = { .owner = THIS_MODULE, .name = "usb-storage", .probe = storage_probe, //探測并初始化設(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 */ { } };
探測函數(shù)storage_probe分析
函數(shù)storage_probe 探測看是否能驅(qū)動一個新連接的USB設(shè)備。創(chuàng)建了大存儲設(shè)備控制線程usb_stor_control_thread和SCSI設(shè)備后期掃描線程 usb_stor_scan_thread。函數(shù)storage_probe在控制線程中通過虛擬SCSI主機控制器發(fā)送SCSI命令,經(jīng)Bulk- Only協(xié)議封裝后,再填充為URB包,傳送給USB核心層來發(fā)送給設(shè)備。函數(shù)storage_probe調(diào)用層次圖如圖2所示。下面按照這個圖分析函數(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)對象空間 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表中序號匹配,找到表中對應(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在這兒是一個可接受的反應(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; } //重置配置,重新初始化端點及接口 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; ? //初始化所有需要的動態(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,掃描通道及目標,掃描探測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初始化所有的需要的動態(tài)資源,啟動控制線程,函數(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)對象空間 if (!us->current_urb) { US_DEBUGP("URB allocation failedn"); return -ENOMEM; } ? //當我們執(zhí)行下兩個操作時鎖住設(shè)備?!? down(&us->dev_semaphore); ? //僅對于批量設(shè)備,得到最大邏輯單元值, //在SCSI協(xié)議模型中,每個邏輯單元用來操作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); ? //因為這是一個新設(shè)備,我們需要注冊一個設(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掃描準備的hostdata us->host->hostdata[0] = (unsigned long) us; ? //啟動控制線程 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; ? //等待線程啟動 wait_for_completion(&(us->notify)); ? return 0; }
SCSI主機模板結(jié)構(gòu)被用來分配SCSI主機,結(jié)構(gòu)實例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, ? //命令接口,僅用于排隊 .queuecommand = queuecommand, ? //錯誤及錯誤退出處理函數(shù) .eh_abort_handler = command_abort, .eh_device_reset_handler = device_reset, .eh_bus_reset_handler = bus_reset, ? //排隊命令數(shù),每個LUN僅一個命令?!? .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, ? //當設(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命令請求描述塊srb后,調(diào)用協(xié)議處理函數(shù)us->proto_handler來進行封裝傳輸。函數(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(); ? //線程后臺化,定向成從init進程繼承,這樣就去掉了不需要的進程資源 daemonize("usb-storage"); ? current->flags |= PF_NOFREEZE; ? unlock_kernel(); ? //發(fā)信號表示我們已開始了這個線程 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, 線程被請求退出。 if (us->srb == NULL) { US_DEBUGP("-- exit command receivedn"); up(&(us->dev_semaphore)); break; } ? //鎖住SCSI主機控制器 scsi_lock(host); ? //命令超時 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); ? //如果方向標識是未知的,拒絕命令?!? 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; } ? //得到一個命令,按照功能設(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"); } ? /*如果一個錯誤退出請求被收到,我們需要發(fā)信號表示退出完成了。應(yīng)該測試TIMED_OUT標識而不是srb->result == DID_ABORT,因為timeout/abort請求可能在所有的USB處理完成后被收到的*/ if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) complete(&(us->notify)); ? //完成了在這個命令上的操作 SkipForDisconnect: us->srb = NULL; scsi_unlock(host); ? /* 解鎖*/ up(&(us->dev_semaphore)); } /* for (;;) */ ? //通知exit例程我們實際上正在退出操作。 complete_and_exit(&(us->notify), 0); }
對于支持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ā)傳輸和基本的錯誤處理/恢復(fù)方法,它被協(xié)議層用來實際發(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標識 need_auto_sense = 0; ? /*如果我們正在支持CB傳輸,它不能決定它自己的狀態(tài),我們將自動感知(auto-sense),除非操作包括在一個data-in的傳輸中。設(shè)備能通過安裝bulk-in管道來發(fā)出大多關(guān)開data-in錯誤的信號。*/ 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; } ? //如果有一個操作失敗,我們將自動做REQUEST_SENSE。 //注意在傳輸機制中在“失敗”和“錯誤”之間的命令是不同的。 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"); ? //存儲舊的命令 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傳輸長度 old_request_bufflen = srb->request_bufflen; srb->request_bufflen = 18; ? //存碎片收集鏈表中的碎片數(shù) old_sg = srb->use_sg; srb->use_sg = 0; ? //改變序號 – 或非高位 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,這樣不讓高層認識到我們做了一個自愿的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; ? //出錯退出處理:bulk-only傳輸在一個出錯退出后請求一個復(fù)位操作 Handle_Abort: srb->result = DID_ABORT << 16; if (us->protocol == US_PR_BULK) us->transport_reset(us); }
對于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; ? //對于BULK32設(shè)備,設(shè)置多余字節(jié)到0 if ( unlikely(us->flags & US_FL_BULK32)) {