天天看點

usb serial

 淺析usb轉serial序列槽裝置在linux核心中枚舉建立及生成tty裝置的全過程

1.usb_register和usb_register_driver用來注冊一個interface接口驅動for_devices = 0;

2.usb_register_device_driver用來注冊一個usb裝置驅動,for_devices = 1;用來解析裝置描述符,

  進而生成配置描述符下的功能接口,嘗試比對usb_register_driver注冊的接口驅動來驅動該usb裝置的功能接口.[luther.gliethttp]

在整個kernel中,隻有usb_init即[subsys_initcall(usb_init)]一處調用了usb_register_device_driver函數,

usb_register_device_driver(&usb_generic_driver, THIS_MODULE);是以所有通過hub_thread檢測到插入的usb裝置也都将調用到

generic_probe裝置枚舉函數,我們這裡需要提到一些usb通信方面的知識,以便我們能夠透徹了解kernel中usb代碼,

一個插入到HUB上的usb裝置使用4種描述符來描述自己,

(1) 裝置描述符

(2) 配置描述符

(3) 接口描述符

(4) 端點描述符

一個usb裝置隻能有1個裝置描述符,1個裝置描述符可以有多個配置描述符,然後每個配置描述符下面又可以有多個接口描述符用來

具體描述一個裝置在該配置下的一個或多個獨立功能,每個接口下面又由端點描述符來具體聲明該功能接口在usb實體通信中使用哪幾個

端點管道來執行usb實體信道的實際收發工作[luther.gliethttp].

是以一個usb裝置的具體功能是由接口描述符來描述的,是以我們開發的usb driver也就幾乎99.9%都在使用usb_register函數來實作一個

接口對應的驅動,進而驅動usb裝置上該接口對應的具體功能,比如UMS.[luther.gliethttp]

當kernel使用generic_probe()函數完成插入到HUB上的usb裝置的合法檢驗之後,将調用設定配置描述符操作usb_set_configuration,

生成該配置描述符下面若幹個接口描述符對應的dev裝置,

usb_set_configuration

==>device_add(&intf->dev);

// 這樣該接口dev将掃描usb_bus_type總線上的所有drivers驅動,kernel嘗試為該接口dev找到驅動它的driver.[luther.gliethttp]

如下幾個函數中都會調用到usb_set_configuration

usb_authorize_device    // 以sysfs中attr性質存在,這樣使用者空間的程式就可以通過attr屬性檔案來強制控制接口驅動的關聯.

usb_deauthorize_device

driver_set_config_work

proc_setconfig

set_bConfigurationValue

generic_disconnect

generic_probe

usb裝置的檢測工作是通過核心線程hub_thread完成的.

usb_hub_init==>khubd_task = kthread_run(hub_thread, NULL, "khubd"); // 建立核心線程hub_thread,監控hub上usb裝置的插拔情況

hub_thread

==>hub_events

==>hub_port_connect_change==>udev =usb_alloc_dev // 添加usb裝置

==>hub_port_connect_change==>usb_new_device(udev)==>device_add(&udev->dev); // 将檢測到的usb裝置添加到usb_bus_type總線上,

// 該dev的type值為usb_device_type,最後函數執行device_add==>bus_add_device實作具體添加操作[luther.gliethttp]

// bus_add_device将調用上面usb_register_device_driver(&usb_generic_driver, THIS_MODULE);注冊的唯一一個裝置描述符解析驅動

// usb_generic_driver==>generic_probe來完成接口裝置生成和相應的接口裝置驅動關關聯作[luther.gliethttp].

usb_add_hcd==>usb_alloc_dev // 添加HCD

usb_alloc_dev

==>dev->dev.bus = &usb_bus_type;設定dev為usb總線上的裝置

==>dev->dev.type = &usb_device_type;設定該dev為usb裝置而非接口

driver_register或者device_register

調用driver_attach或者bus_attach_device==>device_attach

來為裝置嘗試比對驅動或者為驅動嘗試添加裝置,不論是哪一種情況,都将

執行到:driver_probe_device函數.

int driver_probe_device(struct device_driver *drv, struct device *dev)

{

    int ret = 0;

    if (!device_is_registered(dev)) // 1.裝置已經完成了注冊到bus總線工作

        return -ENODEV;

    if (drv->bus->match && !drv->bus->match(dev, drv)) // 2.執行bus提供的match操作usb_device_match

        goto done;

    pr_debug("bus: '%s': %s: matched device %s with driver %s/n",

         drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);

    ret = really_probe(dev, drv); // bus的match通過檢驗,這裡做進一步的probe檢驗,

    // 如果bus提供了probe,那麼執行bus->probe(dev);

    // 否則執行driver提供的probe函數drv->probe(dev);

done:

    return ret;

}

usb_register(&mct_u232_driver);

==>usb_register_driver

    new_driver->drvwrap.for_devices = 0; // 僅僅用來驅動interface接口,是以上面hub_port_connect_change由usb_alloc_dev生成的usb裝置不會調用該usb接口驅動

    new_driver->drvwrap.driver.bus = &usb_bus_type;

    new_driver->drvwrap.driver.probe = usb_probe_interface; // 當檢測到usb裝置插入後,将調用usb_probe_interface進行細緻處理

    // 提供為device_driver提供probe處理函數,因為bus總線usb_bus_type不提供probe操作[luther.gliethttp]

==>driver_register(&new_driver->drvwrap.driver); // 将驅動添加到usb bus總線管理的driver驅動連結清單上

usb_device_match==>is_usb_device

static inline int is_usb_device(const struct device *dev)

{

    return dev->type == &usb_device_type;

}

