usb驅動的基本結構和函數簡介

幾個重要的結構

struct--接口

struct usb_interface 
{
         /* array of alternate settings for this interface,
          * stored in no particular order */
         struct usb_host_interface *altsetting;

         struct usb_host_interface *cur_altsetting;      /* the currently
                                          * active alternate setting */
         unsigned num_altsetting;        /* number of alternate settings */

         int minor;                      /* minor number this interface is
                                          * bound to */
         enum usb_interface_condition condition;         /* state of binding */
         unsigned is_active:1;           /* the interface is not suspended */
         unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */

         struct device dev;              /* interface specific device info */
         struct device *usb_dev;         /* pointer to the usb class's device, if any */
         int pm_usage_cnt;               /* usage counter for autosuspend */
};

結構體struct usb_host_interface就代表一個設置

struct usb_interface中的struct usb_host_interface *cur_altsetting成員,表示當前正在使用的設置

struct--設置

struct usb_host_interface 
{
         struct usb_interface_descriptor desc;//usb描述符,主要有四種usb描述符,設備描述符,配置描述符,接口描述符和端點描述符,協議裏規定一個usb設備是必須支持這四大描述符的。
                                 //usb描述符放在usb設備的eeprom裏邊
         /* array of desc.bNumEndpoint endpoints associated with this
          * interface setting. these will be in no particular order.
          */
         struct usb_host_endpoint *endpoint;//這個設置所使用的端點

         char *string;           /* iInterface string, if present */
         unsigned char *extra;   /* Extra descriptors */關於額外描述符
         int extralen;
};

具體到接口描述符,它當然就是描述接口本身的信息的。一個接口可以有多個設置,使用不同的設置,描述接口的信息會有些不同,所以接口描述符並沒有放在struct usb_interface結構裏,而是放在表示接口設置的struct usb_host_interface結構裏。

struct--接口描述符

struct usb_interface_descriptor 
{
         __u8  bLength;//接口描述符長度
         __u8 bDescriptorType;//接口描述符類型

         __u8 bInterfaceNumber;//接口號。每個配置可以包含多個接口,這個值就是它們的索引值。
         __u8 bAlternateSetting;//接口使用的是哪個可選設置。協議裏規定,接口默認使用的設置總爲0號設置。
         __u8 bNumEndpoints;//接口擁有的端點數量。這裏並不包括端點0,因爲端點0是控制傳輸,是所有的設備都必須提供的,所以這裏就沒必要多此一舉的包括它了。對於hub,因爲它的傳輸是中斷傳輸,所以此值爲1(不包括端點0)
         __u8 bInterfaceClass;
         __u8 bInterfaceSubClass;//對於hub,這個值是零
         __u8 bInterfaceProtocol;
         __u8 iInterface;
} __attribute__ ((packed));

struct--端點

struct usb_host_endpoint 
{
         struct usb_endpoint_descriptor desc;
         struct list_head                urb_list;//端點要處理的urb隊列.urb是usb通信的主角,設備中的每個端點都可以處理一個urb隊列.要想和你的usb通信,就得創建一個urb,並且爲它賦好值,
                                   //交給咱們的usb core,它會找到合適的host controller,從而進行具體的數據傳輸
         void                            *hcpriv;//這是提供給HCD(host controller driver)用的
         struct ep_device                *ep_dev;        /* For sysfs info */

         unsigned char *extra;   /* Extra descriptors */
         int extralen;
};

struct--端點描述符

struct usb_endpoint_descriptor {
         __u8 bLength;
         __u8 bDescriptorType;//接口類型

         __u8 bEndpointAddress;//它的bits 0~3表示的就是端點號,USB_ENDPOINT_NUMBER_MASK;bit7是方向,USB_ENDPOINT_DIR_MASK
         __u8 bmAttributes;//bmAttributes,屬性,總共8位,其中bit1和bit0 共同稱爲Transfer Type,即傳輸類型, 00 表示控制,01 表示等時,10 表示批量,11 表示中斷。USB_ENDPOINT_XFERTYPE_MASK 
         __le16 wMaxPacketSize;//端點一次可以處理的最大字節數,如果你發送的數據量大於端點的這個值,也會分成多次一次一次來傳輸
         __u8 bInterval;//USB是輪詢式的總線,這個值表達了端點一種美好的期待,希望主機輪詢自己的時間間隔,但實際上批准不批准就是host的事了

         /* NOTE: these two are _only_ in audio endpoints. */
         /* use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. */
         __u8 bRefresh;
         __u8 bSynchAddress;
} __attribute__ ((packed));

注:0號端點沒有自己的端點描述符

struct--設備

struct usb_device {
         int             devnum;         //devnum只是usb設備在一條usb總線上的編號.一條usb_bus_type類型的總線上最多可以連上128個設備
         char            devpath [16];   /* Use in messages: /port/port/*/  //對於root hub.會將dev->devpath[0]=’0’
         enum usb_device_state   state;  //設備的狀態Attached,Powered,Default,Address,Configured,Suspended;
                               //Attached表示設備已經連接到usb接口上了,是hub檢測到設備時的初始狀態。那麼這裏所謂的USB_STATE_NOTATTACHED就是表示設備並沒有Attached。
                               //Address狀態表示主機分配了一個唯一的地址給設備,此時設備可以使用缺省管道響應主機的請求
                               //Configured狀態表示設備已經被主機配置過了,也就是協議裏說的處理了一個帶有非0值的SetConfiguration()請求,此時主機可以使用設備提供的所有功能
                               //Suspended掛起狀態,爲了省電,設備在指定的時間內,3ms吧,如果沒有發生總線傳輸,就要進入掛起狀態。此時,usb設備要自己維護包括地址、配置在內的信息             

