usb hub驱动

news/2024/11/23 0:05:58/

参考文章: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状态,还能获取是高速、全速还是低速设备。


http://www.ppmy.cn/news/334971.html

相关文章

ps软件怎么测试性能,怎么用ps测试电脑性能 设计师要知道

对于设计师而言&#xff0c;电脑是吃饭的工具&#xff0c;如果工具不趁手&#xff0c;势必事倍功半。我相信大多数准备入行或已经工作多年的设计师&#xff0c;都明白这个道理。 画布的创建 先说大家熟悉的 Photoshop&#xff0c;这是一款位图软件&#xff0c;而它重要的功能就…

索尼PS5主机系统软件更新推出 大量问题未解决

PS5首批主机在日本、美国、加拿大、墨西哥、澳洲、新西兰和韩国等地发布后&#xff0c;一直出现多种不同的问题&#xff0c;部分更声称会引致玩家游戏崩溃等&#xff0c;近日官方终于推出了系统软件更新。 Sony近日为PS5推出了一个866mb的20.02-02.25.00版本系统软件更新&#…

外媒爆料:PS5将在2019年之后发售,是一次真正的更新换代!

关于索尼的下一代新主机传闻近期层出不穷&#xff0c;并且很多消息都表示它将会在2018年底或者2019年发售。不过&#xff0c;根据消息一直很准确的知名媒体 Kotaku 爆料&#xff0c;索尼确实正在筹备全新的主机&#xff0c;但不会在近期发售。在报道中 Kotaku 表示&#xff0c;…

Py之eli5:eli5库的简介、安装、使用方法之详细攻略

Py之eli5&#xff1a;eli5库的简介、安装、使用方法之详细攻略 目录 eli5库的简介 1、eli5库有助于机器学习的可解释性 2、eli5库实现了几种用于检查黑盒模型的算法(参见检查黑盒估计器) 3、eli5库支持的机器学习框架和包—可以和大多数通用的python机器学习工具包一起使用…

常见的性能测试与应用领域

目录 前言一、常见的6种性能测试1.后端性能测试2.前端性能测试3.代码级性能测试4.压力测试5.并发测试6.可靠性测试 二、性能测试的四大应用领域能力验证能力规划性能调优缺陷发现 前言 分两个方面讲性能&#xff0c;主要就是一些概念性的东西 一、常见的6种性能测试 1.后端性…

ps太卡怎么解决

PS是一款非常常用的绘图软件&#xff0c;但是有些小伙伴感觉用PS会卡顿&#xff0c;可是电脑的配置也不是很低&#xff0c;排除PS本身的问题后&#xff0c;这种情况往往和ps里的基础设置有关。macw教大家解决PS卡顿的问题。 1、停用“主页”屏幕 现在很多小伙伴都用上了新版p…

Android系统性能监控最全面分析与实践(一)

1、背景 随着移动互联网技术的发展,安卓APP的功能越来越多,对于APP性能的要求也随之提高。目前有很多应用性能监控(APM:Application perfmance monitor)的工具,如阿里的mobileperf,网易开源的Emmagee,腾讯的Matrix等等。 以上主流的性能监控是针对APP层,对安卓系统性能…

jmeter性能测试步骤

&#x1f3c6;作者简介&#xff1a;哪吒&#xff0c;CSDN2022博客之星Top1、CSDN2021博客之星Top2、多届新星计划导师✌、博客专家&#x1f4aa;&#xff0c;专注Java硬核干货分享&#xff0c;立志做到Java赛道全网Top N。 &#x1f3c6;本文收录于&#xff0c;Java基础教程系列…