usb hub驱动

本文深入剖析Linux USB驱动中hub_port_connect函数的作用,详细讲解了hub_port_connect如何处理hub端口设备的插入变化,包括设备的分配、初始化、注册等步骤,以及在获取设备描述符过程中的新旧策略。通过对hub_port_init、hub_port_reset等关键函数的分析,揭示了USB设备连接和识别的流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文章:Linux那些事儿之我是Hub(20)八大重量级函数闪亮登场(四)-CSDN博客

资料: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加1
		highspeed_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 NULL
	    usb_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 hub
    if (!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";
	else
		speed = 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	64

			buf = 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=0
				default:
					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 usb
			for (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手册): 

 GET_DESCTIPTOR的值:

 描述符类型如下:

wvalue= USB_DT_DEVICE << 8,USB_DT_DEVICE << 8等于0x0100,高字节等于1,所以获取的是设备描述符。

usb所有的标准请求命令:

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-17
		status = 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 Feature
	return 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;
	else
		udev->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状态,还能获取是高速、全速还是低速设备。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值