         enum usb_device_speed   speed;  /* high/full/low (or error) */
        struct usb_tt   *tt;            //如果一個高速設備裏有這麼一個TT,那麼就可以連接低速/全速設備,如不然,那低速/全速設備沒法用,只能連接到OHCI/UHCI那邊出來的hub口裏。
         int             ttport;         //如果一個高速設備裏有這麼一個TT,那麼就可以連接低速/全速設備,如不然,那低速/全速設備沒法用,只能連接到OHCI/UHCI那邊出來的hub口裏。
         unsigned int toggle[2];         /* one bit for each endpoint     //他實際上就是一個位圖.IN方向的是toggle[0].OUT方向的是toggle[1].其實,這個數組中的每一位表示ep的toggle值
                                          * ([0] = IN, [1] = OUT) */它裏面的每一位表示的就是每個端點當前發送或接收的數據包是DATA0還是DATA1
         struct usb_device *parent;      /* our hub, unless we're the root */
                              //USB設備是從Root Hub開始,一個一個往外面連的,比如Root Hub有4個口,每個口連一個USB設備,比如其中有一個是Hub,那麼這個Hub有可以繼續有多個口,於是一級一級的往下連,
                               //最終連成了一棵樹。
         struct usb_bus *bus;            /* Bus we're part of */設備所在的總線
         struct usb_host_endpoint ep0;   //端點0的特殊地位決定了她必將受到特殊的待遇,在struct usb_device對象產生的時候它就要初始化
         struct device dev;              /* Generic device interface */嵌入到struct usb_device結構裏的struct device結構

         struct usb_device_descriptor descriptor;/* Descriptor */設備描述符,此結構體的bMaxPacketSize0 filed保存了端點0的maximum packet size
        struct usb_host_config *config; //設備擁有的所有配置

         struct usb_host_config *actconfig;//設備正在使用的配置
         struct usb_host_endpoint *ep_in[16];//ep_in[16],359行,ep_out[16],除了端點0,一個設備即使在高速模式下也最多隻能再有15個IN端點和15個OUT端點,端點0太特殊了,
         struct usb_host_endpoint *ep_out[16];//對應的管道是Message管道,又能進又能出特能屈能伸的那種,所以這裏的ep_in和ep_out數組都有16個值
         char **rawdescriptors;          /* Raw descriptors for each config */
         unsigned short bus_mA;          /* Current available from the bus */這個值是在host controller的驅動程序中設置的,通常來講,計算機的usb端口可以提供500mA的電流
         u8 portnum;                     //不管是root hub還是一般的hub,你的USB設備總歸要插在一個hub的端口上才能用,portnum就是那個端口號。
         u8 level;                       //層次,也可以說是級別,表徵usb設備樹的級連關係。Root Hub的level當然就是0,其下面一層就是level 1,再下面一層就是level 2,依此類推

         unsigned discon_suspended:1;    /* Disconnected while suspended */
         unsigned have_langid:1;         /* whether string_langid is valid */
         int string_langid;              /* language ID for strings */
        /* static strings from the device */
         char *product;                  /* iProduct string, if present */
         char *manufacturer;             /* iManufacturer string, if present */
         char *serial;                   /* iSerialNumber string, if present */
                                 //分別用來保存產品、廠商和序列號對應的字符串描述符信息
         struct list_head filelist;
 #ifdef CONFIG_USB_DEVICE_CLASS
         struct device *usb_classdev;
 #endif
 #ifdef CONFIG_USB_DEVICEFS
         struct dentry *usbfs_dentry;    /* usbfs dentry entry for the device */
 #endif
         /*
          * Child devices - these can be either new devices
          * (if this is a hub device), or different instances
          * of this same device.
          *
          * Each instance needs its own set of data structures.
          */

         int maxchild;                   /* Number of ports if hub */
        struct usb_device *children[USB_MAXCHILDREN];
         int pm_usage_cnt;               /* usage counter for autosuspend */
         u32 quirks;                     //quirk就是用來判斷這些有毛病的產品啥毛病的
 #ifdef CONFIG_PM
         struct delayed_work autosuspend; /* for delayed autosuspends */
         struct mutex pm_mutex;          /* protects PM operations */
         unsigned long last_busy;        /* time of last use */
         int autosuspend_delay;          /* in jiffies */
         unsigned auto_pm:1;             /* autosuspend/resume in progress */
         unsigned do_remote_wakeup:1;    /* remote wakeup should be enabled */
         unsigned autosuspend_disabled:1; /* autosuspend and autoresume */
        unsigned autoresume_disabled:1;  /*  disabled by the user */
 #endif
 };

struct--設備描述符


struct usb_device_descriptor {
         __u8  bLength;        
         __u8  bDescriptorType;
         __le16 bcdUSB;            //USB spec的版本號,一個設備如果能夠進行高速傳輸,那麼它設備描述符裏的bcdUSB這一項就應該爲0200H。
         __u8  bDeviceClass;            //
         __u8  bDeviceSubClass;
         __u8  bDeviceProtocol;        //爲了設置tt。full/low speed的hub的bDeviceProtocol是0;對於high speed的hub,其bDeviceProtocol爲1表示是single tt,爲2表示是multiple tt;
         __u8  bMaxPacketSize0;        //端點0一次可以處理的最大字節數
         __le16 idVendor;            //廠商id
        __le16 idProduct;            //產品id
         __le16 bcdDevice;            //設備版本號
         __u8  iManufacturer;
         __u8  iProduct;
         __u8  iSerialNumber;
        __u8  bNumConfigurations;    //設備當前速度模式下支持的配置數量。有的設備可以在多個速度模式下操作,這裏包括的只是當前速度模式下的配置數目,不是總的配置數目
 } __attribute__ ((packed));

 #define USB_DT_DEVICE_SIZE              