開看看驅動hub_thread==>usb_alloc_dev建立的插入到HUB上的usb裝置的probe函數generic_probe.[luther.gliethttp]

subsys_initcall(usb_init);

==>usb_init

==>usb_register_device_driver(&usb_generic_driver, THIS_MODULE);

struct usb_device_driver usb_generic_driver = {

    .name =    "usb",

    .probe = generic_probe,

    .disconnect = generic_disconnect,

#ifdef    CONFIG_PM

    .suspend = generic_suspend,

    .resume = generic_resume,

#endif

    .supports_autosuspend = 1,

};

==>generic_probe

==>usb_set_configuration // 生成該設定配置描述下的所有接口描述符所描述的接口dev對象

==>ret = device_add(&intf->dev);

int usb_set_configuration(struct usb_device *dev, int configuration)

{

    int i, ret;

    struct usb_host_config *cp = NULL;

    struct usb_interface **new_interfaces = NULL;

    int n, nintf;

    if (dev->authorized == 0 || configuration == -1)

        configuration = 0;

    else {

        for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {

            if (dev->config[i].desc.bConfigurationValue ==

                    configuration) {

                cp = &dev->config[i];

                break;

            }

        }

    }

    if ((!cp && configuration != 0))

        return -EINVAL;

    if (cp && configuration == 0)

        dev_warn(&dev->dev, "config 0 descriptor??/n");

    n = nintf = 0;

    if (cp) {

        nintf = cp->desc.bNumInterfaces;

        new_interfaces = kmalloc(nintf * sizeof(*new_interfaces),

                GFP_KERNEL);

        if (!new_interfaces) {

            dev_err(&dev->dev, "Out of memory/n");

            return -ENOMEM;

        }

        for (; n < nintf; ++n) {

            new_interfaces[n] = kzalloc(

                    sizeof(struct usb_interface),

                    GFP_KERNEL);

            if (!new_interfaces[n]) {

                dev_err(&dev->dev, "Out of memory/n");

                ret = -ENOMEM;

free_interfaces:

                while (--n >= 0)

                    kfree(new_interfaces[n]);

                kfree(new_interfaces);

                return ret;

            }

        }

        i = dev->bus_mA - cp->desc.bMaxPower * 2;

        if (i < 0)

            dev_warn(&dev->dev, "new config #%d exceeds power "

                    "limit by %dmA/n",

                    configuration, -i);

    }

    ret = usb_autoresume_device(dev);

    if (ret)

        goto free_interfaces;

    if (dev->state != USB_STATE_ADDRESS)

        usb_disable_device(dev, 1);   

    ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),

                  USB_REQ_SET_CONFIGURATION, 0, configuration, 0,

                  NULL, 0, USB_CTRL_SET_TIMEOUT);

    if (ret < 0) {

        cp = NULL;

    }

    dev->actconfig = cp;

    if (!cp) {

        usb_set_device_state(dev, USB_STATE_ADDRESS);

        usb_autosuspend_device(dev);

        goto free_interfaces;

    }

    usb_set_device_state(dev, USB_STATE_CONFIGURED);

    for (i = 0; i < nintf; ++i) {

        struct usb_interface_cache *intfc;

        struct usb_interface *intf;

        struct usb_host_interface *alt;

        cp->interface[i] = intf = new_interfaces[i];

        intfc = cp->intf_cache[i];

        intf->altsetting = intfc->altsetting;

        intf->num_altsetting = intfc->num_altsetting;

        intf->intf_assoc = find_iad(dev, cp, i);

        kref_get(&intfc->ref);

        alt = usb_altnum_to_altsetting(intf, 0);

        if (!alt)

            alt = &intf->altsetting[0];

        intf->cur_altsetting = alt;

        usb_enable_interface(dev, intf);

        intf->dev.parent = &dev->dev;

        intf->dev.driver = NULL;

        intf->dev.bus = &usb_bus_type; // 位于usb_bus_type總線

        intf->dev.type = &usb_if_device_type; // 為接口裝置,這樣在usb_device_match中将去和接口驅動去比對[luther.gliethttp]

        intf->dev.dma_mask = dev->dev.dma_mask;

        device_initialize(&intf->dev);

        mark_quiesced(intf);

        sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d",

            dev->bus->busnum, dev->devpath,

            configuration, alt->desc.bInterfaceNumber);

    }

    kfree(new_interfaces);

    if (cp->string == NULL)

        cp->string = usb_cache_string(dev, cp->desc.iConfiguration);

    for (i = 0; i < nintf; ++i) {

        struct usb_interface *intf = cp->interface[i];

        dev_dbg(&dev->dev,

            "adding %s (config #%d, interface %d)/n",

            intf->dev.bus_id, configuration,

            intf->cur_altsetting->desc.bInterfaceNumber);

        ret = device_add(&intf->dev); // 将接口裝置添加bus總線上同時到sysfs系統中.

        if (ret != 0) {

            dev_err(&dev->dev, "device_add(%s) --> %d/n",

                intf->dev.bus_id, ret);

            continue;

        }

        usb_create_sysfs_intf_files(intf);

    }

    usb_autosuspend_device(dev);

    return 0;

}

如下是一個整體代碼流程:

driver_probe_device

int driver_probe_device(struct device_driver *drv, struct device *dev)

{

    int ret = 0;

    if (!device_is_registered(dev)) // 1.裝置已經完成了注冊到bus總線工作

        return -ENODEV;

    if (drv->bus->match && !drv->bus->match(dev, drv)) // 2.執行bus提供的match操作usb_device_match

        goto done;

    pr_debug("bus: '%s': %s: matched device %s with driver %s/n",

         drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);

    ret = really_probe(dev, drv); // bus的match通過檢驗,這裡做進一步的probe檢驗,

    // 如果bus提供了probe,那麼執行bus->probe(dev);

    // 否則執行driver提供的probe函數drv->probe(dev);

done:

    return ret;

}

