當(dāng)前位置:首頁 > 芯聞號(hào) > 充電吧
[導(dǎo)讀]http://www.shangshuwu.cn/index.php/Linux%E5%86%85%E6%A0%B8USB%E4%B8%BB%E8%AE%BE%E5%A4%87%E9%A9%B1%E5

http://www.shangshuwu.cn/index.php/Linux%E5%86%85%E6%A0%B8USB%E4%B8%BB%E8%AE%BE%E5%A4%87%E9%A9%B1%E5%8A%A8%E7%A8%8B%E5%BA%8F
目錄 [隱藏] 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 探測函數(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è) 備。


圖4 USB驅(qū)動(dòng)程序系統(tǒng)框架圖

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通用構(gòu)架圖

EHCI支持兩種類型傳輸:異步類型和周期類型。周期類型包括同步傳輸和中斷傳輸,異步類型包括控制傳輸和批量傳輸。EHCI調(diào)度接口給兩種類型提 供了分開的調(diào)度。周期調(diào)度基于時(shí)間發(fā)起的幀鏈表,它代表主機(jī)控制器工作條目的滑動(dòng)窗口。所有的同步和中斷傳輸都通過周期調(diào)度來進(jìn)行。

異步調(diào)度是簡單的調(diào)度工作條目的循環(huán)鏈表,它給所有異步傳輸提供了循環(huán)調(diào)度服務(wù)。

EHCI使用一個(gè)簡單的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, //探測函數(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è)置提供了簡單的端口狀態(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中的探測函數(shù)usb_hcd_pci_probe:

函數(shù) usb_hcd_pci_probe初始化基于PCI的HCD(主機(jī)控制器驅(qū)動(dòng)程序),參數(shù)dev是被探測的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開始,長度為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; }


Mass Storage主機(jī)驅(qū)動(dòng)程序 Mass Storage規(guī)范介紹

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ǔ)類遵從測試規(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)。 精簡塊命令(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所示。


圖6 USB接口大存儲(chǔ)設(shè)備的操作流程圖

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所示。

表1 命令塊數(shù)據(jù)包裹器(CBW)格式表
Byte Bit 7 6 5 4 3 2 1 0 0-3 dCBWSignature 4-7 dCBWTag 8-11
(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ù)傳輸長度  __u8 CDB[16];  //傳輸?shù)拿钭止?jié) };


命令狀態(tài)數(shù)據(jù)包裹器(CSW)的格式如表2所示。

表2 命令狀態(tài)數(shù)據(jù)包(CSW)的格式表
Byte Bit 7 6 5 4 3 2 1 0 0-3 dCSWSignature 4-7 dCSWTag 8-11(Bh) dCSWDataResidue 12(Ch) dCSWStatus

命令狀態(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ī)定長度的數(shù)據(jù)CBWCB;當(dāng)傳輸方向是從主機(jī)到設(shè) 備時(shí),則當(dāng)CBW發(fā)送成功后,向設(shè)備的Out端點(diǎn)發(fā)送CBW中規(guī)定長度的數(shù)據(jù)CBWCB。CBWCB是命令塊數(shù)據(jù),是遵循某一規(guī)范的命令集, 如:SCSI-2命令集,最長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ā)送接收的。


圖 主機(jī)從設(shè)備讀到數(shù)據(jù)的流程圖

第0到第5包的數(shù)據(jù)格式圖列出如圖7所示:

圖7 第0到第5包的數(shù)據(jù)格式圖

在第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ù)傳輸長度,在此情況下是0000,0200H=512字節(jié);80 是后面固有的標(biāo)志碼;00 是后面固有的CBWLUN;0A 是CBWCB長度,意味著命令描述塊(CDB)長度是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所示:

圖8 CSW包的數(shù)據(jù)格式圖

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所示。

表8一個(gè)典型的SCSI命令描述塊結(jié)構(gòu)
? 7 6 5 4 3 2 1 0 0 操作碼 1

命令的指定參數(shù) … … n-1 n 控制字節(jié)

  SCSI命令描述塊的結(jié)構(gòu)的各項(xiàng)說明如下:

操作碼(Opcode)

  每個(gè)命令的0號(hào)字節(jié)就是操作碼,它定義了命令的類型和長度。它的高3位代表了命令所屬的命令組,低5位表示命令本身。每個(gè)命令組都有一個(gè)命令長度。因而,對(duì)命令的第一個(gè)字節(jié)進(jìn)行解碼以后,目標(biāo)器就知道這個(gè)命令還剩下多少字節(jié)。操作碼在不同設(shè)備上含義是不同的。

SCSI常用命令塊有查詢、讀請(qǐng)求、測試單元準(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所示。

表9 SCSI命令組說明
組 操作碼 說明 0 00h~1Fh 6字節(jié)命令 1 20h~3Fh 10字節(jié)命令 2 40h~5Fh 10字節(jié)命令 3 60h~7Fh 保留 4 80h~9Fh 16字節(jié)命令 5 A0h~BFh 12字節(jié)命令 6 C0h~DFh 廠商自定 7 E0h~FFh 廠商自定


控制字節(jié)

控制字節(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ì)地址。

表10 控制字節(jié)的格式
位數(shù) 7 6 5 4 3 2 1 0 ? 廠商自定 保留 ACA 狀態(tài) 連接

標(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, //探測并初始化設(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ū)動(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。


圖2 函數(shù)storage_probe調(diào)用層次圖

函數(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),掃描探測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)該測試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傳輸長度   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)) {        
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動(dòng)力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