18
注:爲什麼端點0的屬性bMaxPacketSize0要放到設備描述符裏邊呢 ?
首先表明了這是一個共性的東西。
前面說端點的時候說了端點0並沒有一個專門的端點描述符,因爲不需要,基本上它所有的特性都在spec裏規定好了的,然而,別忘了這裏說的是“基本上”,有一個特性則是不一樣的,這叫做maximum packet size,每個端點都有這麼一個特性,即告訴你該端點能夠發送或者接收的包的最大值。對於通常的端點來說,這個值被保存在該端點描述符中的wMaxPacketSize這一個field,而對於端點0就不一樣了,由於它自己沒有一個描述符,而每個設備又都有這麼一個端點,所以這個信息被保存在了設備描述符裏,所以我們在設備描述符裏可以看到這麼一項,bMaxPacketSize0。而且spec還規定了,這個值只能是8,16,32或者64這四者之一,如果一個設備工作在高速模式,這個值還只能是64,如果是工作在低速模式,則只能是8,取別的值都不行。

struct--配置

struct usb_config_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;        //這裏的值並不僅僅可以爲USB_DT_CONFIG,還可以爲USB_DT_OTHER_SPEED_CONFIG
        __le16 wTotalLength;        //使用GET_DESCRIPTOR請求從設備裏獲得配置描述符信息時,返回的數據長度
        __u8  bNumInterfaces;        //這個配置包含的接口數量
        __u8  bConfigurationValue;    //對於擁有多個配置的幸運設備來說,可以拿這個值爲參數,使用SET_CONFIGURATION請求來改變正在被使用的 USB配置,bConfigurationValue就指明瞭將要激活哪個配置。
                           //咱們的設備雖然可以有多個配置,但同一時間卻也只能有一個配置被激活。捎帶着提一下,SET_CONFIGURATION請求也是標準的設備請求之一,專門用來設置設備的配置。
        __u8  iConfiguration;        //描述配置信息的字符串描述符的索引值
        __u8  bmAttributes;        //這個字段表徵了配置的一些特點,比如bit 6爲1表示self-powered,bit 5爲1表示這個配置支持遠程喚醒。另外,它的bit 7必須爲1
       __u8  bMaxPower;            //設備正常運轉時,從總線那裏分得的最大電流值,以2mA爲單位。設備可以使用這個字段向hub表明自己需要的的電流,但如果設備需求過於旺盛,請求的超出了hub所能給予的,hub就會直接拒絕
                           //還記得struct usb_device結構裏的bus_mA嗎?它就表示hub所能夠給予的。計算機的usb端口可以提供最多500mA的電流
} __attribute__ ((packed));

struct--配置描述符

struct usb_config_descriptor {
        __u8  bLength;
        __u8  bDescriptorType;        //這裏的值並不僅僅可以爲USB_DT_CONFIG,還可以爲USB_DT_OTHER_SPEED_CONFIG

       __le16 wTotalLength;        //使用GET_DESCRIPTOR請求從設備裏獲得配置描述符信息時,返回的數據長度
        __u8  bNumInterfaces;        //這個配置包含的接口數量
        __u8  bConfigurationValue;    //對於擁有多個配置的幸運設備來說,可以拿這個值爲參數,使用SET_CONFIGURATION請求來改變正在被使用的 USB配置,bConfigurationValue就指明瞭將要激活哪個配置。
                           //咱們的設備雖然可以有多個配置,但同一時間卻也只能有一個配置被激活。捎帶着提一下,SET_CONFIGURATION請求也是標準的設備請求之一,專門用來設置設備的配置。
        __u8  iConfiguration;        //描述配置信息的字符串描述符的索引值
        __u8  bmAttributes;        //這個字段表徵了配置的一些特點,比如bit 6爲1表示self-powered,bit 5爲1表示這個配置支持遠程喚醒。另外,它的bit 7必須爲1
        __u8  bMaxPower;            //設備正常運轉時,從總線那裏分得的最大電流值,以2mA爲單位。設備可以使用這個字段向hub表明自己需要的的電流,但如果設備需求過於旺盛,請求的超出了hub所能給予的,hub就會直接拒絕
                           //還記得struct usb_device結構裏的bus_mA嗎?它就表示hub所能夠給予的。計算機的usb端口可以提供最多500mA的電流
} __attribute__ ((packed));

struct--usb接口的緩存
struct usb_interface_cache {
         unsigned num_altsetting;        /* number of alternate settings */
         struct kref ref;                /* reference counter */

         /* variable-length array of alternate settings for this interface,
          * stored in no particular order */
        struct usb_host_interface altsetting[0];
 };

struct--hub描述符
struct usb_hub_descriptor {
    __u8  bDescLength;
    __u8  bDescriptorType;
    __u8  bNbrPorts;        //Number of downstream facing ports that this hub supports,就是說這個hub所支持的下行端口,這個值不能比31大
    __le16 wHubCharacteristics;    //它記錄了很多信息
    __u8  bPwrOn2PwrGood;
    __u8  bHubContrCurrent;        //bHubContrCurrent是Hub控制器的最大電流需求
        /* add 1 bit for hub status change; round to bytes */
    __u8  DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8];//用來判斷這個端口連接的設備是否是可以移除的,每一個bit代表一個端口,如果該bit爲0,則說明可以被移除,爲1,就說明不可以移除
    __u8  PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
} __attribute__ ((packed));

struct--hub結構體