==>usb_bus_type

==>usb_device_match

static int usb_device_match(struct device *dev, struct device_driver *drv)

{

    if (is_usb_device(dev)) {

        if (!is_usb_device_driver(drv))

            return 0;

        return 1;

    } else {

        struct usb_interface *intf;

        struct usb_driver *usb_drv;

        const struct usb_device_id *id;

        if (is_usb_device_driver(drv))

            return 0;

        intf = to_usb_interface(dev); // 轉為接口dev裝置

        usb_drv = to_usb_driver(drv); // 轉為usb驅動

        id = usb_match_id(intf, usb_drv->id_table); // 接口裝置與usb接口驅動的id_table值表進行比對嘗試[luther.gliethttp]

        if (id)

            return 1;

        id = usb_match_dynamic_id(intf, usb_drv);

        if (id)

            return 1;

    }

    return 0;

}

==>really_probe

static int really_probe(struct device *dev, struct device_driver *drv)

{

    int ret = 0;

    atomic_inc(&probe_count);

    pr_debug("bus: '%s': %s: probing driver %s with device %s/n",

         drv->bus->name, __FUNCTION__, drv->name, dev->bus_id);

    WARN_ON(!list_empty(&dev->devres_head));

    dev->driver = drv;  // 先假定該driver就是驅動本dev的驅動,後面講做進一步确認[luther.gliethttp]

    if (driver_sysfs_add(dev)) {

        printk(KERN_ERR "%s: driver_sysfs_add(%s) failed/n",

            __FUNCTION__, dev->bus_id);

        goto probe_failed;

    }

    if (dev->bus->probe) {

        ret = dev->bus->probe(dev); // 如果bus總線提供probe,那麼執行之

        if (ret)

            goto probe_failed;

    } else if (drv->probe) {

        ret = drv->probe(dev); // 如果driver提供probe,那麼執行之,

                               // 我們這裡就是new_driver->drvwrap.driver.probe = usb_probe_interface;

        if (ret)

            goto probe_failed;

    }

    driver_bound(dev);

    ret = 1;

    pr_debug("bus: '%s': %s: bound device %s to driver %s/n",

         drv->bus->name, __FUNCTION__, dev->bus_id, drv->name);

    goto done;

probe_failed:

    devres_release_all(dev);

    driver_sysfs_remove(dev);

    dev->driver = NULL;

    if (ret != -ENODEV && ret != -ENXIO) {

        printk(KERN_WARNING

               "%s: probe of %s failed with error %d/n",

               drv->name, dev->bus_id, ret);

    }

    ret = 0;

done:

    atomic_dec(&probe_count);

    wake_up(&probe_waitqueue);

    return ret;

}

==>usb_probe_interface 即:new_driver->drvwrap.driver.probe = usb_probe_interface;

static int usb_probe_interface(struct device *dev)

{

    struct usb_driver *driver = to_usb_driver(dev->driver);

    struct usb_interface *intf;

    struct usb_device *udev;

    const struct usb_device_id *id;

    int error = -ENODEV;

    dev_dbg(dev, "%s/n", __FUNCTION__);

    if (is_usb_device(dev))       

        return error;

    intf = to_usb_interface(dev);

    udev = interface_to_usbdev(intf);

    if (udev->authorized == 0) {

        dev_err(&intf->dev, "Device is not authorized for usage/n");

        return -ENODEV;

    }

    id = usb_match_id(intf, driver->id_table);

    if (!id)

        id = usb_match_dynamic_id(intf, driver);

    if (id) {

        dev_dbg(dev, "%s - got id/n", __FUNCTION__);

        error = usb_autoresume_device(udev);

        if (error)

            return error;

        mark_active(intf);

        intf->condition = USB_INTERFACE_BINDING;

        intf->pm_usage_cnt = !(driver->supports_autosuspend);

        error = driver->probe(intf, id); // 調用usb_driver驅動定義的probe函數,我們這裡就是usb_serial_probe即:mct_u232_driver.probe

        if (error) {

            mark_quiesced(intf);

            intf->needs_remote_wakeup = 0;

            intf->condition = USB_INTERFACE_UNBOUND;

        } else

            intf->condition = USB_INTERFACE_BOUND;

        usb_autosuspend_device(udev);

    }

    return error;

}

==>usb_serial_probe即:mct_u232_driver.probe函數

int usb_serial_probe(struct usb_interface *interface,

                   const struct usb_device_id *id)

