USB 3G上网卡讲解之二

USB 3G卡热插拔那些事3——usb serial驱动

上一节我们已经把3G识别出来并且可以工作了,具体3G卡拨号这一块我们稍后会讲到,这里先提下,首先就是pppd拨号程序,可以网上下载最新版源码自己编译安装,我是利用evdo拨号(本人是用的SIM5360E,WCDMA/GSM,即联通2G/3G),当然还有其他方式。这里是用户空间如何建立ppp连接,在内核层就是ppp协议以及tty模块,在往下就是wcdma模块驱动(上一节我们已经讲过)。下面就说说usb serial驱动.
在之前我们知道uevent事件,但是对于它的调用我一直不太明白,然后就代码搜了下(当然这里知道它是在注册进设备模型的时候产生的),在core.c drivers/base,它是在dev_uevent里调用:

    if (dev->bus && dev->bus->uevent) {
        /* have the bus specific function add its stuff */
        retval = dev->bus->uevent(dev, envp, num_envp, buffer, buffer_size);
            if (retval) {
            pr_debug ("%s - uevent() returned %d\n",
                  __FUNCTION__, retval);
        }
    }

然后就追了下,dev_uevent在这里初始化:

static struct kset_uevent_ops device_uevent_ops = {
    .filter =   dev_uevent_filter,
    .name =     dev_uevent_name,
    .uevent =   dev_uevent,
};

而device_uevent_ops刚好就是一个声明子系统的宏里调用:

/*
 *  devices_subsys - structure to be registered with kobject core.
 */

decl_subsys(devices, &ktype_device, &device_uevent_ops);

言归正传,首先我们看下usb serial模块的初始化函数:

static int __init usb_serial_init(void)
{
    int i;
    int result;

    usb_serial_tty_driver = alloc_tty_driver(SERIAL_TTY_MINORS);
    if (!usb_serial_tty_driver)
        return -ENOMEM;

    /* Initialize our global data */
    for (i = 0; i < SERIAL_TTY_MINORS; i) {
        serial_table[i] = NULL;
    }

    result = bus_register(&usb_serial_bus_type);
    if (result) {
        err("%s - registering bus driver failed", __FUNCTION__);
        goto exit_bus;
    }

    usb_serial_tty_driver->owner = THIS_MODULE;
    usb_serial_tty_driver->driver_name = "usbserial";
    usb_serial_tty_driver->devfs_name = "usb/tts/";
    usb_serial_tty_driver->name =   "ttyUSB";
    usb_serial_tty_driver->major = SERIAL_TTY_MAJOR;
    usb_serial_tty_driver->minor_start = 0;
    usb_serial_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
    usb_serial_tty_driver->subtype = SERIAL_TYPE_NORMAL;
    usb_serial_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS;
    usb_serial_tty_driver->init_termios = tty_std_termios;
    usb_serial_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
    tty_set_operations(usb_serial_tty_driver, &serial_ops);
    result = tty_register_driver(usb_serial_tty_driver);
    if (result) {
        err("%s - tty_register_driver failed", __FUNCTION__);
        goto exit_reg_driver;
    }

    /* register the USB driver */
    result = usb_register(&usb_serial_driver);
    if (result < 0) {
        err("%s - usb_register failed", __FUNCTION__);
        goto exit_tty;
    }

    /* register the generic driver, if we should */
    result = usb_serial_generic_register(debug);
    if (result < 0) {
        err("%s - registering generic driver failed", __FUNCTION__);
        goto exit_generic;
    }

    info(DRIVER_DESC);

    return result;

exit_generic:
    usb_deregister(&usb_serial_driver);

exit_tty:
    tty_unregister_driver(usb_serial_tty_driver);

exit_reg_driver:
    bus_unregister(&usb_serial_bus_type);

exit_bus:
    err ("%s - returning with error %d", __FUNCTION__, result);
    put_tty_driver(usb_serial_tty_driver);
    return result;
}

这里我们最关注bus_register(&usb_serial_bus_type)tty_register_driver(usb_serial_tty_driver)usb_register(&usb_serial_driver) 这几个函数,其实我们知道usb serial的驱动也是注册到usb bus总线上的,下面我们来看具体的函数usb_register(&usb_serial_driver);这里我感觉非常有趣,usb_serial_driver注册注册函数是usb_register,我们是否明白了点什么,其实usb驱动没有我们想象的那么复杂和难懂^^!
那么我们来看usb_serial_driver的初始化:

/* Driver structure we register with the USB core */
static struct usb_driver usb_serial_driver = {
    .name =     "usbserial",
    .probe =    usb_serial_probe,
    .disconnect =   usb_serial_disconnect,
    .no_dynamic_id =    1,
};