struct usb_hub {
    struct device        *intfdev;    /* the "interface" device */
    struct usb_device    *hdev;
    struct kref        kref;
    struct urb        *urb;        /* for interrupt polling pipe */

    /* buffer for urb  with extra space in case of babble */
    char            (*buffer)[8];
    dma_addr_t        buffer_dma;    /* DMA address for buffer */
    union {
        struct usb_hub_status    hub;
        struct usb_port_status    port;
    }            *status;    /* buffer for status reports */
    struct mutex        status_mutex;    /* for the status buffer */

    int            error;        /* last reported error */
    int            nerrors;    /* track consecutive errors */

    struct list_head    event_list;    /* hubs w/data or errs ready */
    unsigned long        event_bits[1];    /* status change bitmask */
    unsigned long        change_bits[1];    /* ports with logical connect status change */
    unsigned long        busy_bits[1];    /* ports being reset or
                        //resumed */這個flag只有在reset和resume的函數內部纔會設置,所以可以通過測試busy_bits的相應位來檢測某個端口是否在執行reset或resume操作。
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif

    struct usb_hub_descriptor *descriptor;    /* class descriptor */
    struct usb_tt        tt;        /* Transaction Translator */

    unsigned        mA_per_port;     //提供給每一個port的電流,一般爲500mA,但可能會因爲hub提供不了這麼大的電流或者host controller那邊提供不了這麼大的電流,而導致不足500mA

    unsigned        limited_power:1; //對於host controller那邊限制了電流的情況下,這個值被設置爲1
    unsigned        quiescing:1;    //quiescing是停止的意思,在reset的時候我們會設置它爲1,在suspend的時候我們也會把它設置爲1,一旦把它設置成了1,那麼hub驅動程序就不會再提交任何URB
    unsigned        activating:1;    //如果activating爲1,那麼hub驅動程序就會給每個端口發送一個叫做Get Port Status的請求,通常情況下,hub驅動只有在一個端口發生了狀態變化的情況下才會去發送
                         //Get Port Status從而去獲得端口的狀態。正常情況下,quiescing和activating都應該爲0.
    unsigned        disconnected:1;

    unsigned        has_indicators:1;
    u8            indicator[USB_MAXCHILDREN];
    struct delayed_work    leds;
};

struct--usb_bus結構體
struct usb_bus {
    struct device *controller;    /* host/master side hardware */
    int busnum;            /* Bus number (in order of reg) */
    char *bus_name;            /* stable id (PCI slot_name etc) */
    u8 uses_dma;            /* Does the host controller use DMA? */
    u8 otg_port;            /* 0, or number of OTG/HNP port */
    unsigned is_b_host:1;        /* true during some HNP roleswitches */
    unsigned b_hnp_enable:1;    /* OTG: did A-Host enable HNP? */

    int devnum_next;        /* Next open device number in //          在總線初始化的時候,其devnum_next被設置爲1
                     * round-robin allocation */

    struct usb_devmap devmap;    /* device address allocation map */ 每條總線設有一個地址映射表
    struct usb_device *root_hub;    /* Root hub */
    struct list_head bus_list;    /* list of busses */

    int bandwidth_allocated;    /* on this bus: how much of the time
                     * reserved for periodic (intr/iso)
                     * requests is used, on average?
                     * Units: microseconds/frame.
                     * Limits: Full/low speed reserve 90%,
                     * while high speed reserves 80%.
                     */
    int bandwidth_int_reqs;        /* number of Interrupt requests */
    int bandwidth_isoc_reqs;    /* number of Isoc. requests */

 

#ifdef CONFIG_USB_DEVICEFS
    struct dentry *usbfs_dentry;    /* usbfs dentry entry for the bus */
#endif
    struct device *dev;        /* device for this bus */

#if defined(CONFIG_USB_MON)
    struct mon_bus *mon_bus;    /* non-null when associated */
    int monitored;            /* non-zero when monitored */
#endif
};

struct--usb_devmap

struct usb_devmap {
    unsigned long devicemap[128 / (8*sizeof(unsigned long))];
};

幾個重要的函數分析usb_hub_init所做的工作:
代碼原型:
khubd_task = kthread_run(hub_thread, NULL, "khubd");
建立一個內核級線程:hub_threadhub_thread所做的工作:
代碼原型:


do {
        hub_events();
        wait_event_freezable(khubd_wait,
                !list_empty(&hub_event_list) ||
                kthread_should_stop());    //一般來說,只要hub_event_list不爲空,就會一直調用hub_events()函數,hub_event_list將所有的usb_hub的list_head類型的event_list對象連在一起
        } while (!kthread_should_stop() || !list_empty(&hub_event_list));

調用規則:
hub_events函數所做的工作:
對每個端口號(共計bNbrPorts個端口,bNbrPorts這個值從hub描述符裏邊得到,因爲此值描述了hub所用用的端口的情況),假如滿足下列條件則調用hub_port_connect_change:
1.連接有變化
2.端口本身重新使能,即所謂的enable,這種情況通常就是爲了對付電磁干擾的,正如我們前面的判斷中所說的那樣
3.在復位一個設備的時候發現其描述符變了,這通常對應的是硬件本身有了升級.很顯然,第一種情況是真正的物理變化,後兩者就算是邏輯變化
代碼模型如下:


for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
      
      if (connect_change) //表示滿足如上條件之一
              hub_port_connect_change(hub, i,
                      portstatus, portchange);
          
  }

個人感覺,此處的i沒有什麼實際意義,只是爲了保證有“幾個端口,才能接幾個設備”這樣的一個邏輯。雖然說usb_bus最多可以接128個設備,hub最多可以接31個設備,但是,假如hub沒有那麼多的端口,你怎麼接設備?hub_port_connect_change的調用規則:
代碼模型:


for (i = 0; i < SET_CONFIG_TRIES; i++)            //在此驅動裏邊,SET_CONFIG_TRIES=4,新策略嘗試兩次,舊策略嘗試兩次
   {    
       
       udev = usb_alloc_dev(hdev, hdev->bus, port1);
       udev->speed = USB_SPEED_UNKNOWN;
       //進行一些必要的設置
       choose_address(udev);                        //設置了udev->devnum
       //錯誤判斷
       status = hub_port_init(hub, udev, port1, i);        //先進行兩次新的策略(i=0和=1時),如果不行就再進行兩次舊的策略(i=2和i=3時).所有這一切只有一個目的,就是爲了獲得設備的描述符
                                           //設置了udev->tt、udev->ttport和udev->ep0.desc.wMaxPacketSize,設置udev->status= USB_STATE_ADDRESS
           
   }

hub_port_init功能:
    hub_port_init裏邊爲udev->tt和udev->ttport和udev->ep0.desc.wMaxPacketSize賦了值,設置udev->status= USB_STATE_ADDRESS.
    它的長篇大論只是爲了討論如何確定賦給udev->ep0.desc.wMaxPacketSize的值,這個值是由設備描述符的bMaxPacketSize0字段確定的.每調用一次hub_port_init,使用一種策略。
hub_port_init的原型:

static int  hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
    {
    
    retval = hub_port_reset(hub, port1, udev, delay);            //將hub hub上的udev設備重啓,並設置udev->speed的值
    
    //根據udev->speed的值設置udev->ep0.desc.wMaxPacketSize的值,但此處的值僅僅是個猜測,最終端點0的maxinum packet size的值要根據設備描述符的bMaxPacketSize0字段得到
    //設置udev->tt和udev->ttport的值
    for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100)))        //GET_DESCRIPTOR_TRIES=2,是爲了多執行幾次保證正確
    {
        if (USE_NEW_SCHEME(retry_counter))                     //USE_NEW_SCHEME判斷使用什麼策略,USB_NEW_SCHEME(i)將在i爲0和1的時候爲1,在i爲2和3的時候爲0
        {                                        //當retry_counter=0,1時,USE_NEW_SCHEME(retry_counter)=1,表示使用新策略;
                                                //當retry_counter=2,3時,USE_NEW_SCHEME(retry_counter)=0,表示使用舊策略
#define GET_DESCRIPTOR_BUFSIZE    64                            
            buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);    //首先定義一個struct usb_device_descriptor的指針buf,然後申請64個字節的空間
            for (j = 0; j < 3; ++j) {
                buf->bMaxPacketSize0 = 0;
                r = usb_control_msg(udev, usb_rcvaddr0pipe(),    //發送一個64字節的控制傳輸的請求
                    USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                    USB_DT_DEVICE << 8, 0,
                    buf, GET_DESCRIPTOR_BUFSIZE,
                    USB_CTRL_GET_TIMEOUT);
                switch (buf->bMaxPacketSize0) {
                case 8: case 16: case 32: case 64: case 255:
                    if (buf->bDescriptorType ==
                            USB_DT_DEVICE) {
                        r = 0;
                        break;
                    }
                    /* FALL THROUGH */
                default:
                    if (r == 0)
                        r = -EPROTO;
                    break;
                }
                if (r == 0)
                    break;
            }
            udev->descriptor.bMaxPacketSize0 = buf->bMaxPacketSize0;//得到udev->descriptor.bMaxPacketSize0
            kfree(buf);
            retval = hub_port_reset(hub, port1, udev, delay);    //將hub hub上的udev設備重啓,並設置udev->speed的值
#undef GET_DESCRIPTOR_BUFSIZE
        }
        for (j = 0; j < SET_ADDRESS_TRIES; ++j) {                //SET_ADDRESS_TRIES=2,多試幾次
            retval = hub_set_address(udev, devnum);            //告知設備它的總線地址,設置udev->status= USB_STATE_ADDRESS
            if (retval >= 0)                            //表示正確,退出循環
                break;
            msleep(200);
        }
        if (retval < 0) {
            dev_err(&udev->dev,
                "device not accepting address %d, error %d\n",    //我的驅動打印出了這個信息,表示hub_set_address函數出錯了
                devnum, retval);
            goto fail;
        }
        if (USE_NEW_SCHEME(retry_counter))                //假如這是新策略,執行到這裏,表示得到了正確的設備描述符,退出大循環
            break;
        retval = usb_get_device_descriptor(udev, 8);            //執行到這裏,說明使用的是舊策略,先一次讀取8個字節
                                                //假如返回出錯,那麼go to fail吧,兩種策略都失敗了
            
    }                                            //到此爲止,應該成功讀取到了設備描述符,並可以從中得到bMaxPacketSize0的值
    //根據bMaxPacketSize0的值重新設置udev->ep0.desc.wMaxPacketSize的值
    retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);     //再一次讀取設備描述符來測試可不可以正確讀取,注意這次讀取的長度爲USB_DT_DEVICE_SIZE
                                                //由於知道了endpoint 0的max packet size,所以可以進行正常的控制傳輸了
    //假如retval == sizeof(udev->descriptor),那麼說明控制傳輸ok了,也驗證了端點0的maxinum packet size的值是正確的,那麼happy的返回把
    }

hub_port_reset功能:

hub_port_reset的原型:
static int hub_port_reset(struct usb_hub *hub, int port1,
                struct usb_device *udev, unsigned int delay)
{
    int i, status;

    /* Block EHCI CF initialization during the port reset.
     * Some companion controllers don't like it when they mix.
     */
    down_read(&ehci_cf_port_reset_rwsem);