{

    struct usb_device *dev = interface_to_usbdev (interface);

    struct usb_serial *serial = NULL;

    struct usb_serial_port *port;

    struct usb_host_interface *iface_desc;

    struct usb_endpoint_descriptor *endpoint;

    struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];

    struct usb_endpoint_descriptor *interrupt_out_endpoint[MAX_NUM_PORTS];

    struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];

    struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];

    struct usb_serial_driver *type = NULL;

    int retval;

    int minor;

    int buffer_size;

    int i;

    int num_interrupt_in = 0;

    int num_interrupt_out = 0;

    int num_bulk_in = 0;

    int num_bulk_out = 0;

    int num_ports = 0;

    int max_endpoints;

    lock_kernel();

    type = search_serial_device(interface); // 周遊usb_serial_driver_list連結清單,搜尋與該interface比對上的序列槽驅動,

                                            // usb_serial_driver_list連結清單元素由函數usb_serial_driver_list添加

                                            // 我們這裡将傳回type = &mct_u232_device;[luther.gliethtp]

    if (!type) {

        unlock_kernel();

        dbg("none matched");

        return -ENODEV;

    }

    serial = create_serial (dev, interface, type); // 建立serial序列槽裝置

    if (!serial) {

        unlock_kernel();

        dev_err(&interface->dev, "%s - out of memory/n", __FUNCTION__);

        return -ENOMEM;

    }

    if (type->probe) { // mct_u232_device序列槽驅動沒有probe函數

        const struct usb_device_id *id;

        if (!try_module_get(type->driver.owner)) {

            unlock_kernel();

            dev_err(&interface->dev, "module get failed, exiting/n");

            kfree (serial);

            return -EIO;

        }

        id = get_iface_id(type, interface);

        retval = type->probe(serial, id);

        module_put(type->driver.owner);

        if (retval) {

            unlock_kernel();

            dbg ("sub driver rejected device");

            kfree (serial);

            return retval;

        }

    }

    iface_desc = interface->cur_altsetting; // 目前接口對應的接口描述符資訊[luther.gliethttp]

    for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { // 為該接口dev裝置收集端點通信管道

        endpoint = &iface_desc->endpoint[i].desc;

        if (usb_endpoint_is_bulk_in(endpoint)) {

            dbg("found bulk in on endpoint %d", i);

            bulk_in_endpoint[num_bulk_in] = endpoint; // 批量IN端點

            ++num_bulk_in;

        }

        if (usb_endpoint_is_bulk_out(endpoint)) {

            dbg("found bulk out on endpoint %d", i);

            bulk_out_endpoint[num_bulk_out] = endpoint; // 批量OUT端點

            ++num_bulk_out;

        }

        if (usb_endpoint_is_int_in(endpoint)) {

            dbg("found interrupt in on endpoint %d", i);

            interrupt_in_endpoint[num_interrupt_in] = endpoint; // 中斷IN端點

            ++num_interrupt_in;

        }

        if (usb_endpoint_is_int_out(endpoint)) {

            dbg("found interrupt out on endpoint %d", i);

            interrupt_out_endpoint[num_interrupt_out] = endpoint; // 中斷OUT端點

            ++num_interrupt_out;

        }

    }

