插入一个 USB设备的处理机制总体计:
1. 中断定时查询:
2. 总体架构设计:
3. 解析各个部分:
中断定时查询:
Hub层处理
usb枚举
当守护程序第一次运行或usb port上状态发生变化,守护进程被唤醒都会运行hub_events
函数,这个函数在usb系统中处理核心位置,usb的枚举过程就是由它完成。
usb具体的枚举流程:
hub_events
函数
-
-
static void hub_events(void)
-
{
-
struct list_head *tmp;
-
struct usb_device *hdev;
-
struct usb_interface *intf;
-
struct usb_hub *hub;
-
struct device *hub_dev;
-
u16 hubstatus;
-
u16 hubchange;
-
u16 portstatus;
-
u16 portchange;
-
int i, ret;
-
int connect_change, wakeup_change;
-
/*
-
* We restart the list every time to avoid a deadlock with
-
* deleting hubs downstream from this one. This should be
-
* safe since we delete the hub from the event list.
-
* Not the most efficient, but avoids deadlocks.
-
*/
-
while (1) {
-
/* Grab the first entry at the beginning of the list */
-
spin_lock_irq(&hub_event_lock);
-
if (list_empty(&hub_event_list)) {
-
spin_unlock_irq(&hub_event_lock);
-
break;
-
}
-
tmp = hub_event_list.next;
-
list_del_init(tmp);
-
hub = list_entry(tmp, struct usb_hub, event_list);
-
kref_get(&hub->kref);
-
/* make sure hdev is not freed before accessing it */
-
if (hub->disconnected) { //判断hub是否连接
-
spin_unlock_irq(&hub_event_lock);
-
goto hub_disconnected;
-
} else {
-
usb_get_dev(hub->hdev);
-
}
-
spin_unlock_irq(&hub_event_lock);
-
hdev = hub->hdev;
-
hub_dev = hub->intfdev;
-
intf = to_usb_interface(hub_dev);
-
dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n",
-
hdev->state, hub->descriptor
-
? hub->descriptor->bNbrPorts
-
: 0,
-
/* NOTE: expects max 15 ports... */
-
(u16) hub->change_bits[0],
-
(u16) hub->event_bits[0]);
-
/* Lock the device, then check to see if we were
-
* disconnected while waiting for the lock to succeed. */
-
usb_lock_device(hdev);
-
if (unlikely(hub->disconnected))
-
goto loop_disconnected;
-
/* If the hub has died, clean up after it */
-
if (hdev->state == USB_STATE_NOTATTACHED) {
-
hub->error = -ENODEV;
-
hub_quiesce(hub, HUB_DISCONNECT);
-
goto loop;
-
}
-
/* Autoresume */
-
ret = usb_autopm_get_interface(intf);
-
if (ret) {
-
dev_dbg(hub_dev, "Can't autoresume: %d\n", ret);
-
goto loop;
-
}
-
/* If this is an inactive hub, do nothing */
-
if (hub->quiescing)
-
goto loop_autopm;
-
if (hub->error) {
-
dev_dbg (hub_dev, "resetting for error %d\n",
-
hub->error);
-
ret = usb_reset_device(hdev);
-
if (ret) {
-
dev_dbg (hub_dev,
-
"error resetting hub: %d\n", ret);
-
goto loop_autopm;
-
}
-
hub->nerrors = 0;
-
hub->error = 0;
-
}
-
/* deal with port status changes */
-
//遍历hub中的所有port,对各个port的状态进行查看,判断port是否发生了状态变化
-
for (i = 1; i <= hub->descriptor->bNbrPorts; i++) {
-
if (test_bit(i, hub->busy_bits))
-
continue;
-
connect_change = test_bit(i, hub->change_bits);
-
wakeup_change = test_and_clear_bit(i, hub->wakeup_bits);
-
if (!test_and_clear_bit(i, hub->event_bits) &&
-
!connect_change && !wakeup_change)
-
continue;
-
ret = hub_port_status(hub, i,
-
&portstatus, &portchange);
-
if (ret < 0)
-
continue;
-
if (portchange & USB_PORT_STAT_C_CONNECTION) {
-
usb_clear_port_feature(hdev, i,
-
USB_PORT_FEAT_C_CONNECTION);
-
connect_change = 1;
-
}
-
if (portchange & USB_PORT_STAT_C_ENABLE) {
-
if (!connect_change)
-
dev_dbg (hub_dev,
-
"port %d enable change, "
-
"status %08x\n",
-
i, portstatus);
-
usb_clear_port_feature(hdev, i,
-
USB_PORT_FEAT_C_ENABLE);
-
/*
-
* EM interference sometimes causes badly
-
* shielded USB devices to be shutdown by
-
* the hub, this hack enables them again.
-
* Works at least with mouse driver.
-
*/
-
if (!(portstatus & USB_PORT_STAT_ENABLE)
-
&& !connect_change
-
&& hub->ports[i - 1]->child) {
-
dev_err (hub_dev,
-
"port %i "
-
"disabled by hub (EMI?), "
-
"re-enabling...\n",
-
i);
-
connect_change = 1;
-
}
-
}
-
if (hub_handle_remote_wakeup(hub, i,
-
portstatus, portchange))
-
connect_change = 1;
-
if (portchange & USB_PORT_STAT_C_OVERCURRENT) {
-
u16 status = 0;
-
u16 unused;
-
dev_dbg(hub_dev, "over-current change on port "
-
"%d\n", i);
-
usb_clear_port_feature(hdev, i,
-
USB_PORT_FEAT_C_OVER_CURRENT);
-
msleep(100); /* Cool down */
-
hub_power_on(hub, true);
-
hub_port_status(hub, i, &status, &unused);
-
if (status & USB_PORT_STAT_OVERCURRENT)
-
dev_err(hub_dev, "over-current "
-
"condition on port %d\n", i);
-
}
-
if (portchange & USB_PORT_STAT_C_RESET) {
-
dev_dbg (hub_dev,
-
"reset change on port %d\n",
-
i);
-
usb_clear_port_feature(hdev, i,
-
USB_PORT_FEAT_C_RESET);
-
}
-
if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
-
hub_is_superspeed(hub->hdev)) {
-
dev_dbg(hub_dev,
-
"warm reset change on port %d\n",
-
i);
-
usb_clear_port_feature(hdev, i,
-
USB_PORT_FEAT_C_BH_PORT_RESET);
-
}
-
if (portchange & USB_PORT_STAT_C_LINK_STATE) {
-
usb_clear_port_feature(hub->hdev, i,
-
USB_PORT_FEAT_C_PORT_LINK_STATE);
-
}
-
if (portchange & USB_PORT_STAT_C_CONFIG_ERROR) {
-
dev_warn(hub_dev,
-
"config error on port %d\n",
-
i);
-
usb_clear_port_feature(hub->hdev, i,
-
USB_PORT_FEAT_C_PORT_CONFIG_ERROR);
-
}
-
/* Warm reset a USB3 protocol port if it's in
-
* SS.Inactive state.
-
*/
-
if (hub_port_warm_reset_required(hub, portstatus)) {
-
int status;
-
struct usb_device *udev =
-
hub->ports[i - 1]->child;
-
dev_dbg(hub_dev, "warm reset port %d\n", i);
-
if (!udev ||
-
!(portstatus & USB_PORT_STAT_CONNECTION) ||
-
udev->state == USB_STATE_NOTATTACHED) {
-
status = hub_port_reset(hub, i,
-
NULL, HUB_BH_RESET_TIME,
-
true);
-
if (status < 0)
-
hub_port_disable(hub, i, 1);
-
} else {
-
usb_lock_device(udev);
-
status = usb_reset_device(udev);
-
usb_unlock_device(udev);
-
connect_change = 0;
-
}
-
}
-
if (connect_change)//如果port状态发生变化,则调用hub_port_connect_change
-
hub_port_connect_change(hub, i,
-
portstatus, portchange);
-
} /* end for i */
-
/* deal with hub status changes */
-
if (test_and_clear_bit(0, hub->event_bits) == 0)
-
; /* do nothing */
-
else if (hub_hub_status(hub, &hubstatus, &hubchange) < 0)
-
dev_err (hub_dev, "get_hub_status failed\n");
-
else {
-
if (hubchange & HUB_CHANGE_LOCAL_POWER) {
-
dev_dbg (hub_dev, "power change\n");
-
clear_hub_feature(hdev, C_HUB_LOCAL_POWER);
-
if (hubstatus & HUB_STATUS_LOCAL_POWER)
-
/* FIXME: Is this always true? */
-
hub->limited_power = 1;
-
else
-
hub->limited_power = 0;
-
}
-
if (hubchange & HUB_CHANGE_OVERCURRENT) {
-
u16 status = 0;
-
u16 unused;
-
dev_dbg(hub_dev, "over-current change\n");
-
clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
-
msleep(500); /* Cool down */
-
hub_power_on(hub, true);
-
hub_hub_status(hub, &status, &unused);
-
if (status & HUB_STATUS_OVERCURRENT)
-
dev_err(hub_dev, "over-current "
-
"condition\n");
-
}
-
}
-
loop_autopm:
-
/* Balance the usb_autopm_get_interface() above */
-
usb_autopm_put_interface_no_suspend(intf);
-
loop:
-
/* Balance the usb_autopm_get_interface_no_resume() in
-
* kick_khubd() and allow autosuspend.
-
*/
-
usb_autopm_put_interface(intf);
-
loop_disconnected:
-
usb_unlock_device(hdev);
-
usb_put_dev(hdev);
-
hub_disconnected:
-
kref_put(&hub->kref, hub_release);
-
} /* end while (1) */
-
}
-
hub_events
本身是一个死循环,只要条件满足它便会一直执行。
首先判断hub_event_list
是否为空,如果为空,则跳出循环,hub_events
运行结束,会进入休眠状态;如果不为空,则从hub_event_list
列表中取出某一项,并把它从hub_event_list
中删除,通过list_entry
来获取event_list
所对应的hub,并增加hub使用计数;然后做了一些逻辑判断,判断hub是否连接,hub上是否有错误,如果有错误就重启hub,如果没有错误,接下来就对hub 上的每个port进行扫描,判断各个port是否发生状态变化;
接着遍历hub中的所有port,对各个port的状态进行查看,判断port是否发生了状态变化,如果产生了变化则调用hub_port_connect_change
进行处理;
在hub结构中存在多个标志位,用来表示port一些状态:
- 如果usb的第i个port处于resume或reset状态,则hub中
busy_bits
中第i个位置1;如果busy_bits
中第i个位置1,则退过当前port; -
event_bits
中第0位用来表示hub本身是否产生local power status change和是否产生过流,其它的各位用来表示hub下各个port状态是否发生变化,这些状态包括: ConnectStatusChange 连接状态发生变化,PortEnableStatusChange端口使能状态发生变化,PortSuspendStatusChange端口从挂起到恢复完成,PortOverCurrentIndicatorChange端口过流状态发生变化,PortResetStatusChange端口reset10ms置位; -
remove_bits
用来表示hub下各个port是否有设备连接,如果置位表示没有设备连接或设备已经断开; -
change_bits
用来表示hub下各个端口逻辑状态是否发生变化,它是在hub初始化的时候赋值的,这些状态主要有:1.原先port上没有设备,现在检测到有设备连接;2.原先port上有设备,由于控制器不正常关闭或禁止usb port等原图使得该设备处于NOATTACHED态,则需要断开设备;
在遍历port时,首先对这些标志进行判断,如果没有产生变化则跳过当前port,否则读取当前port的status,判断出产生状态变化的原因;
如果发生变化port的连接状态发生变化,清除相应的端口状态,设置connect_change
变量为1;
如果port的使能状态发生变化,清除相应的状态标志,如果是由于EMI电磁干扰引起的状态变化,则设置connect_change
变量为1;
……
经过一系列列port判断,如果发现port的连接状态发生变化或由于EMI导致连接使能发生变化,即connect_change=1
,则调用hub_port_connect_change
.
-
/* Handle physical or logical connection change events.
-
* This routine is called when:
-
* a port connection-change occurs;
-
* a port enable-change occurs (often caused by EMI);
-
* usb_reset_and_verify_device() encounters changed descriptors (as from
-
* a firmware download)
-
* caller already locked the hub
-
*/
-
static void hub_port_connect_change(struct usb_hub *hub, int port1,
-
u16 portstatus, u16 portchange)
-
{
-
struct usb_device *hdev = hub->hdev;
-
struct device *hub_dev = hub->intfdev;
-
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
-
unsigned wHubCharacteristics =
-
le16_to_cpu(hub->descriptor->wHubCharacteristics);
-
struct usb_device *udev;
-
int status, i;
-
unsigned unit_load;
-
dev_dbg (hub_dev,
-
"port %d, status %04x, change %04x, %s\n",
-
port1, portstatus, portchange, portspeed(hub, portstatus));
-
if (hub->has_indicators) {//如果当前的hub支持indicator指示灯,则设备指示灯为HUB_LED_AUTO
-
set_port_led(hub, port1, HUB_LED_AUTO);
-
hub->indicator[port1-1] = INDICATOR_AUTO;
-
}
-
#ifdef CONFIG_USB_OTG
-
/* during HNP, don't repeat the debounce */
-
if (hdev->bus->is_b_host)
-
portchange &= ~(USB_PORT_STAT_C_CONNECTION |
-
USB_PORT_STAT_C_ENABLE);
-
#endif
-
/* Try to resuscitate an existing device */
-
udev = hub->ports[port1 - 1]->child;
-
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
-
udev->state != USB_STATE_NOTATTACHED) {
-
usb_lock_device(udev);
-
if (portstatus & USB_PORT_STAT_ENABLE) {
-
status = 0; /* Nothing to do */
-
#ifdef CONFIG_PM_RUNTIME
-
} else if (udev->state == USB_STATE_SUSPENDED &&
-
udev->persist_enabled) {
-
/* For a suspended device, treat this as a
-
* remote wakeup event.
-
*/
-
status = usb_remote_wakeup(udev);
-
#endif
-
} else {
-
status = -ENODEV; /* Don't resuscitate */
-
}
-
usb_unlock_device(udev);
-
if (status == 0) { //清除当前port的change_bits标志
-
clear_bit(port1, hub->change_bits);
-
return;
-
}
-
}
-
/* Disconnect any existing devices under this port */
-
if (udev) {
-
if (hcd->phy && !hdev->parent &&
-
!(portstatus & USB_PORT_STAT_CONNECTION))
-
usb_phy_notify_disconnect(hcd->phy, udev->speed);
-
usb_disconnect(&hub->ports[port1 - 1]->child);
-
}
-
clear_bit(port1, hub->change_bits);
-
/* We can forget about a "removed" device when there's a physical
-
* disconnect or the connect status changes.
-
*/
-
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
-
(portchange & USB_PORT_STAT_C_CONNECTION))
-
clear_bit(port1, hub->removed_bits);
-
if (portchange & (USB_PORT_STAT_C_CONNECTION |
-
USB_PORT_STAT_C_ENABLE)) {
-
status = hub_port_debounce_be_stable(hub, port1);
-
if (status < 0) {
-
if (status != -ENODEV && printk_ratelimit())
-
dev_err(hub_dev, "connect-debounce failed, "
-
"port %d disabled\n", port1);
-
portstatus &= ~USB_PORT_STAT_CONNECTION;
-
} else {
-
portstatus = status;
-
}
-
}
-
/* Return now if debouncing failed or nothing is connected or
-
* the device was "removed".
-
*/
-
if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
-
test_bit(port1, hub->removed_bits)) {
-
/* maybe switch power back on (e.g. root hub was reset) */
-
if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
-
&& !port_is_power_on(hub, portstatus))
-
set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
-
if (portstatus & USB_PORT_STAT_ENABLE)
-
goto done;
-
return;
-
}
-
if (hub_is_superspeed(hub->hdev))
-
unit_load = 150;
-
else
-
unit_load = 100;
-
status = 0;
-
for (i = 0; i < SET_CONFIG_TRIES; i++) {
-
/* reallocate for each attempt, since references
-
* to the previous one can escape in various ways
-
* 通过usb_alloc_dev为新的USB设备申请资源,并进行一些初始化,
-
* 将usb设置为USB_STATE_ATTACHED,表示设备已经连接
-
*/
-
udev = usb_alloc_dev(hdev, hdev->bus, port1);
-
if (!udev) {
-
dev_err (hub_dev,
-
"couldn't allocate port %d usb_device\n",
-
port1);
-
goto done;
-
}
-
usb_set_device_state(udev, USB_STATE_POWERED);//设置usb设备为USB_STATE_POWERED态
-
udev->bus_mA = hub->mA_per_port;//设置usb设备可以从port上获取的电流量
-
udev->level = hdev->level + 1;,//设置usb设备的拓扑层级
-
udev->wusb = hub_is_wusb(hub);
-
/* Only USB 3.0 devices are connected to SuperSpeed hubs. */
-
//如果hub支持超速,则设置usb设备的速度为超速,否则设置usb设备速度 为unknow
-
if (hub_is_superspeed(hub->hdev))
-
udev->speed = USB_SPEED_SUPER;
-
else
-
udev->speed = USB_SPEED_UNKNOWN;
-
choose_devnum(udev); //从usb 总线中找到一个usb地址
-
if (udev->devnum <= 0) {
-
status = -ENOTCONN; /* Don't retry */
-
goto loop;
-
}
-
/* reset (non-USB 3.0 devices) and get descriptor */
-
status = hub_port_init(hub, udev, port1, i);
-
if (status < 0)
-
goto loop;
-
usb_detect_quirks(udev);
-
if (udev->quirks & USB_QUIRK_DELAY_INIT)
-
msleep(1000);
-
/* consecutive bus-powered hubs aren't reliable; they can
-
* violate the voltage drop budget. if the new child has
-
* a "powered" LED, users should notice we didn't enable it
-
* (without reading syslog), even without per-port LEDs
-
* on the parent.
-
*/
-
//如果当前的usb设备是一个hub,它由hub供电
-
if (udev->descriptor.bDeviceClass == USB_CLASS_HUB
-
&& udev->bus_mA <= unit_load) {
-
u16 devstat;
-
status = usb_get_status(udev, USB_RECIP_DEVICE, 0,
-
&devstat);
-
if (status < 2) {
-
dev_dbg(&udev->dev, "get status %d ?\n", status);
-
goto loop_disable;
-
}
-
le16_to_cpus(&devstat);
-
if ((devstat & (1 << USB_DEVICE_SELF_POWERED)) == 0) {
-
dev_err(&udev->dev,
-
"can't connect bus-powered hub "
-
"to this port\n");
-
if (hub->has_indicators) {
-
hub->indicator[port1-1] =
-
INDICATOR_AMBER_BLINK;
-
schedule_delayed_work (&hub->leds, 0);
-
}
-
status = -ENOTCONN; /* Don't retry */
-
goto loop_disable;
-
}
-
}
-
/* check for devices running slower than they could */
-
//如果usb设备支持高速运行,而现在却工作在全速,它就会通过点亮绿色指示灯来指示这种错误
-
if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
-
&& udev->speed == USB_SPEED_FULL
-
&& highspeed_hubs != 0)
-
check_highspeed (hub, udev, port1);
-
/* Store the parent's children[] pointer. At this point
-
* udev becomes globally accessible, although presumably
-
* no one will look at it until hdev is unlocked.
-
*/
-
status = 0;
-
/* We mustn't add new devices if the parent hub has
-
* been disconnected; we would race with the
-
* recursively_mark_NOTATTACHED() routine.
-
*/
-
spin_lock_irq(&device_state_lock);
-
if (hdev->state == USB_STATE_NOTATTACHED)
-
status = -ENOTCONN;
-
else
-
hub->ports[port1 - 1]->child = udev;
-
spin_unlock_irq(&device_state_lock);
-
/* Run it through the hoops (find a driver, etc) */
-
if (!status) {
-
status = usb_new_device(udev);//通过usb_new_device去获取usb设备的配置信息,将它注册到系统中
-
if (status) {
-
spin_lock_irq(&device_state_lock);
-
hub->ports[port1 - 1]->child = NULL;
-
spin_unlock_irq(&device_state_lock);
-
}
-
}
-
if (status)
-
goto loop_disable;
-
status = hub_power_remaining(hub);
-
if (status)
-
dev_dbg(hub_dev, "%dmA power budget left\n", status);
-
return;
-
loop_disable:
-
hub_port_disable(hub, port1, 1);
-
loop:
-
usb_ep0_reinit(udev);
-
release_devnum(udev);
-
hub_free_dev(udev);
-
usb_put_dev(udev);
-
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
-
break;
-
}
-
if (hub->hdev->parent ||
-
!hcd->driver->port_handed_over ||
-
!(hcd->driver->port_handed_over)(hcd, port1)) {
-
if (status != -ENOTCONN && status != -ENODEV)
-
dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
-
port1);
-
}
-
done:
-
hub_port_disable(hub, port1, 1);
-
if (hcd->driver->relinquish_port && !hub->hdev->parent)
-
hcd->driver->relinquish_port(hcd, port1);
-
}
hub_port_connect_change
分为两部分:
第一部分主要是:确认是否有新设备插入;
第二部分主要是:确认port口上有新设备插入后,分配设备资源,并进行枚举操作;
对于一些已经连接的设备,进行一些类似防抖动处理,处理机制:每隔25ms去读取port的status和change状态,查看port连接状态是否稳定,如果port处于稳定状态大于100ms,则认为当前port上的设备已经稳定,这种处理机制最长时间为1.5s;如果port处于稳定状态的时间小于100ms,则认为连接是不稳定的,则跳出当前port,去执行下一个port;
对于一个新插入的设备,并已经确定它的存在,则接下来会为这个usb设备进行枚举
一条usb总线下总共可以有128个设备,usb总线通过位图的形式来管理usb设备的地址,choose_devnum
是从usb 总线中找到一个usb地址,usb地址在1和128之间;
通过hub_port_init
对usb设备进行reset,并设置usb设备的地址,获取usb设备的设备描述符,这个函数相对比较重要,所以对它进行了进一步分析:
-
/* Reset device, (re)assign address, get device descriptor.
-
* Device connection must be stable, no more debouncing needed.
-
* Returns device in USB_STATE_ADDRESS, except on error.
-
*
-
* If this is called for an already-existing device (as part of
-
* usb_reset_and_verify_device), the caller must own the device lock. For a
-
* newly detected device that is not accessible through any global
-
* pointers, it's not necessary to lock the device.
-
*/
-
static int
-
hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
-
int retry_counter)
-
{
-
static DEFINE_MUTEX(usb_address0_mutex);
-
struct usb_device *hdev = hub->hdev;
-
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
-
int i, j, retval;
-
unsigned delay = HUB_SHORT_RESET_TIME;
-
enum usb_device_speed oldspeed = udev->speed;
-
const char *speed;
-
int devnum = udev->devnum;
-
/* root hub ports have a slightly longer reset period
-
* (from USB 2.0 spec, section 7.1.7.5)
-
* 设置用于读取port状态的时间间隔,root hub需要50ms,普通的hub需要10ms,
-
* 对于低速的usb设备需要200ms
-
*/
-
if (!hdev->parent) {
-
delay = HUB_ROOT_RESET_TIME;
-
if (port1 == hdev->bus->otg_port)
-
hdev->bus->b_hnp_enable = 0;
-
}
-
/* Some low speed devices have problems with the quick delay, so */
-
/* be a bit pessimistic with those devices. RHbug #23670 */
-
if (oldspeed == USB_SPEED_LOW)
-
delay = HUB_LONG_RESET_TIME;
-
mutex_lock(&usb_address0_mutex);
-
/* Reset the device; full speed may morph to high speed */
-
/* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */
-
//通过hub_port_reset来reset设备
-
retval = hub_port_reset(hub, port1, udev, delay, false);
-
if (retval < 0) /* error or disconnect */
-
goto fail;
-
/* success, speed is known */
-
retval = -ENODEV;
-
if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) {
-
dev_dbg(&udev->dev, "device reset changed speed!\n");
-
goto fail;
-
}
-
oldspeed = udev->speed;
-
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
-
* it's fixed size except for full speed devices.
-
* For Wireless USB devices, ep0 max packet is always 512 (tho
-
* reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
-
* 根据usb设备的速度来初始化endpont0的最大发送数据长度,
-
* 对于无线usb其maxpacketsize为512字节,对于高速和全速为64字节,而低速为8个字节
-
*/
-
switch (udev->speed) {
-
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 */
-
/* to determine the ep0 maxpacket size, try to read
-
* the device descriptor to get bMaxPacketSize0 and
-
* then correct our initial guess.
-
*/
-
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;
-
default:
-
goto fail;
-
}
-
if (udev->speed == USB_SPEED_WIRELESS)
-
speed = "variable speed Wireless";
-
else
-
speed = usb_speed_string(udev->speed);
-
if (udev->speed != USB_SPEED_SUPER)
-
dev_info(&udev->dev,
-
"%s %s USB device number %d using %s\n",
-
(udev->config) ? "reset" : "new", speed,
-
devnum, udev->bus->controller->driver->name);
-
/* Set up TT records, if needed */
-
//如果hub为高速,而usb设备为低速或全速,则在它们之间需要有一个速度转换设备tt
-
if (hdev->tt) {
-
udev->tt = hdev->tt;
-
udev->ttport = hdev->ttport;
-
} else if (udev->speed != USB_SPEED_HIGH
-
&& hdev->speed == USB_SPEED_HIGH) {
-
if (!hub->tt.hub) {
-
dev_err(&udev->dev, "parent hub has no TT\n");
-
retval = -EINVAL;
-
goto fail;
-
}
-
udev->tt = &hub->tt;
-
udev->ttport = port1;
-
}
-
/* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
-
* Because device hardware and firmware is sometimes buggy in
-
* this area, and this is how Linux has done it for ages.
-
* Change it cautiously.
-
*
-
* NOTE: If USE_NEW_SCHEME() is true we will start by issuing
-
* a 64-byte GET_DESCRIPTOR request. This is what Windows does,
-
* so it may help with some non-standards-compliant devices.
-
* Otherwise we start with SET_ADDRESS and then try to read the
-
* first 8 bytes of the device descriptor to get the ep0 maxpacket
-
* value.
-
*/
-
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
-
if (USE_NEW_SCHEME(retry_counter) &&
-
!(hcd->driver->flags & HCD_USB3) &&
-
!((hcd->driver->flags & HCD_RT_OLD_ENUM) &&
-
!hdev->parent)) {
-
struct usb_device_descriptor *buf;
-
ushort idvendor;
-
int r = 0;
-
#define GET_DESCRIPTOR_BUFSIZE 64
-
buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
-
if (!buf) {
-
retval = -ENOMEM;
-
continue;
-
}
-
/* Retry on all errors; some devices are flakey.
-
* 255 is for WUSB devices, we actually need to use
-
* 512 (WUSB1.0[4.8.1]).
-
*/
-
for (j = 0; j < 3; ++j) {
-
buf->bMaxPacketSize0 = 0;
-
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);
-
switch (buf->bMaxPacketSize0) {
-
case 8: case 16: case 32: case 64: case 255:
-
if (buf->bDescriptorType ==
-
USB_DT_DEVICE) {
-
r = 0;
-
break;
-
}
-
/* FALL THROUGH */
-
default:
-
if (r == 0)
-
r = -EPROTO;
-
break;
-
}
-
if (r == 0)
-
break;
-
}
-
udev->descriptor.bMaxPacketSize0 =
-
buf->bMaxPacketSize0;
-
idvendor = le16_to_cpu(buf->idVendor);
-
kfree(buf);
-
/*
-
* If it is a HSET Test device, we don't issue a
-
* second reset which results in failure due to
-
* speed change.
-
*/
-
if (idvendor != 0x1a0a) {
-
retval = hub_port_reset(hub, port1, udev,
-
delay, false);
-
if (retval < 0) /* error or disconnect */
-
goto fail;
-
if (oldspeed != udev->speed) {
-
dev_dbg(&udev->dev,
-
"device reset changed speed!\n");
-
retval = -ENODEV;
-
goto fail;
-
}
-
}
-
if (r) {
-
if (r != -ENODEV)
-
dev_err(&udev->dev, "device descriptor read/64, error %d\n",
-
r);
-
retval = -EMSGSIZE;
-
continue;
-
}
-
#undef GET_DESCRIPTOR_BUFSIZE
-
}
-
/*
-
* If device is WUSB, we already assigned an
-
* unauthorized address in the Connect Ack sequence;
-
* authorization will assign the final address.
-
*/
-
if (udev->wusb == 0) {
-
for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
-
retval = hub_set_address(udev, devnum);
-
if (retval >= 0)
-
break;
-
msleep(200);
-
}
-
if (retval < 0) {
-
if (retval != -ENODEV)
-
dev_err(&udev->dev, "device not accepting address %d, error %d\n",
-
devnum, retval);
-
goto fail;
-
}
-
if (udev->speed == USB_SPEED_SUPER) {
-
devnum = udev->devnum;
-
dev_info(&udev->dev,
-
"%s SuperSpeed USB device number %d using %s\n",
-
(udev->config) ? "reset" : "new",
-
devnum, udev->bus->controller->driver->name);
-
}
-
/* cope with hardware quirkiness:
-
* - let SET_ADDRESS settle, some device hardware wants it
-
* - read ep0 maxpacket even for high and low speed,
-
*/
-
msleep(10);
-
if (USE_NEW_SCHEME(retry_counter) &&
-
!(hcd->driver->flags & HCD_USB3) &&
-
!((hcd->driver->flags & HCD_RT_OLD_ENUM) &&
-
!hdev->parent))
-
break;
-
}
-
retval = usb_get_device_descriptor(udev, 8);
-
if (retval < 8) {
-
if (retval != -ENODEV)
-
dev_err(&udev->dev,
-
"device descriptor read/8, error %d\n",
-
retval);
-
if (retval >= 0)
-
retval = -EMSGSIZE;
-
} else {
-
retval = 0;
-
break;
-
}
-
}
-
if (retval)
-
goto fail;
-
if (hcd->phy && !hdev->parent)
-
usb_phy_notify_connect(hcd->phy, udev->speed);
-
/*
-
* Some superspeed devices have finished the link training process
-
* and attached to a superspeed hub port, but the device descriptor
-
* got from those devices show they aren't superspeed devices. Warm
-
* reset the port attached by the devices can fix them.
-
*/
-
if ((udev->speed == USB_SPEED_SUPER) &&
-
(le16_to_cpu(udev->descriptor.bcdUSB) < 0x0300)) {
-
dev_err(&udev->dev, "got a wrong device descriptor, "
-
"warm reset device\n");
-
hub_port_reset(hub, port1, udev,
-
HUB_BH_RESET_TIME, true);
-
retval = -EINVAL;
-
goto fail;
-
}
-
if (udev->descriptor.bMaxPacketSize0 == 0xff ||
-
udev->speed == USB_SPEED_SUPER)
-
i = 512;
-
else
-
i = udev->descriptor.bMaxPacketSize0;
-
if (usb_endpoint_maxp(&udev->ep0.desc) != i) {
-
if (udev->speed == USB_SPEED_LOW ||
-
!(i == 8 || i == 16 || i == 32 || i == 64)) {
-
dev_err(&udev->dev, "Invalid ep0 maxpacket: %d\n", i);
-
retval = -EMSGSIZE;
-
goto fail;
-
}
-
if (udev->speed == USB_SPEED_FULL)
-
dev_dbg(&udev->dev, "ep0 maxpacket = %d\n", i);
-
else
-
dev_warn(&udev->dev, "Using ep0 maxpacket: %d\n", i);
-
udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
-
usb_ep0_reinit(udev);
-
}
-
//根据maxpacketsize去获取完整的设备描述符
-
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
-
if (retval < (signed)sizeof(udev->descriptor)) {
-
if (retval != -ENODEV)
-
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
-
retval);
-
if (retval >= 0)
-
retval = -ENOMSG;
-
goto fail;
-
}
-
if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201) {
-
retval = usb_get_bos_descriptor(udev);
-
if (!retval) {
-
udev->lpm_capable = usb_device_supports_lpm(udev);
-
usb_set_lpm_parameters(udev);
-
}
-
}
-
retval = 0;
-
/* notify HCD that we have a device connected and addressed */
-
if (hcd->driver->update_device)
-
hcd->driver->update_device(hcd, udev);
-
fail:
-
if (retval) {
-
hub_port_disable(hub, port1, 0);
-
update_devnum(udev, devnum); /* for disconnect processing */
-
}
-
mutex_unlock(&usb_address0_mutex);
-
return retval;
-
}
hub_port_init
首先对port进行reset,复位成功后设备usb设备的速度和端口0最大发送数据长度,设置usb设备的地址,并获取usb设备的设备描述符;
通过hub_port_reset
来reset设备,reset机制为:通过set_port_feature
来传输USB_PORT_FEAT_RESET
指令,设置成功后循环延时由之前确定的时间间隔后去读取port的status和change状态,要确保usb设备在reset后还能正常存在,如reset还能正常,则通过port的status状态位来确定usb设备的速度,循环延时总时间为500ms,而usb设备reset次数为5次,
根据usb设备的速度来初始化endpont0的最大发送数据长度,对于无线usb其maxpacketsize为512字节,对于高速和全速为64字节,而低速为8个字节;
如果hub为高速,而usb设备为低速或全速,则在它们之间需要有一个速度转换设备tt;
通过获取usb设备的设备描述符来得到usb设备的endpoint0的最大发送数据长度,设备描述符实际 上有18个字节,而endpoint0的maxpacketsize在设备描述符里的第8个字节位,所以只要获取设备描述符的前8个字节数据就可以了,但有些USB设备它并不支持只获取8个字节这样不完整的usb请求,为解决这种问题usb驱动里采用了两种策略去获取maxpacketsize,一种是windows作法,它直接向usb设备请求64字节数据,对于一些maxpacketsize为32或64的,它可以直接一次性把18个字节的设备描述符传输回来,而对于像low speed的设备它的maxpacketsize只有8个字节,就需要进行多次发送; 而Linux作法是先设置usb设备的地址,然后再发送8个字节数据请求,从返回的8个字节里获取endpoint0的maxpacketsize;
-
/**
-
* usb_new_device - perform initial device setup (usbcore-internal)
-
* @udev: newly addressed device (in ADDRESS state)
-
*
-
* This is called with devices which have been detected but not fully
-
* enumerated. The device descriptor is available, but not descriptors
-
* for any device configuration. The caller must have locked either
-
* the parent hub (if udev is a normal device) or else the
-
* usb_bus_list_lock (if udev is a root hub). The parent's pointer to
-
* udev has already been installed, but udev is not yet visible through
-
* sysfs or other filesystem code.
-
*
-
* It will return if the device is configured properly or not. Zero if
-
* the interface was registered with the driver core; else a negative
-
* errno value.
-
*
-
* This call is synchronous, and may not be used in an interrupt context.
-
*
-
* Only the hub driver or root-hub registrar should ever call this.
-
*/
-
int usb_new_device(struct usb_device *udev)
-
{
-
int err;
-
if (udev->parent) {
-
/* Initialize non-root-hub device wakeup to disabled;
-
* device (un)configuration controls wakeup capable
-
* sysfs power/wakeup controls wakeup enabled/disabled
-
*/
-
device_init_wakeup(&udev->dev, 0);
-
}
-
/* Tell the runtime-PM framework the device is active */
-
pm_runtime_set_active(&udev->dev);
-
pm_runtime_get_noresume(&udev->dev);
-
pm_runtime_use_autosuspend(&udev->dev);
-
pm_runtime_enable(&udev->dev);
-
/* By default, forbid autosuspend for all devices. It will be
-
* allowed for hubs during binding.
-
*/
-
usb_disable_autosuspend(udev);
-
err = usb_enumerate_device(udev); /* Read descriptors */
-
if (err < 0)
-
goto fail;
-
dev_dbg(&udev->dev, "udev %d, busnum %d, minor = %d\n",
-
udev->devnum, udev->bus->busnum,
-
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
-
/* export the usbdev device-node for libusb */
-
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
-
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
-
/* Tell the world! */
-
announce_device(udev);
-
if (udev->serial)
-
add_device_randomness(udev->serial, strlen(udev->serial));
-
if (udev->product)
-
add_device_randomness(udev->product, strlen(udev->product));
-
if (udev->manufacturer)
-
add_device_randomness(udev->manufacturer,
-
strlen(udev->manufacturer));
-
device_enable_async_suspend(&udev->dev);
-
/*
-
* check whether the hub marks this port as non-removable. Do it
-
* now so that platform-specific data can override it in
-
* device_add()
-
*/
-
if (udev->parent)
-
set_usb_port_removable(udev);
-
/* Register the device. The device driver is responsible
-
* for configuring the device and invoking the add-device
-
* notifier chain (used by usbfs and possibly others).
-
*/
-
err = device_add(&udev->dev);
-
if (err) {
-
dev_err(&udev->dev, "can't device_add, error %d\n", err);
-
goto fail;
-
}
-
/* Create link files between child device and usb port device. */
-
if (udev->parent) {
-
struct usb_hub *hub = usb_hub_to_struct_hub(udev->parent);
-
struct usb_port *port_dev;
-
if (!hub)
-
goto fail;
-
port_dev = hub->ports[udev->portnum - 1];
-
if (!port_dev)
-
goto fail;
-
err = sysfs_create_link(&udev->dev.kobj,
-
&port_dev->dev.kobj, "port");
-
if (err)
-
goto fail;
-
err = sysfs_create_link(&port_dev->dev.kobj,
-
&udev->dev.kobj, "device");
-
if (err) {
-
sysfs_remove_link(&udev->dev.kobj, "port");
-
goto fail;
-
}
-
pm_runtime_get_sync(&port_dev->dev);
-
}
-
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
-
usb_mark_last_busy(udev);
-
pm_runtime_put_sync_autosuspend(&udev->dev);
-
return err;
-
fail:
-
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
-
pm_runtime_disable(&udev->dev);
-
pm_runtime_set_suspended(&udev->dev);
-
return err;
-
}
usb_new_device
主是要获取usb设备的配置信息,然后将usb设备添加到系统中,这里usb_enumerate_device
相对比较重要,就是它完成了配置信息获取及分配;
-
/**
-
* usb_enumerate_device - Read device configs/intfs/otg (usbcore-internal)
-
* @udev: newly addressed device (in ADDRESS state)
-
*
-
* This is only called by usb_new_device() and usb_authorize_device()
-
* and FIXME -- all comments that apply to them apply here wrt to
-
* environment.
-
*
-
* If the device is WUSB and not authorized, we don't attempt to read
-
* the string descriptors, as they will be errored out by the device
-
* until it has been authorized.
-
*/
-
static int usb_enumerate_device(struct usb_device *udev)
-
{
-
int err;
-
if (udev->config == NULL) {
-
err = usb_get_configuration(udev);
-
if (err < 0) {
-
if (err != -ENODEV)
-
dev_err(&udev->dev, "can't read configurations, error %d\n",
-
err);
-
return err;
-
}
-
}
-
/* read the standard strings and cache them if present */
-
udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
-
udev->manufacturer = usb_cache_string(udev,
-
udev->descriptor.iManufacturer);
-
udev->serial = usb_cache_string(udev, udev->descriptor.iSerialNumber);
-
err = usb_enumerate_device_otg(udev);
-
if (err < 0)
-
return err;
-
usb_detect_interface_quirks(udev);
-
return 0;
-
}
通过usb_get_configuration
获取和分配usb设备的配置,接口,端口信息
-
/*
-
* Get the USB config descriptors, cache and parse'em
-
*
-
* hub-only!! ... and only in reset path, or usb_new_device()
-
* (used by real hubs and virtual root hubs)
-
*/
-
int usb_get_configuration(struct usb_device *dev)
-
{
-
struct device *ddev = &dev->dev;
-
int ncfg = dev->descriptor.bNumConfigurations;
-
int result = 0;
-
unsigned int cfgno, length;
-
unsigned char *bigbuffer;
-
struct usb_config_descriptor *desc;
-
cfgno = 0;
-
result = -ENOMEM;
-
//如果usb设备的配置个数大于最大允许配置数8,则发出警告,并把设备的配置个数改成最大允许配置数8
-
if (ncfg > USB_MAXCONFIG) {
-
dev_warn(ddev, "too many configurations: %d, "
-
"using maximum allowed: %d\n", ncfg, USB_MAXCONFIG);
-
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
-
}
-
//如果usb设备没有配置,则不允许,直接返回错误信息
-
if (ncfg < 1) {
-
dev_err(ddev, "no configurations\n");
-
return -EINVAL;
-
}
-
//根据配置数,申请usb配置结构usb_host_config,并为用于存放配置结构的指针数据申请空间
-
length = ncfg * sizeof(struct usb_host_config);
-
dev->config = kzalloc(length, GFP_KERNEL);
-
if (!dev->config)
-
goto err2;
-
length = ncfg * sizeof(char *);
-
dev->rawdescriptors = kzalloc(length, GFP_KERNEL);
-
if (!dev->rawdescriptors)
-
goto err2;
-
//申请用于暂时存放配置描述符的结构空间
-
desc = kmalloc(USB_DT_CONFIG_SIZE, GFP_KERNEL);
-
if (!desc)
-
goto err2;
-
result = 0;
-
//依次获取usb设备的配置信息
-
for (; cfgno < ncfg; cfgno++) {
-
/* We grab just the first descriptor so we know how long
-
* the whole configuration is */
-
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
-
desc, USB_DT_CONFIG_SIZE);
-
if (result < 0) {
-
dev_err(ddev, "unable to read config index %d "
-
"descriptor/%s: %d\n", cfgno, "start", result);
-
if (result != -EPIPE)
-
goto err;
-
dev_err(ddev, "chopping to %d config(s)\n", cfgno);
-
dev->descriptor.bNumConfigurations = cfgno;
-
break;
-
} else if (result < 4) {
-
dev_err(ddev, "config index %d descriptor too short "
-
"(expected %i, got %i)\n", cfgno,
-
USB_DT_CONFIG_SIZE, result);
-
result = -EINVAL;
-
goto err;
-
}
-
length = max((int) le16_to_cpu(desc->wTotalLength),
-
USB_DT_CONFIG_SIZE);//得到wTotallLength
-
/* Now that we know the length, get the whole thing */
-
//根据得到的wTotallLength,申请用于存放这些信息的内存空间
-
bigbuffer = kmalloc(length, GFP_KERNEL);
-
if (!bigbuffer) {
-
result = -ENOMEM;
-
goto err;
-
}
-
if (dev->quirks & USB_QUIRK_DELAY_INIT)
-
msleep(100);
-
//根据得到的wTotallLength,去获取完整的配置,接口,端口等描述符信息,把这些信息存放到bigbuffer里
-
result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno,
-
bigbuffer, length);
-
if (result < 0) {
-
dev_err(ddev, "unable to read config index %d "
-
"descriptor/%s\n", cfgno, "all");
-
kfree(bigbuffer);
-
goto err;
-
}
-
if (result < length) {
-
dev_warn(ddev, "config index %d descriptor too short "
-
"(expected %i, got %i)\n", cfgno, length, result);
-
length = result;
-
}
-
//把用于存放配置等信息的地址保存在之前申请的rawdescriptors里
-
dev->rawdescriptors[cfgno] = bigbuffer;
-
//通过usb_parse_configuration函数,把获得的配置等信息按照类型进行分类
-
result = usb_parse_configuration(dev, cfgno,
-
&dev->config[cfgno], bigbuffer, length);
-
if (result < 0) {
-
++cfgno;
-
goto err;
-
}
-
}
-
result = 0;
-
err:
-
kfree(desc);
-
dev->descriptor.bNumConfigurations = cfgno;
-
err2:
-
if (result == -ENOMEM)
-
dev_err(ddev, "out of memory\n");
-
return result;
-
}
这个函数主要分成二部分,第一部分是向usb device侧获取设备的配置,接口,端口信息。首先,它为这些信息申请存放空间,然后像之前获取设备描述符一样,先发送一个9 个字节的请求,用来获取配置,接口,端口等描述符的总长度,最后根据得到的总长度去得到完成 的设备配置,接口,端口信息;第二部分是把获取到的这个信息按照类别分别进行分类,并存到相应的结构中;
通过usb_parse_configuration
函数,把获得的配置等信息按照类型进行分类
-
static int usb_parse_configuration(struct usb_device *dev, int cfgidx,
-
struct usb_host_config *config, unsigned char *buffer, int size)
-
{
-
struct device *ddev = &dev->dev;
-
unsigned char *buffer0 = buffer;
-
int cfgno;
-
int nintf, nintf_orig;
-
int i, j, n;
-
struct usb_interface_cache *intfc;
-
unsigned char *buffer2;
-
int size2;
-
struct usb_descriptor_header *header;
-
int len, retval;
-
u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES];
-
unsigned iad_num = 0;
-
memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE);
-
if (config->desc.bDescriptorType != USB_DT_CONFIG ||
-
config->desc.bLength < USB_DT_CONFIG_SIZE ||
-
config->desc.bLength > size) {
-
dev_err(ddev, "invalid descriptor for config index %d: "
-
"type = 0x%X, length = %d\n", cfgidx,
-
config->desc.bDescriptorType, config->desc.bLength);
-
return -EINVAL;
-
}
-
cfgno = config->desc.bConfigurationValue;
-
buffer += config->desc.bLength;
-
size -= config->desc.bLength;
-
nintf = nintf_orig = config->desc.bNumInterfaces;
-
if (nintf > USB_MAXINTERFACES) {
-
dev_warn(ddev, "config %d has too many interfaces: %d, "
-
"using maximum allowed: %d\n",
-
cfgno, nintf, USB_MAXINTERFACES);
-
nintf = USB_MAXINTERFACES;
-
}
-
/* Go through the descriptors, checking their length and counting the
-
* number of altsettings for each interface */
-
n = 0;
-
for ((buffer2 = buffer, size2 = size);
-
size2 > 0;
-
(buffer2 += header->bLength, size2 -= header->bLength)) {
-
if (size2 < sizeof(struct usb_descriptor_header)) {
-
dev_warn(ddev, "config %d descriptor has %d excess "
-
"byte%s, ignoring\n",
-
cfgno, size2, plural(size2));
-
break;
-
}
-
header = (struct usb_descriptor_header *) buffer2;
-
if ((header->bLength > size2) || (header->bLength < 2)) {
-
dev_warn(ddev, "config %d has an invalid descriptor "
-
"of length %d, skipping remainder of the config\n",
-
cfgno, header->bLength);
-
break;
-
}
-
if (header->bDescriptorType == USB_DT_INTERFACE) {
-
struct usb_interface_descriptor *d;
-
int inum;
-
d = (struct usb_interface_descriptor *) header;
-
if (d->bLength < USB_DT_INTERFACE_SIZE) {
-
dev_warn(ddev, "config %d has an invalid "
-
"interface descriptor of length %d, "
-
"skipping\n", cfgno, d->bLength);
-
continue;
-
}
-
inum = d->bInterfaceNumber;
-
if ((dev->quirks & USB_QUIRK_HONOR_BNUMINTERFACES) &&
-
n >= nintf_orig) {
-
dev_warn(ddev, "config %d has more interface "
-
"descriptors, than it declares in "
-
"bNumInterfaces, ignoring interface "
-
"number: %d\n", cfgno, inum);
-
continue;
-
}
-
if (inum >= nintf_orig)
-
dev_warn(ddev, "config %d has an invalid "
-
"interface number: %d but max is %d\n",
-
cfgno, inum, nintf_orig - 1);
-
/* Have we already encountered this interface?
-
* Count its altsettings */
-
for (i = 0; i < n; ++i) {
-
if (inums[i] == inum)
-
break;
-
}
-
if (i < n) {
-
if (nalts[i] < 255)
-
++nalts[i];
-
} else if (n < USB_MAXINTERFACES) {
-
inums[n] = inum;
-
nalts[n] = 1;
-
++n;
-
}
-
} else if (header->bDescriptorType ==
-
USB_DT_INTERFACE_ASSOCIATION) {
-
if (iad_num == USB_MAXIADS) {
-
dev_warn(ddev, "found more Interface "
-
"Association Descriptors "
-
"than allocated for in "
-
"configuration %d\n", cfgno);
-
} else {
-
config->intf_assoc[iad_num] =
-
(struct usb_interface_assoc_descriptor
-
*)header;
-
iad_num++;
-
}
-
} else if (header->bDescriptorType == USB_DT_DEVICE ||
-
header->bDescriptorType == USB_DT_CONFIG)
-
dev_warn(ddev, "config %d contains an unexpected "
-
"descriptor of type 0x%X, skipping\n",
-
cfgno, header->bDescriptorType);
-
} /* for ((buffer2 = buffer, size2 = size); ...) */
-
size = buffer2 - buffer;
-
config->desc.wTotalLength = cpu_to_le16(buffer2 - buffer0);
-
if (n != nintf)
-
dev_warn(ddev, "config %d has %d interface%s, different from "
-
"the descriptor's value: %d\n",
-
cfgno, n, plural(n), nintf_orig);
-
else if (n == 0)
-
dev_warn(ddev, "config %d has no interfaces?\n", cfgno);
-
config->desc.bNumInterfaces = nintf = n;
-
/* Check for missing interface numbers */
-
for (i = 0; i < nintf; ++i) {
-
for (j = 0; j < nintf; ++j) {
-
if (inums[j] == i)
-
break;
-
}
-
if (j >= nintf)
-
dev_warn(ddev, "config %d has no interface number "
-
"%d\n", cfgno, i);
-
}
-
/* Allocate the usb_interface_caches and altsetting arrays */
-
for (i = 0; i < nintf; ++i) {
-
j = nalts[i];
-
if (j > USB_MAXALTSETTING) {
-
dev_warn(ddev, "too many alternate settings for "
-
"config %d interface %d: %d, "
-
"using maximum allowed: %d\n",
-
cfgno, inums[i], j, USB_MAXALTSETTING);
-
nalts[i] = j = USB_MAXALTSETTING;
-
}
-
len = sizeof(*intfc) + sizeof(struct usb_host_interface) * j;
-
config->intf_cache[i] = intfc = kzalloc(len, GFP_KERNEL);
-
if (!intfc)
-
return -ENOMEM;
-
kref_init(&intfc->ref);
-
}
-
/* FIXME: parse the BOS descriptor */
-
/* Skip over any Class Specific or Vendor Specific descriptors;
-
* find the first interface descriptor */
-
config->extra = buffer;
-
i = find_next_descriptor(buffer, size, USB_DT_INTERFACE,
-
USB_DT_INTERFACE, &n);
-
config->extralen = i;
-
if (n > 0)
-
dev_dbg(ddev, "skipped %d descriptor%s after %s\n",
-
n, plural(n), "configuration");
-
buffer += i;
-
size -= i;
-
/* Parse all the interface/altsetting descriptors */
-
while (size > 0) {
-
retval = usb_parse_interface(ddev, cfgno, config,
-
buffer, size, inums, nalts);
-
if (retval < 0)
-
return retval;
-
buffer += retval;
-
size -= retval;
-
}
-
/* Check for missing altsettings */
-
for (i = 0; i < nintf; ++i) {
-
intfc = config->intf_cache[i];
-
for (j = 0; j < intfc->num_altsetting; ++j) {
-
for (n = 0; n < intfc->num_altsetting; ++n) {
-
if (intfc->altsetting[n].desc.
-
bAlternateSetting == j)
-
break;
-
}
-
if (n >= intfc->num_altsetting)
-
dev_warn(ddev, "config %d interface %d has no "
-
"altsetting %d\n", cfgno, inums[i], j);
-
}
-
}
-
return 0;
-
}