    /* Reset the port */
    for (i = 0; i < PORT_RESET_TRIES; i++) {                         //PORT_RESET_TRIES=5
        status = set_port_feature(hub->hdev,                    //發送一個hub類型的控制請求,設置一個feature,傳遞進來的feature是USB_PORT_FEAT_RESET,即對應於usb spec中的reset
                port1, USB_PORT_FEAT_RESET);
        if (status)
            dev_err(hub->intfdev,
                    "cannot reset port %d (err = %d)\n",
                    port1, status);
        else {
            status = hub_port_wait_reset(hub, port1, udev, delay);    //取得端口的狀態,然後判斷reset是否成功,如果成功,則根據端口的狀態字設置udev->speed的值,否則,出錯
            if (status && status != -ENOTCONN)
                dev_dbg(hub->intfdev,
                        "port_wait_reset: err = %d\n",
                        status);
        }
                                                    //在我的例子理,hub_port_wait_reset返回了-110
        /* return on disconnect or reset */
        switch (status) {
        case 0:
            /* TRSTRCY = 10 ms; plus some extra */
            msleep(10 + 40);
              udev->devnum = 0;    /* Device now at address 0 */
            /* FALL THROUGH */
        case -ENOTCONN:       &n,bsp;                            //ENOTCONN=107,transport endpoint is not connected
        case -ENODEV:                                    //ENODEV=19,no such device
            clear_port_feature(hub->hdev,                        //status的值爲0,-107,-19的時候纔會執行clear_port_feature和usb_set_device
                port1, USB_PORT_FEAT_C_RESET);
            /* FIXME need disconnect() for NOTATTACHED device */    
            usb_set_device_state(udev, status                    //status爲0的情況,屬於正常情況,從這時候開始,struct usb_device結構體的狀態就將記錄爲USB_STATE_DEFAULT
                    ? USB_STATE_NOTATTACHED
                    : USB_STATE_DEFAULT);                    
            goto done;                                    
        }

        dev_dbg (hub->intfdev,                                //status的值不爲0,-107,-19的時候纔會執行到這裏,比如,我的驅動裏爲-110
            "port %d not enabled, trying reset again\n",
            port1);
        delay = HUB_LONG_RESET_TIME;                            
    }

    dev_err (hub->intfdev,                                    //假如執行到這裏,說明幾次嘗試重啓都失敗了
        "Cannot enable port %i.  Maybe the USB cable is bad?\n",
        port1);

 done:
    up_read(&ehci_cf_port_reset_rwsem);
    return status;
}


hub_port_wait_reset功能:
取得端口的狀態,然後判斷reset是否成功,如果成功,則根據端口的狀態字設置udev->speed的值,否則,出錯
hub_port_wait_reset函數原型:
static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                struct usb_device *udev, unsigned int delay)
{
        //超時重試控制
        ret = hub_port_status(hub, port1, &portstatus, &portchange);    //得到端口的狀態
        ..
        if (!(portstatus & USB_PORT_STAT_CONNECTION))                //如果在reset期間設備都被撤掉了,那就返回吧
            return -ENOTCONN;
        if ((portchange & USB_PORT_STAT_C_CONNECTION))                //如果又一次彙報說有設備插入,那就是見鬼了.返回錯誤
            return -ENOTCONN;

        if (!(portstatus & USB_PORT_STAT_RESET) &&
            (portstatus & USB_PORT_STAT_ENABLE)) {                //reset真正完成以後,status就應該是enabled,所以if如果滿足就說明reset好了
            if (hub_is_wusb(hub))
                udev->speed = USB_SPEED_VARIABLE;
            else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
                udev->speed = USB_SPEED_HIGH;
            else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                udev->speed = USB_SPEED_LOW;
            else
                udev->speed = USB_SPEED_FULL;
            return 0;                                    //reset好了以後,設置udev->speed字段,然後返回
                                                    //問題:怎麼能從端口的狀態裏邊得到設備的speed字段呢?有點疑惑
        }
        //超時重試控制
}
hub_port_status其實就是調用了get_port_status(hub->hdev, port1, &hub->status->port);
get_port_status的實現如下:
static int get_port_status(struct usb_device *hdev, int port1,
        struct usb_port_status *data)
{
    int i, status = -ETIMEDOUT;         //ETIMEDOUT=110

    for (i = 0; i < USB_STS_RETRIES && status == -ETIMEDOUT; i++) {
        status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
            USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port1,
            data, sizeof(*data), USB_STS_TIMEOUT);
    }
    return status;
}


在我的驅動裏邊,這個函數出錯,返回值是-110,

關於usb驅動的若干基本知識:

1.什麼是tt電路?
知道tt幹嘛的嗎?tt叫做transaction translator.你可以把它想成一塊特殊的電路,是hub裏面的電路,確切的說是高速hub中的電路,我們知道usb設備有三種速度的,分別是low speed,full speed,high speed.即所謂的低速/全速/高速,抗日戰爭那會兒,這個世界上只有low speed/full speed的設備,沒有high speed的設備,後來解放後,國民生產力的大幅度提升催生了一種high speed的設備,包括主機控制器,以前只有兩種接口的,OHCI/UHCI,這都是在usb spec 1.0的時候,後來2.0推出了EHCI,高速設備應運而生.Hub也有高速hub和過去的hub,但是這裏就有一個兼容性問題了,高速的hub是否能夠支持低速/全速的設備呢?一般來說是不支持的,於是有了一個叫做TT的電路,它就負責高速和低速/全速的數據轉換,於是,如果一個高速設備裏有這麼一個TT,那麼就可以連接低速/全速設備,如不然,那低速/全速設備沒法用,只能連接到OHCI/UHCI那邊出來的hub口裏。tt有兩種,一種是single tt,一種是multi tt.前者表示整個hub就是一個TT,而multi tt表示每個端口都配了一個TT.大多數hub是single TT,因爲一個hub一個TT就夠了,國家資源那麼緊張,何必鋪張浪費.使用single TT就是支持國家反腐倡廉!
2.
我們知道usb-storage裏面最常見的傳輸方式就是control/bulk傳輸,而對於hub,它的傳輸方式就是control/interrupt,而最有特色的正是它的中斷傳輸.
3.
USB_PORT_STAT_CONNECTION的意思的確是表徵是否有設備連接在這個端口上,我們不妨假設有,那麼portstatus和它相與的結果就是1,在usb spec裏面,這一位叫做Current Connect Status位,於是這裏我們會看到connect_change被設置成了1.而接下來,USB_PORT_STAT_C_CONNECTION則是表徵這個端口的Current Connect Status位是否有變化,如果有變化,那麼portchange和USB_PORT_STAT_C_CONNECTION相與的結果就是1,對於這種情況,我們需要發送另一個請求以清除這個flag,並且將connect_change也設置爲1.這個請求叫做Clear Port Feature.這個請求也是Hub的標準請求。
usb的接口驅動的數據結構爲:struct usb_drive,它和usb接口對等;
usb設備驅動的數據結構爲:struct usb_device_driver,它和整個usb設備對等


4.配置和設置的區別:
配置還有設置有什麼區別,起碼我平時即使是再無聊也不會去想這個,但老外不一樣,他們不知道老子也不知道鄭板橋,所以說他們挺較真兒這個,還分了兩個詞,配置是configuration,設置是setting。先說配置,一個手機可以有多種配置,比如可以攝像,可以接在電腦裏當做一個U盤,那麼這兩種情況就屬於不同的配置,在手機裏面有相應的選擇菜單,你選擇了哪種它就按哪種配置進行工作,供你選擇的這個就叫做配置。很顯然,當你攝像的時候你不可以訪問這塊U盤,當你訪問這塊U盤的時候你不可以攝像,因爲你做了選擇。第二,既然一個配置代表一種不同的功能,那麼很顯然,不同的配置可能需要的接口就不一樣,我假設你的手機裏從硬件上來說一共有5個接口,那麼可能當你配置成U盤的時候它只需要用到某一個接口,當你配置成攝像的時候,它可能只需要用到另外兩個接口,可能你還有別的配置,然後你可能就會用到剩下那兩個接口,也就是說一個配置可能包括到若干個接口
再說說設置,一個手機可能各種配置都確定了,是振動還是鈴聲已經確定了,各種功能都確定了,但是聲音的大小還可以變吧,通常手機的音量是一格一格的變動,大概也就5、6格,那麼這個可以算一個setting吧。


5.端點、接口、設置、配置
•  設備通常有一個或多個配置.
•  配置常常有一個或多個接口
•  接口常常有一個或多個設置.
•  接口有零或多個端點.


6.一般來說,主設備號表明了設備的種類,也表明了設備對應着哪個驅動程序,而次設備號則是因爲一個驅動程序要支持多個設備而爲了讓驅動程序區分它們而設置的。也就是說,主設備號用來幫你找到對應的驅動程序,次設備號給你的驅動用來決定對哪個設備進行操作


7.
每個端點都有一個叫做maximum packet size的filed,即告訴你該端點能夠發送或者接收的包的最大值.對於通常的端點來說,這個值被保存在該端點描述符中的wMaxPacketSize這一個field,而對於端點0就不一樣了,由於它自己沒有一個描述符,而每個設備又都有這麼一個端點,所以這個信息被保存在了設備描述符裏,所以我們在設備描述符裏可以看到這麼一項,bMaxPacketSize0,而且spec還規定了,這個值只能是8,16,32或者64這四者之一,而且,如果一個設備工作在高速模式,這個值還只能是64,取別的值都不行


8.
在usb_hcd_submit_urt()中,Root Hub, rh_urb_enqueue會被執行,對於非Root Hub,即一般的Hub,driver->urb_enqueue會被執行,對於uhci來說,就是uhci_urb_enqueue會被執行.
對於一般的hub,會執行usb/host/ixp4xx_ehci_hcd.c中的ehci_urb_enqueue(),而ehci_urb_enqueue()則會調用submit_async()(對於設備請求,調用到了這裏)
對於一般的hub
ehci_watchdog->    ehci_work()出現ehci_work(),就開始返回了
給出一個請求,會執行如下過程:rh_timer_func->    usb_hcd_poll_rh_status->    usb_hcd_giveback_urb->    urb->complete


9.對於root hub的情況,爲什麼不用對傳輸緩存區進行DMA映射呢?
在後面的處理中我們可以看到,其實對於root hub ,它不需要進行實際的物理傳輸,linux按照spec上的規定,將它靜態放置在內存中,在進行相關操作的時候,只要直接copy過去就可以了.
usb_hcd_giveback_urb函數裏邊會調用urb->complete(urb);


10.urb發送處理過程
(1).假如一個usb_control_msg函數被調用
(2).usb_control_msg會調用usb_internal_control_msg函數
(3).usb_internal_control_msg會調用usb_alloc_urb分配一個urb結構,然後調用usb_fill_control_urb函數來填充這個urb結構,然後調用usb_start_wait_urb函數
(4).usb_start_wait_urb會調用usb_submit_urb(urb,GFP_NOIP)將這個urb發送給usb core,完成過程如下:
(4.1).usb_submit_urb對urb進行一些設置,然後交給usb主控制器驅動來出作進一步的處理,調用的函數爲usb_hcd_submit_urb
(4.2).在usb_hcd_submit_urb函數中,判斷urb要發送到的設備,假如此設備爲 root hub,那麼調用rh_urb_enqueue(hcd, urb),否則,調用hcd->driver->urb_enqueue(hcd, urb, mem_flags);
在我的主控制器驅動裏註冊的urb_enqueue函數爲ehci_urb_enqueue