#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)

    // 執行PL2303 usb轉序列槽裝置的IN端點資訊特殊處理[luther.gliethttp]

    if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&

         (le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||

        ((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&

         (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||

        ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&

         (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID))) {

        if (interface != dev->actconfig->interface[0]) { // 如果目前PL2303 usb轉序列槽不等于0接口,dev->actconfig->interface[0]

            iface_desc = dev->actconfig->interface[0]->cur_altsetting; // 那麼将0接口中的中斷IN端點添加到該serial裝置中

            for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {

                endpoint = &iface_desc->endpoint[i].desc;

                if (usb_endpoint_is_int_in(endpoint)) {

                    dbg("found interrupt in for Prolific device on separate interface");

                    interrupt_in_endpoint[num_interrupt_in] = endpoint;

                    ++num_interrupt_in;

                }

            }

        }

        if (num_bulk_in == 0 || num_bulk_out == 0) {

            unlock_kernel();

            dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not/n");

            kfree (serial);

            return -ENODEV;

        }

    }

#endif

#ifdef CONFIG_USB_SERIAL_GENERIC

    if (type == &usb_serial_generic_device) {

        num_ports = num_bulk_out;

        if (num_ports == 0) { // 如果是usb_serial_generic_device驅動,那麼批量OUT端點務必要存在,并且可用端點個數就是批量OUT端點個數.

            unlock_kernel();

            dev_err(&interface->dev, "Generic device with no bulk out, not allowed./n");

            kfree (serial);

            return -EIO;

        }

    }

#endif

    if (!num_ports) { // 好,不是usb_serial_generic_device驅動

        if (type->calc_num_ports) { // 是否需要調用計算函數計算端點個數

            if (!try_module_get(type->driver.owner)) {

                unlock_kernel();

                dev_err(&interface->dev, "module get failed, exiting/n");

                kfree (serial);

                return -EIO;

            }

            num_ports = type->calc_num_ports (serial);

            module_put(type->driver.owner);

        }

        if (!num_ports)

            num_ports = type->num_ports; // 由usb轉序列槽接口驅動定義端點個數:mct_u232_device定義num_ports為1

    }

    serial->num_ports = num_ports; // 端點個數

    serial->num_bulk_in = num_bulk_in; // 批量IN端點個數

    serial->num_bulk_out = num_bulk_out; // 批量OUT端點個數

    serial->num_interrupt_in = num_interrupt_in; // 中斷IN端點個數

    serial->num_interrupt_out = num_interrupt_out; // 中斷OUT端點個數

#if 0

    if ((type->num_interrupt_in != NUM_DONT_CARE &&

                type->num_interrupt_in != num_interrupt_in)

            || (type->num_interrupt_out != NUM_DONT_CARE &&

                type->num_interrupt_out != num_interrupt_out)

            || (type->num_bulk_in != NUM_DONT_CARE &&

                type->num_bulk_in != num_bulk_in)

            || (type->num_bulk_out != NUM_DONT_CARE &&

                type->num_bulk_out != num_bulk_out)) {

        dbg("wrong number of endpoints");

        kfree(serial);

        return -EIO;

    }

#endif

    dev_info(&interface->dev, "%s converter detected/n",

            type->description);

    max_endpoints = max(num_bulk_in, num_bulk_out);

    max_endpoints = max(max_endpoints, num_interrupt_in);

    max_endpoints = max(max_endpoints, num_interrupt_out);

    max_endpoints = max(max_endpoints, (int)serial->num_ports);

    serial->num_port_pointers = max_endpoints;

    unlock_kernel();

    dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);

    for (i = 0; i < max_endpoints; ++i) {

        port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); // 申請4種端點集合結構體struct usb_serial_port

        if (!port)

            goto probe_error;

        port->serial = serial;

        spin_lock_init(&port->lock);

        mutex_init(&port->mutex);

        INIT_WORK(&port->work, usb_serial_port_work); // 設定工作隊列work_queue

        serial->port[i] = port;

    }

    for (i = 0; i < num_bulk_in; ++i) {

        endpoint = bulk_in_endpoint[i];

        port = serial->port[i];

        port->read_urb = usb_alloc_urb (0, GFP_KERNEL); // 申請URB控制結構體

        if (!port->read_urb) {

            dev_err(&interface->dev, "No free urbs available/n");

            goto probe_error;

        }

        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

        port->bulk_in_size = buffer_size; // 端點大小

        port->bulk_in_endpointAddress = endpoint->bEndpointAddress; // 端點位址

        port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); // 端點管道資料緩沖區

        if (!port->bulk_in_buffer) {

            dev_err(&interface->dev, "Couldn't allocate bulk_in_buffer/n");

            goto probe_error;

        }

        // 填充上面申請到的read_urb URB控制結構體

        usb_fill_bulk_urb (port->read_urb, dev,

                   usb_rcvbulkpipe (dev,

                               endpoint->bEndpointAddress),

                   port->bulk_in_buffer, buffer_size,

                   serial->type->read_bulk_callback, // 在mct_u232_device中定義的批量讀回調函數

                   port);

    }

    for (i = 0; i < num_bulk_out; ++i) {

        endpoint = bulk_out_endpoint[i];

        port = serial->port[i];

        port->write_urb = usb_alloc_urb(0, GFP_KERNEL);// 申請URB控制結構體

        if (!port->write_urb) {

            dev_err(&interface->dev, "No free urbs available/n");

            goto probe_error;

        }

        buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

        port->bulk_out_size = buffer_size;

        port->bulk_out_endpointAddress = endpoint->bEndpointAddress;

        port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);

        if (!port->bulk_out_buffer) {

            dev_err(&interface->dev, "Couldn't allocate bulk_out_buffer/n");

            goto probe_error;

        }

        // 填充上面申請到的write_urb URB控制結構體

        usb_fill_bulk_urb (port->write_urb, dev,

                   usb_sndbulkpipe (dev,

                            endpoint->bEndpointAddress),

                   port->bulk_out_buffer, buffer_size,

                   serial->type->write_bulk_callback,// 在mct_u232_device中定義的批量寫回調函數

                   port);

    }

    if (serial->type->read_int_callback) {

        for (i = 0; i < num_interrupt_in; ++i) {

            endpoint = interrupt_in_endpoint[i];

            port = serial->port[i];

            port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);// 申請URB控制結構體

            if (!port->interrupt_in_urb) {

                dev_err(&interface->dev, "No free urbs available/n");

                goto probe_error;

            }

            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

            port->interrupt_in_endpointAddress = endpoint->bEndpointAddress;

            port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);

            if (!port->interrupt_in_buffer) {

                dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer/n");

                goto probe_error;

            }

            // 填充上面申請到的interrupt_in_urb URB控制結構體

            usb_fill_int_urb (port->interrupt_in_urb, dev,

                      usb_rcvintpipe (dev,

                              endpoint->bEndpointAddress),

                      port->interrupt_in_buffer, buffer_size,

                      serial->type->read_int_callback, port, // 在mct_u232_device中定義的INT中斷讀回調函數

                      endpoint->bInterval);

        }

    } else if (num_interrupt_in) {

        dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined");

    }

    if (serial->type->write_int_callback) {

        for (i = 0; i < num_interrupt_out; ++i) {

            endpoint = interrupt_out_endpoint[i];

            port = serial->port[i];

            port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);// 申請URB控制結構體

            if (!port->interrupt_out_urb) {

                dev_err(&interface->dev, "No free urbs available/n");

                goto probe_error;

            }

            buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);

            port->interrupt_out_size = buffer_size;

            port->interrupt_out_endpointAddress = endpoint->bEndpointAddress;

            port->interrupt_out_buffer = kmalloc (buffer_size, GFP_KERNEL);

            if (!port->interrupt_out_buffer) {

                dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer/n");

                goto probe_error;

            }

            // 填充上面申請到的interrupt_out_urb URB控制結構體

            usb_fill_int_urb (port->interrupt_out_urb, dev,

                      usb_sndintpipe (dev,

                              endpoint->bEndpointAddress),

                      port->interrupt_out_buffer, buffer_size,

                      serial->type->write_int_callback, port,// 在mct_u232_device中定義的INT中斷寫回調函數

                      endpoint->bInterval);

        }

    } else if (num_interrupt_out) {

        dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined");

    }

    if (type->attach) { // mct_u232_device定義了attach方法的實作mct_u232_startup

        if (!try_module_get(type->driver.owner)) {

            dev_err(&interface->dev, "module get failed, exiting/n");

            goto probe_error;

        }

        retval = type->attach (serial);

        module_put(type->driver.owner);

        if (retval < 0)

            goto probe_error;

        if (retval > 0) {

            goto exit;

        }

    }

    if (get_free_serial (serial, num_ports, &minor) == NULL) { // 申請次裝置号,同時:serial->port[j++]->number = i;

        dev_err(&interface->dev, "No more free serial devices/n");

        goto probe_error;

    }

    serial->minor = minor; // 該serial的次裝置号為minor

    for (i = 0; i < num_ports; ++i) {

        port = serial->port[i];

        port->dev.parent = &interface->dev; // 接口當然為端點端口的父object

        port->dev.driver = NULL;

        port->dev.bus = &usb_serial_bus_type; // port位于usb_serial_bus_type總線

        port->dev.release = &port_release;

        snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);

        dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);

        retval = device_register(&port->dev); // 登記注冊該port對應的dev裝置資訊,同樣在bus總線usb_serial_bus_type上執行match動作.

        if (retval)

            dev_err(&port->dev, "Error registering port device, "

                "continuing/n");

    }

    usb_serial_console_init (debug, minor);

