参考文章:https://blog.csdn.net/fudan_abc/article/details/1779800
资料:https://pan.baidu.com/s/1AZlZsqF_-VGREzLaQPdY9Q 提取码:15ky
static const struct usb_device_id hub_id_table[] = {{ .match_flags = USB_DEVICE_ID_MATCH_VENDOR| USB_DEVICE_ID_MATCH_INT_CLASS,.idVendor = USB_VENDOR_GENESYS_LOGIC,.bInterfaceClass = USB_CLASS_HUB,.driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},{ .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 */
};
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,.unlocked_ioctl = hub_ioctl,.id_table = hub_id_table,.supports_autosuspend = 1,
};
前篇分析usb主控制器驱动的时候谈过,注册完root_hub的时候,由于满足这里的struct usb_device_id要求 ,所以会调用这里的hub_probe函数:
int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
{struct usb_host_interface *desc;struct usb_device *hdev;struct usb_hub *hub;hdev = interface_to_usbdev(intf); //hdev设备对应的就是上面注册的root_hub设备hub = kzalloc(sizeof(*hub), GFP_KERNEL);hub->hdev = hdev;INIT_WORK(&hub->events, hub_event);if (hdev->speed == USB_SPEED_HIGH) //表示当是高速hub时,highspeed_hubs加1highspeed_hubs++;hub_configure(hub, &desc->endpoint[0].desc)
}
hub_configure函数:
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;get_hub_descriptor(hdev, hub->descriptor);hub->ports = kzalloc(maxchild * sizeof(struct usb_port *), GFP_KERNEL);for (i = 0; i < maxchild; i++) {usb_hub_create_port_device(hub, i + 1);}
}
hub_configure函数会根据hub的端口数创建端口设备。
usb_hub_create_port_device函数:
int usb_hub_create_port_device(struct usb_hub *hub, int port1)
{struct usb_port *port_dev;struct usb_device *hdev = hub->hdev;port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);hub->ports[port1 - 1] = port_dev;port_dev->portnum = port1;dev_set_name(&port_dev->dev, "%s-port%d", dev_name(&hub->hdev->dev),port1);device_register(&port_dev->dev);
}
usb_hub_create_port_device函数具体负责创建注册第几个端口设备,并赋值给hub端口数组。
hub_event服务程序:
void hub_event(struct work_struct *work)
{port_event(hub, i);
}
port_event函数:
void port_event(struct usb_hub *hub, int port1)
{hub_port_connect_change(hub, port1, portstatus, portchange);
}
hub_port_connect_change函数:
void hub_port_connect_change(struct usb_hub *hub, int port1,u16 portstatus, u16 portchange)
{hub_port_connect(hub, port1, portstatus, portchange);
}
hub_port_connect函数:
void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,u16 portchange)
{struct usb_device *hdev = hub->hdev;struct usb_hcd *hcd = bus_to_hcd(hdev->bus);struct usb_port *port_dev = hub->ports[port1 - 1];struct usb_device *udev = port_dev->child; //NULL//SET_CONFIG_TRIES值是4//尝试4次,成功一次就退出for (i = 0; i < SET_CONFIG_TRIES; i++) {udev = usb_alloc_dev(hdev, hdev->bus, port1);choose_devnum(udev);status = hub_port_init(hub, udev, port1, i);port_dev->child = udev; //NOT NULLusb_new_device(udev);return;}
}
当发觉hub端口有插入变化时,hub_port_connect函数会先分配一个usb设备,并进行初始化,然后把刚才分配的usb设备给注册了。usb_new_device函数注册usb设备之前会从设备描述符里面获取pid、vid等信息,所以需要在usb_new_device函数前先获取设备描述符,该操作在hub_port_init函数里完成的。
hub_port_init函数:
static int hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,int retry_counter)
{struct usb_device *hdev = hub->hdev;struct usb_hcd *hcd = bus_to_hcd(hdev->bus);int retries, operations, retval, i;unsigned delay = HUB_SHORT_RESET_TIME;enum usb_device_speed oldspeed = udev->speed; //记录设备在没有reset之前的速度const char *speed;int devnum = udev->devnum;const char *driver_name;//root hubif (!hdev->parent) {delay = HUB_ROOT_RESET_TIME;}if (oldspeed == USB_SPEED_LOW)delay = HUB_LONG_RESET_TIME;retval = hub_port_reset(hub, port1, udev, delay, false);oldspeed = udev->speed;//hub_port_reset函数设置udev->speed值,usb2.0 spec规定了不同速度的usb设备对应的不同大小switch (udev->speed) {case USB_SPEED_SUPER_PLUS:case USB_SPEED_SUPER:case USB_SPEED_WIRELESS: /* fixed at 512 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(512);break;case USB_SPEED_HIGH: /* fixed at 64 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);break;case USB_SPEED_FULL: /* 8, 16, 32, or 64 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(64);break;case USB_SPEED_LOW: /* fixed at 8 */udev->ep0.desc.wMaxPacketSize = cpu_to_le16(8);break;}if (udev->speed == USB_SPEED_WIRELESS)speed = "variable speed Wireless";elsespeed = usb_speed_string(udev->speed);//如果出错,多给几次机会for (retries = 0; retries < GET_DESCRIPTOR_TRIES; (++retries, msleep(100))) {bool did_new_scheme = false;//判断用的是新策略还是老策略 scheme:计划,策划if (use_new_scheme(udev, retry_counter)) {struct usb_device_descriptor *buf;int r = 0;did_new_scheme = true;retval = hub_enable_device(udev);#define GET_DESCRIPTOR_BUFSIZE 64buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);//获取设备描述符成功概率比较低,所以循环获取三次for (operations = 0; operations < 3; ++operations) {buf->bMaxPacketSize0 = 0;//发送获取设备描述符命令,只获取前64字节数据r = usb_control_msg(udev, usb_rcvaddr0pipe(),USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE,initial_descriptor_timeout);// 判断获取到的前64字节里的buf->bMaxPackSize0值,该值的合理值只有 // 8/16/32/64/512,这里255实际上是WUSB协议规定的,毕竟只有8位,最大就是255了 switch (buf->bMaxPacketSize0) {case 8: case 16: case 32: case 64: case 255:if (buf->bDescriptorType ==USB_DT_DEVICE) {r = 0;break;}//包括未响应设备,向设备获取设备描述符,设备不回应,buf->bMaxPackSize0=0default:if (r == 0)r = -EPROTO;break;}if (r == 0 || (r == -ETIMEDOUT && retries == 0 && udev->speed > USB_SPEED_FULL))break; }//用udev->descriptor.bMaxPacketSize0来记录这个临时获得的值udev->descriptor.bMaxPacketSize0 =buf->bMaxPacketSize0;kfree(buf);retval = hub_port_reset(hub, port1, udev, delay, false);if (oldspeed != udev->speed) {dev_dbg(&udev->dev,"[DEBUG][USB] device reset changed speed!\n");retval = -ENODEV;goto fail;}#undef GET_DESCRIPTOR_BUFSIZE}if (udev->wusb == 0) { //wusb:wireless usbfor (operations = 0; operations < SET_ADDRESS_TRIES; ++operations) {//设置usb地址 retval = hub_set_address(udev, devnum);if (retval >= 0)break; msleep(200);}// 如果是用新策略,did_new_scheme等于true,跳出循环,不执行后面的if (did_new_scheme)break; }//旧策略先设置地址,后在这里获取描述符retval = usb_get_device_descriptor(udev, 8);if (retval < 8) {} else {retval = 0;break; }}usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);if (hcd->driver->update_device)hcd->driver->update_device(hcd, udev);
}
a、hub_port_init函数先是对usb设备进行复位,然后根据udev->speed值设置usb设备的包的最大字节大小,再然后是分配地址,获取设备描述符。
b、hub_port_init函数里面delay不同取值也是有相应意义的,默认HUB_SHORT_RESET_TIME,表示复位信号的最短时间,如果该hub是root hub,那么root hub要求时间的最短时间是HUB_ROOT_RESET_TIME。如果是低速设备的话,需要一个较长的延迟时间,否则可能会出错,所有要取HUB_LONG_RESET_TIME。参考usb2.0 spec手册 7.1.7.5章节复位信号。
c、USB_NEW_SCHEME(retry_counter),retry_counter就是hub_port_init()传递进来的最后一个参数,而我们给它的实参正是那个从0到SET_CONFIG_TRIES-1的那个i.假设我们什么也没有设置,都是使用默认值,那么use_both_schemes默认值为1,而old_scheme_first默认值为0,于是SET_CONFIG_TRIES为4,即i将从0变到3,而USB_NEW_SCHEME(i)将在i为0和1的时候为1,在i为2和3的时候为0.再加上i每取一次值,在hub_port_init函数里面都会循环两次,所以也就是说,先进行四次新的策略,如果不行就再进行四次旧的策略.所有这一切只有一个目的,就是为了获得设备的描述符。
d、由于 hub_port_reset函数比较长,又是重点,放到后面,可以先看下其它的一些函数。
use_new_scheme函数:
bool use_new_scheme(struct usb_device *udev, int retry)
{if (udev->speed >= USB_SPEED_SUPER)return false;return USE_NEW_SCHEME(retry);
}
// old_scheme_first: bool类型的变量
#define USE_NEW_SCHEME(i) ((i) / 2 == (int)old_scheme_first)
hub_enable_device函数:
static int hub_enable_device(struct usb_device *udev)
{struct usb_hcd *hcd = bus_to_hcd(udev->bus);if (!hcd->driver->enable_device)return 0;if (udev->state == USB_STATE_ADDRESS)return 0;if (udev->state != USB_STATE_DEFAULT)return -EINVAL;return hcd->driver->enable_device(hcd, udev);
}
分析函数:usb_control_msg(udev, usb_rcvaddr0pipe(),USB_REQ_GET_DESCRIPTOR,
USB_DIR_IN,USB_DT_DEVICE << 8, 0, buf,
GET_DESCRIPTOR_BUFSIZE,initial_descriptor_timeout);
这个函数的参数说明请往下看,传的参数如下:
//USB_DIR_IN对应的请求类型是获取描述符
#define USB_DIR_IN 0x80
#define GET_DESCRIPTOR_BUFSIZE 64
#define USB_DT_DEVICE 0x01
Get Descriptor请求(获取描述符,具体哪种,还得看wvalue,wvalue是16bit的数据,高字节表示描述符类型,低字节表示描述符索引,具体参考usb spec2.0手册):
描述符类型如下:
wvalue= USB_DT_DEVICE << 8,USB_DT_DEVICE << 8等于0x0100,高字节等于1,所以获取的是设备描述符。
hub_set_address函数:
static int hub_set_address(struct usb_device *udev, int devnum)
{struct usb_hcd *hcd = bus_to_hcd(udev->bus);usb_control_msg(udev, usb_sndaddr0pipe(),USB_REQ_SET_ADDRESS, 0, devnum, 0,NULL, 0, USB_CTRL_SET_TIMEOUT);}
该函数设置usb地址 。
现在回来重点讲hub_port_reset函数:
static int hub_port_reset(struct usb_hub *hub, int port1,struct usb_device *udev, unsigned int delay, bool warm)
{int i, status;u16 portchange, portstatus;struct usb_port *port_dev = hub->ports[port1 - 1];/* Reset the port */for (i = 0; i < PORT_RESET_TRIES; i++) {//#define USB_PORT_FEAT_RESET 4 //USB_PORT_FEAT_RESET 对应的功能是复位,参考下面的表11-17status = set_port_feature(hub->hdev, port1, (warm ? USB_PORT_FEAT_BH_PORT_RESET :USB_PORT_FEAT_RESET));status = hub_port_wait_reset(hub, port1, udev, delay,warm);if (status == 0 || status == -ENOTCONN || status == -ENODEV) {usb_clear_port_feature(hub->hdev, port1,USB_PORT_FEAT_C_RESET);if (!hub_is_superspeed(hub->hdev))goto done;}}
done:if (status == 0) {msleep(10 + 40);if (udev) {struct usb_hcd *hcd = bus_to_hcd(udev->bus); if (hcd->driver->reset_device)hcd->driver->reset_device(hcd, udev);usb_set_device_state(udev, USB_STATE_DEFAULT);}} return status;
}
hub_port_reset函数先是通过set_port_feature函数发出复位命令,进行复位,再通过 hub_port_wait_reset函数实时查询复位是否已完成。如果完成,通过usb_clear_port_feature函数发出清除复位命令。
//发出复位命令
int set_port_feature(struct usb_device *hdev, int port1, int feature)
{//USB_RT_PORT对应的请求类型是Set Featurereturn usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0),USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port1, NULL, 0, 1000);
}
//USB_RT_PORT的值是:0010,0011
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
#define USB_TYPE_CLASS (0x01 << 5)
#define USB_RECIP_OTHER 0x03
Set Feature请求(参考usb2.0 spec手册):
Feature Selector(功能选择) 参考表11-17:
usb_control_msg函数说明:
int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,__u8 requesttype, __u16 value, __u16 index, void *data,__u16 size, int timeout)@dev: pointer to the usb device to send the message to
@pipe: endpoint "pipe" to send the message to
@request: USB message request value
@requesttype: USB message request type value
@value: USB message value
@index: USB message index value
@data: pointer to the data to send
@size: length in bytes of the data to send
@timeout: time in msecs to wait for the message to complete before timing out (if 0 the wait is forever)Return: If successful, the number of bytes transferred. Otherwise, a negative error number.
static int hub_port_wait_reset(struct usb_hub *hub, int port1,struct usb_device *udev, unsigned int delay, bool warm)
{int delay_time, ret;u16 portstatus;u16 portchange;u32 ext_portstatus = 0;for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) {msleep(delay);ret = hub_port_status(hub, port1, &portstatus,&portchange);//复位完成,跳出循环if (!(portstatus & USB_PORT_STAT_RESET) &&(portstatus & USB_PORT_STAT_CONNECTION))break;if (delay_time >= 2 * HUB_SHORT_RESET_TIME)delay = HUB_LONG_RESET_TIME;}if ((portstatus & USB_PORT_STAT_RESET))return -EBUSY;if (!(portstatus & USB_PORT_STAT_ENABLE))return -EBUSY;if (!udev)return 0;if (hub_is_wusb(hub))udev->speed = USB_SPEED_WIRELESS;else if (hub_is_superspeedplus(hub->hdev) &&port_speed_is_ssp(hub->hdev, ext_portstatus &USB_EXT_PORT_STAT_RX_SPEED_ID))udev->speed = USB_SPEED_SUPER_PLUS;else if (hub_is_superspeed(hub->hdev))udev->speed = USB_SPEED_SUPER;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;elseudev->speed = USB_SPEED_FULL;return 0;
}
hub_port_wait_reset函数通过for循环每隔delay毫秒查询一次reset状态。reset状态是通过hub_port_status函数获取的,该函数还能区分是高速、全速还是低速设备。
hub_port_status函数:
static int hub_port_status(struct usb_hub *hub, int port1, u16 *status, u16 *change)
{return hub_ext_port_status(hub, port1, HUB_PORT_STATUS,status, change, NULL);
}
hub_ext_port_status函数:
static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,u16 *status, u16 *change, u32 *ext_status)
{int ret;int len = 4;if (type != HUB_PORT_STATUS)len = 8;ret = get_port_status(hub->hdev, port1, &hub->status->port, type, len);*status = le16_to_cpu(hub->status->port.wPortStatus);*change = le16_to_cpu(hub->status->port.wPortChange);if (type != HUB_PORT_STATUS && ext_status)*ext_status = le32_to_cpu(hub->status->port.dwExtPortStatus);
}struct usb_hub结构:
struct usb_hub {struct device *intfdev; /* the "interface" device */struct usb_device *hdev;struct urb *urb; /* for interrupt polling pipe */u8 (*buffer)[8];union {struct usb_hub_status hub;struct usb_port_status port;}*status; /* buffer for status reports */
}struct usb_port_status结构:
struct usb_port_status {__le16 wPortStatus;__le16 wPortChange;__le32 dwExtPortStatus;
} __attribute__ ((packed));
get_port_status函数:
static int get_port_status(struct usb_device *hdev, int port1,void *data, u16 value, u16 length)
{int i, status = -ETIMEDOUT;for (i = 0; i < USB_STS_RETRIES &&(status == -ETIMEDOUT || status == -EPIPE); i++) {status = usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0),USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, value,port1, data, length, USB_STS_TIMEOUT);}return status;
}
#define USB_DIR_IN 0x80 /* to host */
/*由上面知USB_RT_PORT: 0010,0011 */
#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER)
Get Port Status请求(参考usb2.0 spec手册):
该命令请求不但能获取reset状态,还能获取是高速、全速还是低速设备。