if (is_root_hub(urb->dev))
      status = rh_urb_enqueue(hcd, urb);
  else
      //如果是一般的設備
      status = hcd->driver->urb_enqueue(hcd, urb, mem_flags);

因爲我分析的是一個普通的usb設備,所以調用ehci_urb_enqueue
(4.3).ehci_urb_enqueue將來自usbcore層的urb的傳輸請求轉換成ehci 可識別的傳輸描述結構(iTD,siTD,qTD等),然後安排到echi的periodic schedule list或者asynchronous schedule list的合適位置。
對於urb 所要發送到endpoint的類型的control的情況下,此函數將依次調用兩個函數:qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)和submit_async (ehci, ep, urb, &qtd_list, mem_flags)
(4.4).調用qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)從urb生成一系列qTD結構,並將這些結構連接到qtd_list;
(4.5).調用submit_async (ehci, ep, urb, &qtd_list, mem_flags)將qtd_list鏈接的qTD結構分配到ep對應的QH, 將該QH安排到ehci asynchronous schedule list中;
(5)usb_start_wait_urb調用usb_submit_urb將urb最終發送給主控制器驅動之後,它開始滿懷信息的等待usb core給它的結果。而它呢,在usb core給它結果以前,它沒什麼事情可幹了把。於是,它調用wait_for_completion_timeout進行非中斷的等待(休眠了自己)。


11.urb的完成
(1).在usb_create_hcd函數內有一句:

hcd->rh_timer.function = rh_timer_func;

(2).rh_timer_func函數實現如下:
static void rh_timer_func (unsigned long _hcd)
{
    usb_hcd_poll_rh_status((struct usb_hcd *) _hcd);
}

(3).usb_hcd_poll_rh_status函數實現如下:
root hub的中斷傳輸使用polling方式(使用一個定時器),當驅動請求它的時候。也就是說,當有事件發生的時候,驅動負責調用usb_hcd_poll_rh_status
void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
{
     struct urb *urb;
     int length;
     unsigned long flags;
     char buffer[4]; /* Any root hubs with > 31 ports? */
     //檢測主機控制器驅動是否已經註冊
     if (unlikely(!hcd->rh_registered))
          return;
     if (!hcd->uses_new_polling && !hcd->status_urb)
          return;
     //負責檢測端口和td隊列的狀態
     length = hcd->driver->hub_status_data(hcd, buffer);
     //端口有設備
     if (length > 0)
     {
          /* try to complete the status urb */
          spin_lock_irqsave(&hcd_root_hub_lock, flags);
          urb = hcd->status_urb;
          //檢測urb是否存在
          if (urb)
          {
               hcd->poll_pending = 0;
               //清除hcd的狀態urb
               hcd->status_urb = NULL;
               //置實際傳輸長度爲1
               urb->actual_length = length;
               //拷貝端口狀態描述組到urb中
               memcpy(urb->transfer_buffer, buffer, length);
               //卸載urb與節點的連接
               usb_hcd_unlink_urb_from_ep(hcd, urb);
               spin_unlock(&hcd_root_hub_lock);
              //返回urb給驅動程序
               usb_hcd_giveback_urb(hcd, urb, 0);
               spin_lock(&hcd_root_hub_lock);
          }
          else
          {
               length = 0;
               hcd->poll_pending = 1;
          }
      spin_unlock_irqrestore(&hcd_root_hub_lock, flags);
     }
     /* The USB 2.0 spec says 256 ms. This is close enough and won't
      * exceed that limit if HZ is 100. The math is more clunky than
      * maybe expected, this is to make sure that all timers for USB devices
      * fire at the same time to give the CPU a break inbetween */
     //每間隔HZ/4就執行一次hcd->rh_timer中指定的函數
     if (hcd->uses_new_polling ? hcd->poll_rh :(length == 0 && hcd->status_urb != NULL))
          mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4));
}

usb_hcd_poll_rh_status首先調用hcd->driver->hub_status_data(hcd, buffer);在我的ehci中爲ehci_hub_status_data
ehci_hub_status_data負責檢測端口和td隊列的狀態。假如ehci_hub_status_data返回值大於0,表明存在已經完成的urb,判斷hcd->status_urb是否爲空,
假如非空,那麼設置hcd結構,然後調用usb_hcd_giveback_urb.
(4).usb_hcd_giveback_urb設置urb->status,然後調用urb->complete,這將導致urb的完成處理函數被執行。
(5).在urb的完成處理函數中會調用complete(&ctx->done)或類似的函數。
(6).complete(&ctx->done)將會喚醒usb_start_wait_urb函數.從usb_start_wait_urb函數的wait_for_completion_timeout處繼續執行。
如果超時,也會返回usb_start_wait_urb,然後進行錯誤處理;如果一切正常,那麼恭喜,此次通信圓滿成功。


12.
ehci_irq
ehci_work
scan_saync
qh_completions
在qh_competions函數裏,urb->status的值在此函數裏發生了變化,
ehci_urb_done
usb_hcd_giveback_urb


13.
系統中斷入口:
asm_do_IRQ
handle_level_irq
handle_IRQ_event
usb控制器中斷
usb_hcd_irq
ehci_irq
ehci_wrok
scan_async
qh_completions
qtd_copy_status
consistent_asny

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章