exit:

    usb_set_intfdata (interface, serial);

    return 0;

probe_error:

    for (i = 0; i < num_bulk_in; ++i) {

        port = serial->port[i];

        if (!port)

            continue;

        usb_free_urb(port->read_urb);

        kfree(port->bulk_in_buffer);

    }

    for (i = 0; i < num_bulk_out; ++i) {

        port = serial->port[i];

        if (!port)

            continue;

        usb_free_urb(port->write_urb);

        kfree(port->bulk_out_buffer);

    }

    for (i = 0; i < num_interrupt_in; ++i) {

        port = serial->port[i];

        if (!port)

            continue;

        usb_free_urb(port->interrupt_in_urb);

        kfree(port->interrupt_in_buffer);

    }

    for (i = 0; i < num_interrupt_out; ++i) {

        port = serial->port[i];

        if (!port)

            continue;

        usb_free_urb(port->interrupt_out_urb);

        kfree(port->interrupt_out_buffer);

    }

    for (i = 0; i < serial->num_port_pointers; ++i)

        kfree(serial->port[i]);

    kfree (serial);

    return -EIO;

}

==>usb_serial_bus_type總線驅動port各種端點端口組合結構體

   device_register(&port->dev); // 登記注冊該port對應的dev裝置資訊,同樣在bus總線usb_serial_bus_type上執行match動作.

struct bus_type usb_serial_bus_type = {

    .name =        "usb-serial",

    .match =    usb_serial_device_match,

    .probe =    usb_serial_device_probe,

    .remove =    usb_serial_device_remove,

    .drv_attrs =     drv_attrs,

};

==>usb_serial_device_match

static int usb_serial_device_match (struct device *dev, struct device_driver *drv)

{

    struct usb_serial_driver *driver;

    const struct usb_serial_port *port;

    port = to_usb_serial_port(dev);

    if (!port)

        return 0;

    driver = to_usb_serial_driver(drv);

    if (driver == port->serial->type) // 等于mct_u232_device驅動,那麼傳回1

    // port->serial在serial = create_serial (dev, interface, type); 中建立.

    // serial->type = driver; // 該serial對應的driver

        return 1;

    return 0;

}

==>usb_serial_device_probe

static int usb_serial_device_probe (struct device *dev)

{

    struct usb_serial_driver *driver;

    struct usb_serial_port *port;

    int retval = 0;

    int minor;

    port = to_usb_serial_port(dev);

    if (!port) {

        retval = -ENODEV;

        goto exit;

    }

    driver = port->serial->type;

    if (driver->port_probe) { // mct_u232_device沒有port_probe

        if (!try_module_get(driver->driver.owner)) {

            dev_err(dev, "module get failed, exiting/n");

            retval = -EIO;

            goto exit;

        }

        retval = driver->port_probe (port);

        module_put(driver->driver.owner);

        if (retval)

            goto exit;

    }

    retval = device_create_file(dev, &dev_attr_port_number); // 建立sysfs檔案系統中對應的檔案[luther.gliethttp]

    if (retval)

        goto exit;

    minor = port->number; // 在上面的get_free_serial函數中執行了serial->port[j++]->number = i;指派操作,是以可以為ttyUSB0,ttyUSB1,...

                          // port->number就是serial_table[port->number]數組索引号

    tty_register_device (usb_serial_tty_driver, minor, dev); // 注冊次裝置号為minor的tty裝置,該/dev/ttyUSBx裝置由usb_serial_tty_driver驅動程式管理

    dev_info(&port->serial->dev->dev,

         "%s converter now attached to ttyUSB%d/n",

         driver->description, minor);

exit:

    return retval;

}

==>tty_register_device(usb_serial_tty_driver, minor, dev);

// usb_serial_tty_driver在usb_serial_init,即:module_init(usb_serial_init);

// usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;

// tty_set_operations(usb_serial_tty_driver, &serial_ops);

// tty_register_driver(usb_serial_tty_driver);==>list_add(&driver->tty_drivers, &tty_drivers);将自己添加到tty_drivers連結清單中

struct device *tty_register_device(struct tty_driver *driver, unsigned index,

                   struct device *device)

{

    char name[64];

    dev_t dev = MKDEV(driver->major, driver->minor_start) + index;

    if (index >= driver->num) {

        printk(KERN_ERR "Attempt to register invalid tty line number "

               " (%d)./n", index);

        return ERR_PTR(-EINVAL);

    }

    if (driver->type == TTY_DRIVER_TYPE_PTY)

        pty_line_name(driver, index, name);

    else

        tty_line_name(driver, index, name); // 非pty裝置

    return device_create(tty_class, device, dev, name);

    // 建立sysfs檔案系統下的檔案,同時uevent到使用者空間,建立/dev/ttyUSB0...等

    // 對/dev/ttyUSB0...等操作方法為tty_fops

