hub_port_connect()函数
断开该端口设备的连接:如果是root hub,挂接在控制器上的,则断开该端口下的设备。
if (udev) {
if (hcd->usb_phy && !hdev->parent)
usb_phy_notify_disconnect(hcd->usb_phy, udev->speed);
usb_disconnect(&port_dev->child);
}
处理的是hub上有设备断开的情况。
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 &&
port1 != unreliable_port &&
printk_ratelimit())
dev_err(&port_dev->dev, "connect-debounce failed\n");
portstatus &= ~USB_PORT_STAT_CONNECTION;
unreliable_port = port1;
} else {
portstatus = status;
}
}
hub port 状态相关的宏定义如下:
/*
* wPortStatus bit field
* See USB 2.0 spec Table 11-21
*/
#define USB_PORT_STAT_CONNECTION 0x0001
#define USB_PORT_STAT_ENABLE 0x0002
#define USB_PORT_STAT_SUSPEND 0x0004
#define USB_PORT_STAT_OVERCURRENT 0x0008
#define USB_PORT_STAT_RESET 0x0010
#define USB_PORT_STAT_L1 0x0020
/* bits 6 to 7 are reserved */
#define USB_PORT_STAT_POWER 0x0100
#define USB_PORT_STAT_LOW_SPEED 0x0200
#define USB_PORT_STAT_HIGH_SPEED 0x0400
#define USB_PORT_STAT_TEST 0x0800
#define USB_PORT_STAT_INDICATOR 0x1000
/* bits 13 to 15 are reserved */
对应usb2.0 协议 Hub端口状态标志位如协议中的 表11-21
由上表可知道:
bit0表示连接状态,1 是有设备连接,0 是没有设备连接。bit1表示端口被使能还是禁止工作,1 是被使能,0 是禁止工作状态。bit 2表示挂起状态,0 是没有被挂起,1 是被挂起。bit3 表示端口过电流,0 是没有过电流,1 是发生了过电流。bit4 表示是否重启了,0没有收到重启命令,1 是收到了重启命令。bit5~bit7 未使用。bit8 表示端口供电情况,1 是处于power off 状态,0 是出于power on状态。bit9 表示是设备的速度类型,0 是全速或者高速设备接入该端口,1 是低速设备接入。bit10 表示设备速度类型,0 表示全速设备接入,1是高速设备接入。bit11 表示该端口是否处于测试模式,0 不是,1 处于测试模式。bit12 该端口指示灯控制,0是默认颜色,1是软件控制的颜色。bit13~bit15 未启用。
要读懂上面的程序,除了知道Hub 端口的状态标志位的含义还不行,还得知道Hub 端口发生改变的标志位,摘自协议中的表11-22
bit0 表示状态发生了改变,是什么改变,不知道,也许是断开,也许是设备接入,1 表示改变了,0是没有改变,结合协议中的表11-21中的bit0判定是设备接入还是断开。bit1 表示端口使能/禁用的改变,当端口由于错误引起禁用时被置1。bit2 挂起状态的改变,该位是以host的视角来看设备的状态,0 是没有改变,1是从挂起恢复了。bit3 过电流指示,如果过电流,在hub的描述符中表示出来,0 是过电流发生且没有改善,1过电流指示消除。bit4表示重启状态,0 表示没有改变端口的状态,1表示重启完成。其余bit保留。在代码里对应的code为:
/*
* wPortChange bit field
* See USB 2.0 spec Table 11-22 and USB 2.0 LPM ECN Table-4.10
* Bits 0 to 5 shown, bits 6 to 15 are reserved
*/
#define USB_PORT_STAT_C_CONNECTION 0x0001
#define USB_PORT_STAT_C_ENABLE 0x0002
#define USB_PORT_STAT_C_SUSPEND 0x0004
#define USB_PORT_STAT_C_OVERCURRENT 0x0008
#define USB_PORT_STAT_C_RESET 0x0010
#define USB_PORT_STAT_C_L1 0x0020
/*
上面这段代码翻译过来就是:如果有设备断开或者设备连接情况发生了改变,都清除hub的remove bits。如果设备连接情况发生了改变且端口是被使能的,那么先去抖;消除抖动,接入或拔出时,会有物理抖动,导致状态不稳定,所以需要消抖。消抖后如果设备移除了,设置hub feature等清理工作。
根据速度设置hub设备总线电流的阈值
if (hub_is_superspeed(hub->hdev))
unit_load = 150;
else
unit_load = 100;
for (i = 0; i < SET_CONFIG_TRIES; i++) {
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
*/
udev = usb_alloc_dev(hdev, hdev->bus, port1); //分配usb设备内存并初始化bus、type、group、设备在系统中的路径(dev->path)、ep0的属性并设置设备状态为attached。
//这里有一个疑问:设备中端点0是没有描述符的,但是这里有初始化ep0.desc的代码,是软件层面对ep0有单独的描述吗?
if (!udev) {
dev_err(&port_dev->dev,
"couldn't allocate usb_device\n");
goto done;
}
usb_set_device_state(udev, USB_STATE_POWERED); //设置为 power状态,并设置电源等。
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
udev->wusb = hub_is_wusb(hub);
/* Devices connected to SuperSpeed hubs are USB 3.0 or later */
if (hub_is_superspeed(hub->hdev)) //判断速度是否为高速
udev->speed = USB_SPEED_SUPER;
else
udev->speed = USB_SPEED_UNKNOWN;
choose_devnum(udev); //从USB总线的编号数组中找到一个合适的编号,为设备分配地址做好准备。
if (udev->devnum <= 0) {
status = -ENOTCONN; /* Don't retry */
goto loop;
}
/* reset (non-USB 3.0 devices) and get descriptor */
usb_lock_port(port_dev);
status = hub_port_init(hub, udev, port1, i); //初始化设备,设置地址,读取设备描述符
usb_unlock_port(port_dev);
if (status < 0)
goto loop;
if (udev->quirks & USB_QUIRK_DELAY_INIT)
msleep(2000);
/* 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.
*/
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) {
dev_dbg(&udev->dev, "get status %d ?\n", status);
goto loop_disable;
}
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;
queue_delayed_work(
system_power_efficient_wq,
&hub->leds, 0);
}
status = -ENOTCONN; /* Don't retry */
goto loop_disable;
}
}
/* check for devices running slower than they could */
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;
mutex_lock(&usb_port_peer_mutex);
/* 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
port_dev->child = udev;
spin_unlock_irq(&device_state_lock);
mutex_unlock(&usb_port_peer_mutex);
/* Run it through the hoops (find a driver, etc) */
if (!status) {
status = usb_new_device(udev); //读取、解析配置描述符,解析端点描述符
if (status) {
mutex_lock(&usb_port_peer_mutex);
spin_lock_irq(&device_state_lock);
port_dev->child = NULL;
spin_unlock_irq(&device_state_lock);
mutex_unlock(&usb_port_peer_mutex);
} else {
if (hcd->usb_phy && !hdev->parent)
usb_phy_notify_connect(hcd->usb_phy,
udev->speed);
}
}
if (status)
goto loop_disable;
status = hub_power_remaining(hub);
if (status)
dev_dbg(hub->intfdev, "%dmA power budget left\n", status);
printk(KERN_EMERG"pompey hub: %s %d status:%d\n",__func__,__FUNCTION__,status);
return;
printk(KERN_EMERG"pompey hub: %s %d status:%d\n",__func__,__FUNCTION__,status);
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;
/* When halfway through our retry count, power-cycle the port */
if (i == (SET_CONFIG_TRIES / 2) - 1) {
dev_info(&port_dev->dev, "attempt power cycle\n");
usb_hub_set_port_power(hdev, hub, port1, false);
msleep(2 * hub_power_on_good_delay(hub));
usb_hub_set_port_power(hdev, hub, port1, true);
msleep(hub_power_on_good_delay(hub));
}
}
经过一系列的操作,设备就清清楚楚的被host了解了,是什么设备(设备描述符、配置描述符)、有什么功能(接口描述符)、怎么传数据(端点描述符)都明明白白了。
for (i = 0; i < SET_CONFIG_TRIES; i++)
......
这里的目的就是多试几次,以防失败。