USB从入门到精通-2-Linux那些事儿之USB学习与扩展

标签: USB协议 UKey分析


资源来自《Linux那些事儿之USB》,本文主要是读书笔记,附加一些自己的分析和相关知识点说明
内核代码版本2.6.24

目录

4. 设备生命线

4.1. Hub监听处理

插到Hub到拔出。当USB设备连接到Hub某端口上时,Hub检测到有设备连接后,会分配一个struct usb_device结构并初始化,调用设备模型提供的接口将设备添加到USB总线的设备列表中,然后USB总线会遍历驱动列表中的每个驱动,调用自己的match函数看是否和设备或接口匹配。
Hub检测过程如下:

Created with Raphaël 2.1.2 usb_init hub_thread hub_events hub_port_connect_change 设备新增相关操作

其中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 */
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值