标签: USB协议 UKey分析
资源来自《Linux那些事儿之USB》,本文主要是读书笔记,附加一些自己的分析和相关知识点说明
内核代码版本2.6.24
目录
4. 设备生命线
4.1. Hub监听处理
插到Hub到拔出。当USB设备连接到Hub某端口上时,Hub检测到有设备连接后,会分配一个struct usb_device结构并初始化,调用设备模型提供的接口将设备添加到USB总线的设备列表中,然后USB总线会遍历驱动列表中的每个驱动,调用自己的match函数看是否和设备或接口匹配。
Hub检测过程如下:
其中hub_port_connect_change关键代码如下:
for (i = 0; i < SET_CONFIG_TRIES; i++) {
struct usb_device *udev;
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
*/
udev = usb_alloc_dev(hdev, hdev->bus, port1);
//Hub检测到端口有设备连接后,会调用core的usb_alloc_dev函数
//为struct usb_device结构的对象申请内容,详见4.1.1
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);
udev->speed = USB_SPEED_UNKNOWN;
udev->bus_mA = hub->mA_per_port;
udev->level = hdev->level + 1;
//完成struct usb_device结构体创建后,hub会进行后续初始化工作
//将设备状态设置为powered(加电状态)
//将speed暂时设置为USB_SPEED_UNKNOWN,核心就是成员属性设置
//设备level设置为Hub level+1
//为设备能够从Hub那里获得的电流赋值,为了保证通信
/* set the address */
choose_address(udev);
if (udev->devnum <= 0) {
status = -ENOTCONN; /* Don't retry */
goto loop;
}
//Hub会为设备在总线上选择独一无二的地址,设置usb_device->devnum
/* reset and get descriptor */
status = hub_port_init(hub, udev, port1, i);
//复位、设置地址、并获得设备描述符,详见下文
if (status < 0)
goto loop;
/* 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 <= 100) {
//如果设备类型是USB_CLASS_HUB进行的特殊处理,暂不关心
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 */
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
hdev->children[port1-1] = udev;
spin_unlock_irq(&device_state_lock);
/* Run it through the hoops (find a driver, etc) */
if (!status) {
status = usb_new_device(udev);
//核心调用,初始化设备配置,详见4.1.4
if (status) {
spin_lock_irq(&device_state_lock);
hdev->children[port1-1] = 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:
ep0_reinit(udev);
release_address(udev);
usb_put_dev(udev);
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
}
设备要想从Powered状态发展到下一个Default状态,必须收到一个复位信号,并成功复位。Hub会复位设备,成功后,设备进入Default状态。
复位进入Default状态后,Hub也会获得设备真正的速度,依据速度,能过知道端口0一次能够处理的最大数据长度。
下面Hub使用Core中定义的usb_control_msg函数给设备发送SET_ADDRESS请求,设备就进入Address状态了,设备的address就是上表中的Devnum
4.1.1 usb_alloc_dev-USB设备对象创建
下面来分析一下usb_alloc_dev函数的实现
struct usb_device *
usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
//USB设备的构造函数,parent是设备连接的hub,bus是设备连接的总线,port1是设备连接在Hub上的端口
{
struct usb_device *dev;
struct usb_hcd *usb_hcd = container_of(bus, struct usb_hcd, self);
unsigned root_hub = 0;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
//申请struct usb_device空间,kzalloc=kmalloc+memset
if (!dev)
return NULL;
if (!usb_get_hcd(bus_to_hcd(bus))) {
//hcd,主机控制器驱动相关的函数,主机控制器对应一条USB总线,用struct usb_hcd结构表示
//一条总线用struct usb_bus结构表示
//函数bus_to_hcd,获得总线对应的主机控制器驱动,也就是struct usb_hcd结构对象
//函数usb_get_hcd将得到的usb_hcd结构对象的引用计数加1,,因为总线上多了一个设备
kfree(dev);
return NULL;
}
device_initialize(&dev->dev);
//设备模型的函数,目的是将struct usb_device结构中嵌入的struct device结构体初始化掉
dev->dev.bus = &usb_bus_type;
//设置总线类型为usb_bus_type
dev->dev.type = &usb_device_type;
//设置设备类型为usb_device_type
/*除了总线类型,还有设备类型,如下
struct device_type usb_device_type = {
.name = "usb_device",
.release = usb_release_dev,
.uevent = usb_dev_uevent,
};
*/
dev->dev.dma_mask = bus->controller->dma_mask;
//DMA传输设备,是否支持DMA要看主机控制器的dma_mask
set_dev_node(&dev->dev, dev_to_node(bus->controller));
//设置内存节点,numa,uma表示一致内存访问,uniform memory access
dev->state = USB_STATE_ATTACHED;
//将USB设备的状态设置为ATTACHED,表示已经连接到USB接口上
atomic_set(&dev->urbnum, 0);
INIT_LIST_HEAD(&dev->ep0.urb_list);
//初始化ep0端点的urb_list
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
//初始化ep0的描述符长度和描述符类型
/* ep0 maxpacket comes later, from device descriptor */
usb_enable_endpoint(dev, &dev->ep0);
//设置usb_device,ep_out[0]和ep_in[0]
dev->can_submit = 1;
/* Save readable and stable topology id, distinguishing devices
* by location for diagnostics, tools, driver model, etc. The
* string is a path along hub ports, from the root. Each device's
* dev->devpath will be stable until USB is re-cabled, and hubs
* are often labeled with these port numbers. The bus_id isn't
* as stable: bus->busnum changes easily from modprobe order,
* cardbus or pci hotplugging, and so on.
*/
if (unlikely(!parent)) {
//告诉编译器条件x发生的可能性不大,这个条件块里的语句目标码可以放在较远位置,保证经常执行的目标更紧凑
//这里parent为空,表示设备直接连载root hub上
//内核觉得USB设置直接连到root hub上可能性很小
dev->devpath[0] = '0';
dev->dev.parent = bus->controller;
sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
root_hub = 1;
//如果是连接到root hub,那么需要设置的usb_device的属性
//usb_device->devpath="0"
//usb_device->dev(struct device类型).parent(struct device类型)=主机控制器
//usb_device->dev(struct device类型).bus_id[]="usb1"或者usb2\3\4这样的字符串
} else {
/* match any labeling on the hubs; it's one-based */
if (parent->devpath[0] == '0')
snprintf(dev->devpath, sizeof dev->devpath,
"%d", port1);
else
snprintf(dev->devpath, sizeof dev->devpath,
"%s.%d", parent->devpath, port1);
dev->dev.parent = &parent->dev;
sprintf(&dev->dev.bus_id[0], "%d-%s",
bus->busnum, dev->devpath);
//如果不是连接到root hub,那么需要设置的usb_device的属性
//如果所在hub在Root Hub上,那么usb_device->devpath=端口号
//否则usb_device->devpath=所在Hub的devpath基础上加一个.再加端口号
//usb_device->dev(struct device类型).parent(struct device类型)=parent->dev
//usb_device->dev(struct device类型).bus_id[]=(bus->busnum)-(dev->devpath)
/* hub driver sets up TT records */
}
dev->portnum = port1;
dev->bus = bus;
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
//初始化一个链表
//下面是电源管理相关
#ifdef CONFIG_PM
mutex_init(&dev->pm_mutex);
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
#endif
if (root_hub) /* Root hub always ok [and always wired] */
dev->authorized = 1;
else {
dev->authorized = usb_hcd->authorized_default;
dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
}
return dev;
}
4.1.2. hub_port_init-设备复位、设备Address、设备描述符获取
代码如下:
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;
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
char *speed, *type;
int devnum = udev->devnum;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
*/
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 */
retval = hub_port_reset(hub, port1, udev, delay);
//设备复位,得到真正的设备速度(udev->speed),详见下文
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].
*/
//下面依据设备驱动,设置端点0的一次能够处理的最大长度
switch (udev->speed) {
case USB_SPEED_VARIABLE: /* fixed at 512 */
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
break;
case USB_SPEED_HIGH: /* fixed at 64 */
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
break;
case USB_SPEED_FULL: /* 8, 16, 32, or 64 */