    // /dev/ttyUSB0...等在SERIAL_TTY_MAJOR ~ SERIAL_TTY_MAJOR + SERIAL_TTY_MINORS之間的裝置号讀寫操作均由tty_fops

    // 方法集提供,

    // tty_register_driver(usb_serial_tty_driver);

    // 比如open

    // tty_open==>tty->driver->open即:serial_ops.serial_open

}

==>tty_register_driver(usb_serial_tty_driver); // 注冊驅動/dev/xxx字元裝置的tty驅動程式

    dev = MKDEV(driver->major, driver->minor_start);

    error = register_chrdev_region(dev, driver->num, driver->name); // 裝置号從dev到dev+driver->num個字元裝置都将由該driver驅動

    cdev_init(&driver->cdev, &tty_fops); // 注冊該MAJOR到MINOR之間的/dev/xxx字元裝置節點對應的操作函數集為tty_fops

    cdev_add(&driver->cdev, dev, driver->num); // 添加到字元管理數值中

static const struct file_operations tty_fops = {

    .llseek        = no_llseek,

    .read        = tty_read,

    .write        = tty_write,

    .poll        = tty_poll,

    .ioctl        = tty_ioctl,

    .compat_ioctl    = tty_compat_ioctl,

    .open        = tty_open,

    .release    = tty_release,

    .fasync        = tty_fasync,

};

==>cdev_add

int cdev_add(struct cdev *p, dev_t dev, unsigned count)

{

    p->dev = dev;

    p->count = count;

    return kobj_map(cdev_map, dev, count, NULL, exact_match, exact_lock, p); // 将&driver->cdev添加到cdev_map字元裝置驅動管理數組中,以備下面sys_open時kobj_lookup使用 [luther.gliethttp]

}

看看系統調用open函數

open("/dev/ttyUSB0")

==>chrdev_open // sys_open将調用字元裝置驅動函數集中open函數chrdev_open

const struct file_operations def_chr_fops = {

    .open = chrdev_open,

};

==>kobj = kobj_lookup(cdev_map, inode->i_rdev, &idx); // 搜尋上面cdev_add登記的usb_serial_tty_driver驅動 調用exact_match傳回&driver->cdev.kobj

   new = container_of(kobj, struct cdev, kobj);

   inode->i_cdev = p = new; // 由上面cdev_init(&driver->cdev, &tty_fops); 初始化cdev->ops = &tty_fops

   filp->f_op = fops_get(p->ops); // 擷取驅動方法集

   filp->f_op->open(inode,filp);

==>tty_open同時在tty_drivers連結清單上調用get_tty_driver函數搜尋,

  "/dev/ttyUSB0"裝置節點号對應的tty_drivers,比如搜尋到usb_serial_tty_driver驅動,

  那麼它就是tty->driver了.同時生成tty:init_dev(driver, index, &tty);其中index表示該字元裝置為驅動管理的第index索引處裝置.

  之後init_dev==>tty = alloc_tty_struct();

              ==>initialize_tty_struct(tty); // 初始化該tty的ldisc等于tty_ldisc_N_TTY即tty->ldisc = &tty_ldisc_N_TTY;

              tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));為tty->ldisc綁定線路規程

              console_init==>注冊tty_ldisc_N_TTY線路規程

              tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);

              struct tty_ldisc tty_ldisc_N_TTY = {

                  .magic           = TTY_LDISC_MAGIC,

                  .name            = "n_tty",

                  .open            = n_tty_open,

                  .close           = n_tty_close,

                  .flush_buffer    = n_tty_flush_buffer,

                  .chars_in_buffer = n_tty_chars_in_buffer,

                  .read            = read_chan,

                  .write           = write_chan,

                  .ioctl           = n_tty_ioctl,

                  .set_termios     = n_tty_set_termios,

                  .poll            = normal_poll,

                  .receive_buf     = n_tty_receive_buf,

                  .write_wakeup    = n_tty_write_wakeup

              };

              ==>tty->driver = driver;

              ==>tty->index = idx; // 該tty在driver中的索引值

              ==>tty_line_name(driver, idx, tty->name);

              ==>driver->ttys[idx] = tty; // dirver管理的第index個裝置指針[luther.gliethttp]

              ==>(tty->ldisc.open)(tty); // 調用tty_ldisc_N_TTY.open即n_tty_open函數

  之後filp->private_data = tty;

  之後tty->driver->open

==>tty->driver->open就是usb_serial_tty_driver.open即:serial_ops.serial_open

==>serial_open // 執行裝置實際打開操作

static int serial_open (struct tty_struct *tty, struct file * filp)

