本文是基於mini2440開發板Linux版本號是linux-2.6.32.2的學習筆記
前面root hub 接口device和接口driver都是用的總線usb_bus_type。但是這個時候device的類型是usb_if_device_type,不再是usb_device_type,也就是usb接口設備,而不是usb設備。
那麼註冊設備的時候,會自動遍歷總線上的所有驅動,看看哪個驅動能匹配上;
註冊驅動的時候,也會遍歷總線上的所有設備,看看哪個設備能匹配上;
usb_bus_type的定義如下:
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
};
usb_bus_type的匹配函數是usb_device_match,usb_device_match函數的定義如下:
static int usb_device_match(struct device *dev, struct device_driver *drv)
{
/* devices and interfaces are handled separately */
//如果是USB設備
if (is_usb_device(dev)) {
/* interface drivers never match devices */
//是匹配USB設備的驅動,USB接口的驅動不能匹配
if (!is_usb_device_driver(drv))
return 0;
/* TODO: Add real matching code */
return 1;
} else if (is_usb_interface(dev)) { //如果是USB接口
struct usb_interface *intf;
struct usb_driver *usb_drv;
const struct usb_device_id *id;
/* device drivers never match interfaces */
//usb接口在註冊driver時將for_devices設置爲0,for_devices =1,表示設備驅動,for_devices = 0,表示接口驅動
if (is_usb_device_driver(drv))
return 0;
intf = to_usb_interface(dev);
usb_drv = to_usb_driver(drv);
//匹配id table
id = usb_match_id(intf, usb_drv->id_table);
if (id)
return 1;
//匹配動態id table
id = usb_match_dynamic_id(intf, usb_drv);
if (id)
return 1;
}
return 0;
}
前面說過,它的匹配規則是:
usb 設備device和usb 設備driver任意匹配;
usb device和usb 接口driver不能匹配;
usb 接口device和usb設備driver不能匹配;
usb接口device和usb接口driver根據driver的id_table進行匹配;
這裏是root hub 接口device和接口driver的匹配,所以要根據dev獲得的usb_interface接口和driver的id_table來進行匹配。調用函數usb_match_id
const struct usb_device_id *usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
{
/* proc_connectinfo in devio.c may call us with id == NULL. */
if (id == NULL)
return NULL;
for (; id->idVendor || id->idProduct || id->bDeviceClass ||
id->bInterfaceClass || id->driver_info; id++) {
if (usb_match_one_id(interface, id))
return id;
}
return NULL;
}
可以根據廠家ID,DeviceClass,InterfaceClass,driver_info等規則匹配。過程就是把設備描述符的相關項或者接口描述符的相關項和driver的id_table的相關項進行比對。
那麼接口driver的id_table是多少呢?在hub_driver這個全局變量中有定義,如下所示:
static struct usb_driver hub_driver = {
.name = "hub",
.probe = hub_probe,
.disconnect = hub_disconnect,
.suspend = hub_suspend,
.resume = hub_resume,
.reset_resume = hub_reset_resume,
.pre_reset = hub_pre_reset,
.post_reset = hub_post_reset,
.ioctl = hub_ioctl,
.id_table = hub_id_table,
.supports_autosuspend = 1,
};
static struct usb_device_id hub_id_table [] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
{ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS,
.bInterfaceClass = USB_CLASS_HUB},
{ } /* Terminating entry */
};
因爲我們這裏有定義USB_DEVICE_ID_MATCH_DEV_CLASS和USB_DEVICE_ID_MATCH_INT_CLASS,所以我們要匹配它的bDeviceClass和bInterfaceClass。
USB_CLASS_HUB = 9。
那麼root hub 接口device的bDeviceClass和bInterfaceClass是多少呢?
static const u8 usb11_rh_dev_descriptor [18] = {
0x12, /* __u8 bLength; */
0x01, /* __u8 bDescriptorType; Device */
0x10, 0x01, /* __le16 bcdUSB; v1.1 */
0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
0x00, /* __u8 bDeviceSubClass; */
0x00, /* __u8 bDeviceProtocol; [ low/full speeds only ] */
0x40, /* __u8 bMaxPacketSize0; 64 Bytes */
0x6b, 0x1d, /* __le16 idVendor; Linux Foundation */
0x01, 0x00, /* __le16 idProduct; device 0x0001 */
KERNEL_VER, KERNEL_REL, /* __le16 bcdDevice */
0x03, /* __u8 iManufacturer; */
0x02, /* __u8 iProduct; */
0x01, /* __u8 iSerialNumber; */
0x01 /* __u8 bNumConfigurations; */
};
static const u8 ss_rh_config_descriptor[] = {
/* one configuration */
0x09, /* __u8 bLength; */
0x02, /* __u8 bDescriptorType; Configuration */
0x19, 0x00, /* __le16 wTotalLength; FIXME */
0x01, /* __u8 bNumInterfaces; (1) */
0x01, /* __u8 bConfigurationValue; */
0x00, /* __u8 iConfiguration; */
0xc0, /* __u8 bmAttributes;
Bit 7: must be set,
6: Self-powered,
5: Remote wakeup,
4..0: resvd */
0x00, /* __u8 MaxPower; */
/* one interface */
0x09, /* __u8 if_bLength; */
0x04, /* __u8 if_bDescriptorType; Interface */
0x00, /* __u8 if_bInterfaceNumber; */
0x00, /* __u8 if_bAlternateSetting; */
0x01, /* __u8 if_bNumEndpoints; */
0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
0x00, /* __u8 if_bInterfaceSubClass; */
0x00, /* __u8 if_bInterfaceProtocol; */
0x00, /* __u8 if_iInterface; */
/* one endpoint (status change endpoint) */
0x07, /* __u8 ep_bLength; */
0x05, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
* see hub.c:hub_configure() for details. */
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
/*
* All 3.0 hubs should have an endpoint companion descriptor,
* but we're ignoring that for now. FIXME?
*/
};
可以看到,DeviceClass和InterfaceClass都是9,所以是可以匹配上的。
root hub接口device和接口driver的匹配上後,最終調用的probe函數是hub_probe函數。
現在看一下hub_probe函數。
static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_host_interface *desc;
struct usb_endpoint_descriptor *endpoint;
struct usb_device *hdev;
struct usb_hub *hub;
//獲取當前的設置
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev,
"Unsupported bus topology: hub nested too deep\n");
return -E2BIG;
}
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
return -ENODEV;
}
#endif
/* Some hubs have a subclass of 1, which AFAICT according to the */
/* specs is not defined, but it works */
//hub的子類一般就是0,但是有的廠商設置的是1
if ((desc->desc.bInterfaceSubClass != 0) &&
(desc->desc.bInterfaceSubClass != 1)) {
descriptor_error:
dev_err (&intf->dev, "bad descriptor, ignoring hub\n");
return -EIO;
}
/* Multiple endpoints? What kind of mutant ninja-hub is this? */
//hub就只有一個endpoint,中斷endpoint,因爲hub的傳輸是中斷傳輸,不包含那個大家都有的控制端點
if (desc->desc.bNumEndpoints != 1)
goto descriptor_error;
endpoint = &desc->endpoint[0].desc;
/* If it's not an interrupt in endpoint, we'd better punt! */
//判斷這個端點是不是中斷端點,端點0必須是中斷控制方式的端點
if (!usb_endpoint_is_int_in(endpoint))
goto descriptor_error;
/* We found a hub */
dev_info (&intf->dev, "USB hub found\n");
hub = kzalloc(sizeof(*hub), GFP_KERNEL);
if (!hub) {
dev_dbg (&intf->dev, "couldn't kmalloc hub struct\n");
return -ENOMEM;
}
kref_init(&hub->kref);
INIT_LIST_HEAD(&hub->event_list);
hub->intfdev = &intf->dev;
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
usb_get_intf(intf);
//讓intf和hub關聯起
usb_set_intfdata (intf, hub);
intf->needs_remote_wakeup = 1;
//hub是高速設備
if (hdev->speed == USB_SPEED_HIGH)
highspeed_hubs++;
//配置hub
if (hub_configure(hub, endpoint) >= 0)
return 0;
//如果hub配置失敗,移除hub
hub_disconnect (intf);
return -ENODEV;
}
主要是一些參數檢查,然後最重要的就是調用了hub_configure函數。
static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint)
{
struct usb_hcd *hcd;
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
u16 hubstatus, hubchange;
u16 wHubCharacteristics;
unsigned int pipe;
int maxp, ret;
char *message = "out of memory";
//申請一個buffer存放urb傳輸數據
hub->buffer = usb_buffer_alloc(hdev, sizeof(*hub->buffer), GFP_KERNEL,
&hub->buffer_dma);
if (!hub->buffer) {
ret = -ENOMEM;
goto fail;
}
hub->status = kmalloc(sizeof(*hub->status), GFP_KERNEL);
if (!hub->status) {
ret = -ENOMEM;
goto fail;
}
mutex_init(&hub->status_mutex);
//申請hub 描述符的內存,大小爲15個字節
hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL);
if (!hub->descriptor) {
ret = -ENOMEM;
goto fail;
}
/* Request the entire hub descriptor.
* hub->descriptor can handle USB_MAXCHILDREN ports,
* but the hub can/will return fewer bytes here.
*/
//獲取hub描述符,描述符的長度至少爲9個字節
ret = get_hub_descriptor(hdev, hub->descriptor,
sizeof(*hub->descriptor));
if (ret < 0) {
message = "can't read hub descriptor";
goto fail;
} else if (hub->descriptor->bNbrPorts > USB_MAXCHILDREN) {
message = "hub has too many ports!";
ret = -ENODEV;
goto fail;
}
//保存hub所支持的端口數量
hdev->maxchild = hub->descriptor->bNbrPorts;
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
hub->port_owners = kzalloc(hdev->maxchild * sizeof(void *), GFP_KERNEL);
if (!hub->port_owners) {
ret = -ENOMEM;
goto fail;
}
//用一個臨時變量wHubCharacteristics來代替描述符裏的那個wHubCharacteristics
wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
//判斷是不是複合設備
if (wHubCharacteristics & HUB_CHAR_COMPOUND) {
int i;
char portstr [USB_MAXCHILDREN + 1];
//如果是複合設備,就用一個數組portstr[]來記錄每一個端口的設備是否可以被移除
for (i = 0; i < hdev->maxchild; i++)
portstr[i] = hub->descriptor->DeviceRemovable
[((i + 1) / 8)] & (1 << ((i + 1) % 8))
? 'F' : 'R';
portstr[hdev->maxchild] = 0;
dev_dbg(hub_dev, "compound device; port removable status: %s\n", portstr);
} else
dev_dbg(hub_dev, "standalone hub\n");
//表徵電源切換的方式
//ganged power switching指的是所有port一次性上電
//individual port power switching port單獨上電
switch (wHubCharacteristics & HUB_CHAR_LPSM) {
case 0x00:
dev_dbg(hub_dev, "ganged power switching\n");
break;
case 0x01:
dev_dbg(hub_dev, "individual port power switching\n");
break;
case 0x02:
case 0x03:
dev_dbg(hub_dev, "no power switching (usb 1.0)\n");
break;
}
//表徵過流保護模式
switch (wHubCharacteristics & HUB_CHAR_OCPM) {
case 0x00:
dev_dbg(hub_dev, "global over-current protection\n");
break;
case 0x08:
dev_dbg(hub_dev, "individual port over-current protection\n");
break;
case 0x10:
case 0x18:
dev_dbg(hub_dev, "no over-current protection\n");
break;
}
//tt負責高速和低速/全速的數據轉換
spin_lock_init (&hub->tt.lock);
INIT_LIST_HEAD (&hub->tt.clear_list);
INIT_WORK(&hub->tt.clear_work, hub_tt_work);
switch (hdev->descriptor.bDeviceProtocol) {
//ull/low speed的hub的bDeviceProtocol是0,這種hub就沒有tt
case 0:
break;
//Single TT:整個hub就是一個TT
//對於high speed的hub,其bDeviceProtocol爲1表示是single tt的
case 1:
dev_dbg(hub_dev, "Single TT\n");
hub->tt.hub = hdev;
break;
//TT per port:每個端口都配了一個TT
case 2:
ret = usb_set_interface(hdev, 0, 1);
if (ret == 0) {
dev_dbg(hub_dev, "TT per port\n");
hub->tt.multi = 1;
} else
dev_err(hub_dev, "Using single TT (err %d)\n",
ret);
hub->tt.hub = hdev;
break;
case 3:
/* USB 3.0 hubs don't have a TT */
break;
default:
dev_dbg(hub_dev, "Unrecognized hub protocol %d\n",
hdev->descriptor.bDeviceProtocol);
break;
}
//TT在處理兩個低速/全速的交易之間需要一點點時間來緩衝,這個最大的間隔就叫做TT think time
/* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
switch (wHubCharacteristics & HUB_CHAR_TTTT) {
//Full Speed,其速度是12Mbps,8 FS bit time就是8bits / 1200 0000 bits per second,即約等於666ns
case HUB_TTTT_8_BITS:
if (hdev->descriptor.bDeviceProtocol != 0) {
hub->tt.think_time = 666;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
8, hub->tt.think_time);
}
break;
case HUB_TTTT_16_BITS:
hub->tt.think_time = 666 * 2;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
16, hub->tt.think_time);
break;
case HUB_TTTT_24_BITS:
hub->tt.think_time = 666 * 3;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
24, hub->tt.think_time);
break;
case HUB_TTTT_32_BITS:
hub->tt.think_time = 666 * 4;
dev_dbg(hub_dev, "TT requires at most %d "
"FS bit times (%d ns)\n",
32, hub->tt.think_time);
break;
}
/* probe() zeroes hub->indicator[] */
//端口指示燈
if (wHubCharacteristics & HUB_CHAR_PORTIND) {
hub->has_indicators = 1;
dev_dbg(hub_dev, "Port indicators are supported\n");
}
dev_dbg(hub_dev, "power on to power good time: %dms\n",
hub->descriptor->bPwrOn2PwrGood * 2);
/* power budgeting mostly matters with bus-powered hubs,
* and battery-powered root hubs (may provide just 8 mA).
*/
//一個控制傳輸,發送的是一個控制請求,GET_STATUS是USB的標準請求之一,獲得Device的狀態,保存在hubstatus裏面
ret = usb_get_status(hdev, USB_RECIP_DEVICE, 0, &hubstatus);
if (ret < 2) {
message = "can't get hub status";
goto fail;
}
le16_to_cpus(&hubstatus);
//如果是root hub,給hub->mA_per_port賦值
//計算機的usb端口可以提供500mA的電流
if (hdev == hdev->bus->root_hub) {
if (hdev->bus_mA == 0 || hdev->bus_mA >= 500)
hub->mA_per_port = 500;
//這個hub沒法提供達到500mA的電流,limited_power記錄限制了電流的情況
else {
hub->mA_per_port = hdev->bus_mA;
hub->limited_power = 1;
}
//表徵這個hub是不是自己供電的,外接的hub也有兩種供電方式,自己供電或者請求總線供電
//要總線供電的hub,於是limited_power也設置爲1
} else if ((hubstatus & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
dev_dbg(hub_dev, "hub controller current requirement: %dmA\n",
hub->descriptor->bHubContrCurrent);
hub->limited_power = 1;
if (hdev->maxchild > 0) {
//remaining來記錄剩下多少電流
int remaining = hdev->bus_mA -
hub->descriptor->bHubContrCurrent;
//比如你這個hub有四個口,即maxchild爲4,那麼你最少要剩下400mA電流,
//因爲如果某個端口電流小於100mA的話,設備是沒法正常工作的
if (remaining < hdev->maxchild * 100)
dev_warn(hub_dev,
"insufficient power available "
"to use all downstream ports\n");
hub->mA_per_port = 100; /* 7.2.1.1 */
}
//如果是自己供電的那種hub,直接設置爲500mA
} else { /* Self-powered external hub */
/* FIXME: What about battery-powered external hubs that
* provide less current per port? */
hub->mA_per_port = 500;
}
if (hub->mA_per_port < 500)
dev_dbg(hub_dev, "%umA bus power budget for each child\n",
hub->mA_per_port);
/* Update the HCD's internal representation of this hub before khubd
* starts getting port status changes for devices under the hub.
*/
hcd = bus_to_hcd(hdev->bus);
if (hcd->driver->update_hub_device) {
ret = hcd->driver->update_hub_device(hcd, hdev,
&hub->tt, GFP_KERNEL);
if (ret < 0) {
message = "can't update HCD hub info";
goto fail;
}
}
//這個請求是hub自己定義的,最後狀態保存在status和change裏面
ret = hub_hub_status(hub, &hubstatus, &hubchange);
if (ret < 0) {
message = "can't get hub status";
goto fail;
}
/* local power status reports aren't always correct */
if (hdev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_SELFPOWER)
dev_dbg(hub_dev, "local power source is %s\n",
(hubstatus & HUB_STATUS_LOCAL_POWER)
? "lost (inactive)" : "good");
if ((wHubCharacteristics & HUB_CHAR_OCPM) == 0)
dev_dbg(hub_dev, "%sover-current condition exists\n",
(hubstatus & HUB_STATUS_OVERCURRENT) ? "" : "no ");
/* set up the interrupt endpoint
* We use the EP's maxpacket size instead of (PORTS+1+7)/8
* bytes as USB2.0[11.12.3] says because some hubs are known
* to send more data (and thus cause overflow). For root hubs,
* maxpktsize is defined in hcd.c's fake endpoint descriptors
* to be big enough for at least USB_MAXCHILDREN ports. */
//獲得連接host與這個端點的這條管道
pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
//獲得一個endpoint描述符裏面的wMaxPacketSize,賦給maxp
maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
//前面爲hub->buffer申請了內存,這裏maxp如果大於這個size,就讓它等於hub->buffer的size
if (maxp > sizeof(*hub->buffer))
maxp = sizeof(*hub->buffer);
//申請一個urb
hub->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!hub->urb) {
ret = -ENOMEM;
goto fail;
}
//填充urb,urb完成了就調用hub_irq函數
usb_fill_int_urb(hub->urb, hdev, pipe, *hub->buffer, maxp, hub_irq,
hub, endpoint->bInterval);
hub->urb->transfer_dma = hub->buffer_dma;
hub->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
/* maybe cycle the hub leds */
//blinkenlights表示燈閃不閃
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
hub_activate(hub, HUB_INIT);
return 0;
fail:
dev_err (hub_dev, "config failed, %s (err %d)\n",
message, ret);
/* hub_disconnect() frees urb and descriptor */
return ret;
}
什麼叫hub描述符?
對於hub來說,除了通常的usb設備有的那些設備描述符,接口描述符,端點描述符以外,hub spec自己也定義了一個描述符,這就叫hub描述符。
struct usb_hub_descriptor {
__u8 bDescLength; //長度>=9,不定長
__u8 bDescriptorType; //#define USB_DT_HUB 0x29
__u8 bNbrPorts; //hub所支持的端口數量
__le16 wHubCharacteristics;
__u8 bPwrOn2PwrGood;
__u8 bHubContrCurrent; //Hub控制器的最大電流需求
/* add 1 bit for hub status change; round to bytes */
__u8 DeviceRemovable[(USB_MAXCHILDREN + 1 + 7) / 8]; //用來判斷這個端口連接的設備是否是可以移除的,如果該bit爲0,則說明可以被移除
__u8 PortPwrCtrlMask[(USB_MAXCHILDREN + 1 + 7) / 8];
} __attribute__ ((packed));
- 用get_hub_descriptor函數,獲取hub描述符。
static int get_hub_descriptor(struct usb_device *hdev, void *data, int size)
{
int i, ret;
//requesttype = USB_DIR_IN | USB_RT_HUB
//bit7 = USB_DIR_IN,表示Device-to-host
//bit5~6 = 01,表示class特定的
//bit0~4 = USB_RECIP_DEVICE,表示接收者是設備
//wvalue = USB_DT_HUB << 8, GET_DESCRIPTOR這個請求的wValue就該是Descriptor Type和Descriptor Index,
//wValue作爲一個word,有16位,所以高8位放Descriptor Type,而低8位放Descriptor Index
//hub descriptor的descriptor index必須爲0
for (i = 0; i < 3; i++) {
ret = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),
USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB,
USB_DT_HUB << 8, 0, data, size,
USB_CTRL_GET_TIMEOUT);
if (ret >= (USB_DT_HUB_NONVAR_SIZE + 2))
return ret;
}
return -EINVAL;
}
hub spec規定, bmRequestType必須是10100000B,因爲D7爲方向位,這裏是IN,所以D7 = 1;
D[6:5]這兩位表示Request的類型,hub是Class特定的,所以D[6:5] = 01;
D[4:0]表示接受者,可以是設備,可以是接口,可以是端點,這裏是設備,所以D[4:0] = 0
hub spec規定GET_DESCRIPTOR這個請求的wValue就該是Descriptor Type和Descriptor Index,高8位放Descriptor Type,而低8位放Descriptor Index。hub descriptor的descriptor index必須爲0。所以,wvalue = USB_DT_HUB << 8。
- 保存hub支持的端口數量,最大需求電流,電源切換方式
- 設置hub的高速切換方式
- 獲取hub設備狀態,設置端口的供電電流
- 申請中斷傳輸urb,初始化
- 調用hub_activate(hub, HUB_INIT),調用hub_power_on,提交urb
文章參考:https://blog.csdn.net/fudan_abc/article/category/325189