这里我们只需要关注usb_serial_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;

    type = search_serial_device(interface);
    if (!type) {
        dbg("none matched");
        return -ENODEV;
    }

    serial = create_serial (dev, interface, type);
    if (!serial) {
        dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
        return -ENOMEM;
    }

    /* if this device type has a probe function, call it */
    if (type->probe) {
        const struct usb_device_id *id;

        if (!try_module_get(type->driver.owner)) {
            dev_err(&interface->dev, "module get failed, exiting\n");
            kfree (serial);
            return -EIO;
        }

        id = usb_match_id(interface, type->id_table);
        retval = type->probe(serial, id);
        module_put(type->driver.owner);

        if (retval) {
            dbg ("sub driver rejected device");
            kfree (serial);
            return retval;
        }
    }

    /* descriptor matches, let's find the endpoints needed */
    /* check out the endpoints */
    iface_desc = interface->cur_altsetting;
    for (i = 0; i < iface_desc->desc.bNumEndpoints; i) {
        endpoint = &iface_desc->endpoint[i].desc;

        if ((endpoint->bEndpointAddress & 0x80) &&
            ((endpoint->bmAttributes & 3) == 0x02)) {
            /* we found a bulk in endpoint */
            dbg("found bulk in on endpoint %d", i);
            bulk_in_endpoint[num_bulk_in] = endpoint;
             num_bulk_in;
        }

        if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
            ((endpoint->bmAttributes & 3) == 0x02)) {
            /* we found a bulk out endpoint */
            dbg("found bulk out on endpoint %d", i);
            bulk_out_endpoint[num_bulk_out] = endpoint;
             num_bulk_out;
        }

        if ((endpoint->bEndpointAddress & 0x80) &&
            ((endpoint->bmAttributes & 3) == 0x03)) {
            /* we found a interrupt in endpoint */
            dbg("found interrupt in on endpoint %d", i);
            interrupt_in_endpoint[num_interrupt_in] = endpoint;
             num_interrupt_in;
        }

        if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
            ((endpoint->bmAttributes & 3) == 0x03)) {
            /* we found an interrupt out endpoint */
            dbg("found interrupt out on endpoint %d", i);
            interrupt_out_endpoint[num_interrupt_out] = endpoint;
             num_interrupt_out;
        }
    }

#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
    /* BEGIN HORRIBLE HACK FOR PL2303 */ 
    /* this is needed due to the looney way its endpoints are set up */
    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))) {
        if (interface != dev->actconfig->interface[0]) {
            /* check out the endpoints of the other interface*/
            iface_desc = dev->actconfig->interface[0]->cur_altsetting;
            for (i = 0; i < iface_desc->desc.bNumEndpoints; i) {
                endpoint = &iface_desc->endpoint[i].desc;
                if ((endpoint->bEndpointAddress & 0x80) &&
                    ((endpoint->bmAttributes & 3) == 0x03)) {
                    /* we found a interrupt in endpoint */
                    dbg("found interrupt in for Prolific device on separate interface");
                    interrupt_in_endpoint[num_interrupt_in] = endpoint;
                     num_interrupt_in;
                }
            }
        }

        /* Now make sure the PL-2303 is configured correctly.
         * If not, give up now and hope this hack will work
         * properly during a later invocation of usb_serial_probe
         */
        if (num_bulk_in == 0 || num_bulk_out == 0) {
            dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
            kfree (serial);
            return -ENODEV;
        }
    }
    /* END HORRIBLE HACK FOR PL2303 */
#endif

    /* found all that we need */
    dev_info(&interface->dev, "%s converter detected\n", type->description);

         ...

这个函数后面的代码不再贴出,我们看最后一行,因为3G卡整个识别过程我也是根据识别的时候的打印信息来一步一步分析和验证的来的。最先调用的就是这个usb_serial_probe函数,如果真的是usb serial则继续,后面要执行的就是usb_serial_bus_type

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,
};

接下来执行了usb_serial_device_probe(),跑到这里那么3G卡驱动和tty等已经帮到到一起,可以真正工作了。
我们继续回到usb_serial_probe函数里:

type = search_serial_device(interface);
static struct usb_serial_driver *search_serial_device(struct usb_interface *iface)
{
    struct list_head *p;
    const struct usb_device_id *id;
    struct usb_serial_driver *t;

    /* Check if the usb id matches a known device */
    list_for_each(p, &usb_serial_driver_list) {
        t = list_entry(p, struct usb_serial_driver, driver_list);
        id = usb_match_id(iface, t->id_table);
        if (id != NULL) {
            dbg("descriptor matches");
            return t;
        }
    }

    return NULL;
}

这里就是查询 usb_serial_bus_type下注册的驱动是否有支持它的,而我们看option.c里代码就可以明白,option.c里驱动是注册到这个总线下的。或许我们应该讲讲在3G卡插入后识别并加载相应驱动后的工作原理。也就是说3G的工作原理。不过我从网上看到一篇挺不错了,可以给大家拿来分享。

你可能感兴趣的:(fl2440驱动,wifi-3G,Linux)