{

    struct usb_serial *serial;

    struct usb_serial_port *port;

    unsigned int portNumber;

    int retval;

    dbg("%s", __FUNCTION__);

    serial = usb_serial_get_by_index(tty->index); // 就是 return serial_table[index];因為driver的序列槽索引就等于serial_table數組的索引.

    if (!serial) {

        tty->driver_data = NULL;

        return -ENODEV;

    }

    portNumber = tty->index - serial->minor; // 端口号為目前dev裝置的序列槽索引index減去該serial裝置登記的起始索引值,比如一個

    // serial裝置可以有多個port,比如有3個,那麼portNumber就可能等于0,1或者2.[luther.gliethttp]

    port = serial->port[portNumber]; // 擷取'/dev/ttyUSBx'對應的port

    if (!port) {

        retval = -ENODEV;

        goto bailout_kref_put;

    }

    if (mutex_lock_interruptible(&port->mutex)) {

        retval = -ERESTARTSYS;

        goto bailout_kref_put;

    }

    ++port->open_count;

    tty->driver_data = port; // 驅動driver_data似有資料為port

    port->tty = tty; // 該port服務于該tty

    if (port->open_count == 1) { // 第1次打開

        if (!try_module_get(serial->type->driver.owner)) {

            retval = -ENODEV;

            goto bailout_mutex_unlock;

        }

        retval = usb_autopm_get_interface(serial->interface);

        if (retval)

            goto bailout_module_put;

        retval = serial->type->open(port, filp); // 執行mct_u232_device.open即:mct_u232_open

        if (retval)

            goto bailout_interface_put;

    }

    mutex_unlock(&port->mutex);

    return 0; // ok,至此sys_open工作就算徹底完成了[luther.gliethttp]

bailout_interface_put:

    usb_autopm_put_interface(serial->interface);

bailout_module_put:

    module_put(serial->type->driver.owner);

bailout_mutex_unlock:

    port->open_count = 0;

    tty->driver_data = NULL;

    port->tty = NULL;

    mutex_unlock(&port->mutex);

bailout_kref_put:

    usb_serial_put(serial);

    return retval;

}

tty_write

==>do_tty_write(ld->write, tty, file, buf, count);調用線路規程tty_ldisc_N_TTY的write函數write_chan

==>write_chan

==>tty->driver->write(tty, b, nr);就是上面的usb_serial_tty_driver.write即:serial_ops.serial_write

==>serial_write将調用port->serial->type->write(port, buf, count);就是mct_u232_device的write函數

   在usb_serial_register(&mct_u232_device);中将使用fixup_generic(driver);函數填充mct_u232_device未定義的操作函數集.

   是以mct_u232_device的write函數為usb_serial_generic_write

==>mct_u232_device.write即:usb_serial_generic_write

==>usb_serial_generic_write

==>usb_submit_urb(port->write_urb, GFP_ATOMIC); // 送出一個URB

==>usb_hcd_submit_urb(urb, mem_flags);

==>hcd->driver->urb_enqueue(hcd, urb, mem_flags);

tty_read

==>(ld->read)(tty, file, buf, count);調用線路規程tty_ldisc_N_TTY的read函數read_chan

==>read_chan

在mct_u232_device中定義了mct_u232_read_int_callback,即中斷IN回調函數,當usb轉序列槽裝置從usb接口接收到資料之後,

它将執行mct_u232_read_int_callback回調函數,

static void mct_u232_read_int_callback (struct urb *urb)

{

    struct usb_serial_port *port = (struct usb_serial_port *)urb->context;

    struct mct_u232_private *priv = usb_get_serial_port_data(port);

    struct usb_serial *serial = port->serial;

    struct tty_struct *tty;

    unsigned char *data = urb->transfer_buffer;

    int retval;

    int status = urb->status;

    unsigned long flags;

    switch (status) {

    case 0:

        break;

    case -ECONNRESET:

    case -ENOENT:

    case -ESHUTDOWN:

        dbg("%s - urb shutting down with status: %d",

            __FUNCTION__, status);

        return;

    default:

        dbg("%s - nonzero urb status received: %d",

            __FUNCTION__, status);

        goto exit;

    }

    if (!serial) {

        dbg("%s - bad serial pointer, exiting", __FUNCTION__);

        return;

    }

        dbg("%s - port %d", __FUNCTION__, port->number);

    usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);

    if (urb->transfer_buffer_length > 2) {

        int i;

        tty = port->tty;

        if (urb->actual_length) {

            for (i = 0; i < urb->actual_length ; ++i) {

                tty_insert_flip_char(tty, data[i], 0); // 将從usb接收到的資料放入tty緩沖區中[luther.gliethttp]

            }

            tty_flip_buffer_push(tty); // 喚醒pending着的read_chan函數,這樣tty就收到資料了

        }

        goto exit;

    }

    spin_lock_irqsave(&priv->lock, flags);

    priv->last_msr = data[MCT_U232_MSR_INDEX];

    mct_u232_msr_to_state(&priv->control_state, priv->last_msr);

#if 0

    priv->last_lsr = data[MCT_U232_LSR_INDEX];

    if (priv->last_lsr & MCT_U232_LSR_ERR) {

        tty = port->tty;

        if (priv->last_lsr & MCT_U232_LSR_OE) {

        }

        if (priv->last_lsr & MCT_U232_LSR_PE) {

        }

        if (priv->last_lsr & MCT_U232_LSR_FE) {

        }

        if (priv->last_lsr & MCT_U232_LSR_BI) {

        }

    }

#endif

    spin_unlock_irqrestore(&priv->lock, flags);

exit:

    retval = usb_submit_urb (urb, GFP_ATOMIC);

    if (retval)

        err ("%s - usb_submit_urb failed with result %d",

             __FUNCTION__, retval);

}

todo ...

mct_u232_device 驅動位于usb_serial_bus_type總線上,mct_u232_driver驅動位于usb_bus_type總線上,當hub發現usb新 硬體之後,會首先調用usb_bus_type總線上的mct_u232_driver驅動的probe(),也就是 usb_serial_probe(),在usb_serial_probe()中,程式會周遊usb_serial_driver_list驅動連結清單的 所有驅動,并usb_serial_driver_list嘗試和發現的新硬體進行比對,在計算比對的過程中會調 用,drv->bus->match,即:usb_serial_bus_type->match()和 dev->bus->probe或者drv->probe即:usb_serial_device_probe(),這樣裝置就和分别 處在兩條獨立總線上的mct_u232_driver驅動以及mct_u232_device驅動關聯上了[gliethttp_20090430].

繼續閱讀