本文是基于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