现在的位置: 首页 > 综合 > 正文

通用 USB 设备驱动源码分析 【转】

2013年07月09日 ⁄ 综合 ⁄ 共 35523字 ⁄ 字号 评论关闭

通用 USB 设备驱动源码分析

Author:aaron

前段时间写了篇 <qualcomm usb modem 驱动小结 > 的文章 , 描述了自己如何为高通的一个 usb modem 设备写驱动的过程 , 最近发现实际上可以使用 linux 自带的一个叫 usbserial 的模块作为这个 modem 的驱动并能良好的工作 , 所以写了这片文章来详细的分析下 usbserial 模块的源码 (2.6.16.3).

应该来说 , 对于那些仅仅是用 USB 来通信 , 在上层可看作 tty 设备 , 不属于任何 USB 设备类型 , 没有什么流控等的普通 USB 设备来说都可以使用这个驱动来作为设备驱动程序 . 下面就来对这样一种通用的驱动程序来进行详细的分析 . 不对之处敬请指正 !

 

为了能让 usbserail 模块支持我的设备 , 我必须在命令行上输入如下命令 :

sudo modprobe usbserial vendor=0x12d1 product=0x1003

该命令用特权用户来加载 usbserial 模块 , 并把该模块依赖的模块一并加载进系统 , 同时它还设置了 usbserial 的两个参数 : vendor, product,  很显然这两个参数是厂商 ID 和设备 ID, 而作用就是用于匹配设备 .

首先 , 当然是要知道 usbserial 模块由哪些文件编译而成 , 这样才能有目的性的去分析其代码 . 而要知道其组成当然是去其目录下看 Makefile 了 ,  它位于内核源码目录下的 ./drivers/usb/serial/ 下

./drivers/usb/serial/Makefile:

#

# Makefile for the USB serial device drivers.

#

 

# Object file lists.

 

obj-$(CONFIG_USB_SERIAL)                 += usbserial.o  # 编译内核时如何编译该模块

 

usbserial-obj-$(CONFIG_USB_SERIAL_CONSOLE)      += console.o

usbserial-obj-$(CONFIG_USB_EZUSB)           += ezusb.o

 

usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y)   #OK, 就是 usbserial 模块的组成了 .

 

obj-$(CONFIG_USB_SERIAL_AIRPRIME)            += airprime.o

obj-$(CONFIG_USB_SERIAL_ANYDATA)            += anydata.o

.......

我们重点看的是 usb-serial.c,  generic.c, bus.c

 

在看源码之前我们先说说该模块的原理及整体结构 :

很简单跟应用层交互的是一个 tty 设备 , 也就是说该模块把 USB 设备映射成一个 tty 设备 ( 即在 /dev/ 目录下为该 USB 设备创建一个 tty 设备文件 ), 然后用于可以用 minicom 之类的串口工具来打开这个设备 , 并同设备端的设备通信 .

对于发送过程 : tty 设备文件在获取了用户要求发送的数据之后传递到下层 usbserial 模块的核心层 , 而该核心层就是将数据打包成 USB 格式的数据并由 USB 通信发送到设备端去 ,

对于接收过程 : usbserial 模块会在该设备打开时就启动一个 urb 在那等待设备端发数据过来 , 收到数据后就 push 到上层 tty 设备的缓冲中去 , 而 tty 设备在收到数据后就会给用户 , 或直接显示在 minicom 之类的工具上 .

 

usb-serial.c 就是 usbserial 模块的核心 , 它主要用来接收设备端发来的数据并传送到上层 , 同时也接收来自上层应用的数据 , 并组装成 urb 包发送给设备 .

generic.c 对特定设备单独的操作 , 相当于是设备自己的驱动程序 , 由于很多设备具有通用性 , 所以对于没有特殊要求的设备都可以使用这个驱动来作为自己设备的驱动程序 . 它有两个参数 vendor 和 product, 上面提过了 .

bus.c  每个 usb 驱动和设备都必须要归入某一条总线上 , 即都是归属于某条总线的 , 只有这样系统才能从特定一条总线开始找到每个驱动和设备并为他们匹配 . 这个文件就是用来模拟一条总线 , 而 usbserial 的每个驱动和设备都会注册到这条总线上来 .

 

好了 , 是时候分析 usbserial 模块了 .

 

我们知道当把一个模块加载进系统时会调用这个模块里的一个由 module_init() 声明的一个初始化函数 . usbserial 当然也不另外 ,

usb-serial.c:

module_init(usb_serial_init);   

module_exit(usb_serial_exit);

没错加载时调用的就是 :   usb_serial_init().

usb-serial.c:

struct tty_driver *usb_serial_tty_driver;

static int __init usb_serial_init(void)

{

       int i;

       int result;

 

    // 创建一个 tty_driver 对象 , 对应的就是 tty 设备的驱动 .

       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;   // 该模块共支持 SERIAL_TTY_MINORS 个该类型设备 .

       }

 

       result = bus_register(&usb_serial_bus_type);   // 注册这条 serial bus.

       if (result) {

              err("%s - registering bus driver failed", __FUNCTION__);

              goto exit_bus;

       }

    

    // 初始化 tty_driver 对象

       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";   //tty 设备文件名以这个开头 , 后加 0,1,2,3,....

       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 设备的操作集合 , 即应用层调用 open 时最终会调到 serial_ops->open 里面

       tty_set_operations(usb_serial_tty_driver, &serial_ops);  

       result = tty_register_driver(usb_serial_tty_driver);   // 注册这个 tty 驱动

       if (result) {

              err("%s - tty_register_driver failed", __FUNCTION__);

              goto exit_reg_driver;

       }

 

       /* register the USB driver */

       result = usb_register(&usb_serial_driver);   // 注册一个 usb 驱动

       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);  // 注册 generic 驱动程序

       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;

}

该函数先创建并初始化好了一个 tty_driver 的对象 , 并把该对象注册进系统 , 该对象就是 tty 设备的驱动程序 , 后面我们会看到他是如何于具体 tty 设备绑定在一起的 .

usb_serial.c:

static struct tty_operations serial_ops = {

       .open =                 serial_open,

       .close =          serial_close,

       .write =          serial_write,

       .write_room =       serial_write_room,

       .ioctl =           serial_ioctl,

       .set_termios =        serial_set_termios,

       .throttle =              serial_throttle,

       .unthrottle =          serial_unthrottle,

       .break_ctl =           serial_break,

       .chars_in_buffer = serial_chars_in_buffer,

       .read_proc =          serial_read_proc,

       .tiocmget =            serial_tiocmget,

       .tiocmset =            serial_tiocmset,

};

这个就是 tty 设备文件对应的操作方法集合 , 例如 , 应用层调用 open 函数来打开该设备文件时将最终会走到 serial_open 里面 .

usb_serial_init() 还注册了一条总线 : usb_serial_bus_type, 这样当有设备连上系统时 , 该总线上的驱动就有机会去匹配这个设备 . 后面我们会看到 generic 的驱动就是注册在该总线上的 .

bus.c:

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,

};

关于设备匹配过程 (probe) 可以参考我的另一篇文章 .

usb_serial_init() 在最后 usb_serial_generic_register(debug) 来注册 generic 驱动 .

generic.c:

int usb_serial_generic_register (int _debug)

{

       int retval = 0;

 

       debug = _debug;

#ifdef CONFIG_USB_SERIAL_GENERIC

       generic_device_ids[0].idVendor = vendor;   // 保存厂商 ID

       generic_device_ids[0].idProduct = product;  // 保存产品 ID

       generic_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;     // 匹配类型

 

       /* register our generic driver with ourselves */

       retval = usb_serial_register (&usb_serial_generic_device);   // 注册驱动

       if (retval)

              goto exit;

       retval = usb_register(&generic_driver);     // 注册驱动

       if (retval)

              usb_serial_deregister(&usb_serial_generic_device);

exit:

#endif

       return retval;

}

该函数首先保存了命令通过命令行设备的 vendor,product 用于以后设备匹配 , 由此我们知道该驱动可以动态支持设备匹配 . 接着该函数注册了 usb_serial_generic_device 驱动 .

generic.c:

struct usb_serial_driver usb_serial_generic_device = {

       .driver = {

              .owner = THIS_MODULE,

              .name =         "generic",

       },

       .id_table =             generic_device_ids,  // 匹配用的设备列表 , 支持动态匹配

       .num_interrupt_in =      NUM_DONT_CARE,

       .num_bulk_in =            NUM_DONT_CARE,

       .num_bulk_out =          NUM_DONT_CARE,

       .num_ports =         1,

       .shutdown =          usb_serial_generic_shutdown,

};

Usb-serial.c:

int usb_serial_register(struct usb_serial_driver *driver)

{

       int retval;

 

       fixup_generic(driver);   // 为 driver 赋上默认的操作函数

 

       if (!driver->description)

              driver->description = driver->driver.name;

 

       /* Add this device to our list of devices */

       list_add(&driver->driver_list, &usb_serial_driver_list);   // 加入驱动列表

 

       retval = usb_serial_bus_register(driver);   // 把该驱动注册进 usb serial bus 下

       if (retval) {

              err("problem %d when registering driver %s", retval, driver->description);

              list_del(&driver->driver_list);

       }

       else

              info("USB Serial support registered for %s", driver->description);

 

       return retval;

}

其中的 fixup_generic() 函数仅仅是为 driver 赋上默认的操作函数 .

Usb-serial.c:

#define set_to_generic_if_null(type, function)                         /

       do {                                                  /

              if (!type->function) {                                /

                     type->function = usb_serial_generic_##function; /

                     dbg("Had to override the " #function          /

                              " usb serial operation with the generic one.");/

                     }                                        /

       } while (0)

static void fixup_generic(struct usb_serial_driver *device)

{

       set_to_generic_if_null(device, open);

       set_to_generic_if_null(device, write);

       set_to_generic_if_null(device, close);

       set_to_generic_if_null(device, write_room);

       set_to_generic_if_null(device, chars_in_buffer);

       set_to_generic_if_null(device, read_bulk_callback);

       set_to_generic_if_null(device, write_bulk_callback);

       set_to_generic_if_null(device, shutdown);

}

即通过上面的 usb_serial_register() 函数后 usb_serial_generic_device 的函数集为 :

usb_serial_generic_device.open = usb_serial_generic_open;

usb_serial_generic_device.close = usb_serial_generic_close

......

驱动 usb_serial_generic_device 将是以后操作 tty 设备的主要函数 . 我们会在后面分析 .

 

bus.c:

int usb_serial_bus_register(struct usb_serial_driver *driver)

{

       int retval;

 

       driver->driver.bus = &usb_serial_bus_type;    // 注册到该 bus 下

       retval = driver_register(&driver->driver);

 

       return retval;

}

 

最后 usb_serial_generic_register() 函数注册了一个 generic_driver 驱动 .

generic.c:

static struct usb_driver generic_driver = {

       .name =         "usbserial_generic",

       .probe =  generic_probe,    // 匹配函数

       .disconnect =  usb_serial_disconnect,

       .id_table =      generic_serial_ids,  // 匹配用的设备列表

       .no_dynamic_id =        1,    // 不支持动态匹配

};

整个初始化过程 , 乍一看一下子注册了几个驱动程序 , 几个驱动列表 , 有的支持动态匹配有的不支持 , 感觉很复杂 , 其实注册 generic_driver 驱动主要是为了注册一个 generic_probe 函数 , 而该函数将会在设备连上系统后被调用以来匹配设备 . 除此之外该驱动没什么用 , 而在这个初始化函数中把 vendor,product 都保存在了 generic_device_ids 里 , 因此可以肯定以后的匹配将用这个设备列表 , 而不是 generic_serial_ids, 说的更直白些 generic_serial_ids 其实根本也没什么用 .  真正有用的是 usb_serial_generic_device 驱动 ,

 

generic.c:

static int generic_probe(struct usb_interface *interface,

                            const struct usb_device_id *id)

{

       const struct usb_device_id *id_pattern;

 

       id_pattern = usb_match_id(interface,   generic_device_ids);   // 设备匹配

       if (id_pattern != NULL)

              return usb_serial_probe(interface, id);  // 进一步匹配

       return -ENODEV;

}

如果接入系统的设备的 vendor 和 product 与我们驱动支持的设备列表匹配则调用 usb_serial_probe 来进一步匹配 .

usb_serial_probe 函数比较长 , 我们一段段的来看

usb-serial.c:

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;

       }

......

}

首先是找到合适的驱动程序 .

usb-serial.c:

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;

}

实际上这边的匹配和 generic_probe 里的匹配重复了 , 因为他们的匹配的设备列表是同一个 ,  这边主要是为了得到匹配的驱动程序 , 根据上面的代码分析我们可以知道这里匹配的驱动是 usb_serial_generic_device.

接着看 usb_serial_probe()

usb-serial.c:

....

       serial = create_serial (dev, interface, type);   // 为该设备创建一个 usb_serial 对象

       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) {   // 从上面分析的代码可知这里的 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;

              }

       }

....

这段代码可知 , 主要是创建一个 usb_serial 的对象 , 用于保存该设备的详细信息 , 一般的驱动程序都会为自己匹配的设备创建一个描用于描述该设备的对象 . 在以后的所有操作中如读写等都会直接从这个对象里获取相应的信息 .

usb-serial.c:

static struct usb_serial * create_serial (struct usb_device *dev,

                                     struct usb_interface *interface,

                                     struct usb_serial_driver *driver)

{

       struct usb_serial *serial;

 

       serial = kmalloc (sizeof (*serial), GFP_KERNEL);  // 闯将该对象

       if (!serial) {

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

              return NULL;

       }

    // 初始化该对象

       memset (serial, 0, sizeof(*serial));

       serial->dev = usb_get_dev(dev);   // 增加 dev 的引用计数

       serial->type = driver;  

       serial->interface = interface; 

       kref_init(&serial->kref);

 

       return serial;

}

这个函数就是用来创建 usb_serial 对象的 , 并把相关信息保存在里面 .

继续看 usb_serial_probe()

usb-serial.c:

....

       /* descriptor matches, let's find the endpoints needed */

       /* check out the endpoints */

     // 查找该设备使用的 endpoint 的描述符 , 并检查是否正确

       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 */   //bulk in 的端点

                     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 */   //bulk out 的端点

                     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 */    // 中断 in 端点

                     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 */ // 中断 out 端点

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

                     interrupt_out_endpoint[num_interrupt_out] = endpoint;

                     ++num_interrupt_out;

              }

       }

.....

该段代码主要是获取该设备使用的各个类型及方向的端点描述府 , 并保存起来 , 关于端点的类型与方向可以参考 USB 的规范 .

继续看 usb_serial_probe()

usb-serial.c:

....

#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

上面这段代码主要是用于特定类型设备的 (PL2303), 这里我们不用管他 .

接着看 usb_serial_probe()

usb-serial.c:

....

       /* found all that we need */

       dev_info(&interface->dev, "%s converter detected/n", type->description);

 

#ifdef CONFIG_USB_SERIAL_GENERIC   // 这个宏定义了 , 因为我们使用的是通用 USB 驱动 .

       if (type == &usb_serial_generic_device) {   // 这个 if 为 TRUE( 上面分析过了 )

              num_ports = num_bulk_out;  

              if (num_ports == 0) {    //bulk out 端点必须要有

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

                     kfree (serial);

                     return -EIO;

              }

       }

#endif

       if (!num_ports) {   // 由于走到了上面那个 if 段 , 因此这里的 num_ports 肯定不为 0

              /* if this device type has a calc_num_ports function, call it */

              if (type->calc_num_ports) {

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

                            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;

       }

    // 获取一个空闲的 serial_table 项

       if (get_free_serial (serial, num_ports, &minor) == NULL) {

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

              kfree (serial);

              return -ENOMEM;

       }

usbserial 模块总共支持 SERIAL_TTY_MINORS 个设备 , 它为每个设备都分配了一个 serial_table 项 , 用于保存 usb_serial 对象 , 方便以后直接通过 minor 号获取 usb_serial 对象 .

usb-serial.c:

static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor)

{

       unsigned int i, j;

       int good_spot;

 

       dbg("%s %d", __FUNCTION__, num_ports);

 

       *minor = 0;

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

              if (serial_table[i])     // 查找一个空闲的 serial_table 项 , serial_table 是个 usb_serial 的指针数组 .

                     continue;

 

              good_spot = 1;

        // 从上面代码可知 , 对于 generic 的驱动 , num_ports 就等于 num_bulk_out, 而一般的设备仅有 // 一个 bulk out 的端点 , 因此这个 for 循环不会执行 .

              for (j = 1; j <= num_ports-1; ++j)

                     if ((i+j >= SERIAL_TTY_MINORS) || (serial_table[i+j])) {

                            good_spot = 0;

                            i += j;

                            break;

                     }

              if (good_spot == 0)

                     continue;

 

              *minor = i;  // 获取 minor 号

              dbg("%s - minor base = %d", __FUNCTION__, *minor);

              for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)

serial_table[i] = serial;   // 获取空闲的 serial_table 项 , 并把我们的 usb_serial 对象地址保

// 存在其中 .

              return serial;

       }

       return NULL;

}

通过这个函数我们找到了一个空闲的 serial_table 项 , 并把描述我们设备的 usb_serial 对象保存在其中 , 在以后对设备的使用中 , 我们可以轻易的通过 minor 号来找到这个 usb_serial.

接着看 usb_serial_probe()

usb-serial.c:

....

    // 保存设备信息到 usb_serial 对象中 ,

       serial->minor = minor;

       serial->num_ports = num_ports;   // 这里的 port 数量不是 endpoint 的数量 ,

       serial->num_bulk_in = num_bulk_in;

       serial->num_bulk_out = num_bulk_out;

       serial->num_interrupt_in = num_interrupt_in;

       serial->num_interrupt_out = num_interrupt_out;

 

       /* create our ports, we need as many as the max endpoints */

       /* we don't use num_ports here cauz some devices have more endpoint pairs than ports */

    // 对于 generic 的驱动来说一般都只有一个 bulk in, 一个 bulk out, 一个 interrupt in, 一个 interrupt out

       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);

    // 到这一步 , 对于 generic 来说大多数情况下 max_endpoints 还是为 1

       serial->num_port_pointers = max_endpoints;

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

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

              port = kmalloc(sizeof(struct usb_serial_port), GFP_KERNEL);  // 分配一个 port 对象

              if (!port)

                     goto probe_error;

       // 初始化 port 对象

              memset(port, 0x00, sizeof(struct usb_serial_port));

              port->number = i + serial->minor;

              port->serial = serial;   // 保存 usb_serial 对象 , 便于以后通过 port 对象访问到 usb_serial 对象

              spin_lock_init(&port->lock);

              sema_init(&port->sem, 1);

              INIT_WORK(&port->work, usb_serial_port_softint, port);

              serial->port[i] = port;

       }

    // 由上面的对 port 的初始化可知 , 每个 port 都有一套自己的工作机制 , port 间互不干扰

       /* set up the endpoint information */

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

         // 初始化 bulk in 端点 , 并把它保存到相应的 port 里

              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;

              }

        // 设置好该 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,

                               port);

       }

 

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

        // 初始化 bulk out 端点 , 并把它保存到相应的 port 里

              endpoint = bulk_out_endpoint[i];

              port = serial->port[i];

              port->write_urb = usb_alloc_urb(0, GFP_KERNEL);

              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;

              }

              usb_fill_bulk_urb (port->write_urb, dev,

                                usb_sndbulkpipe (dev,

                                              endpoint->bEndpointAddress),

                               port->bulk_out_buffer, buffer_size,

                               serial->type->write_bulk_callback,

                               port);

       }

.....

上面这段代码主要是保存设备信息到 usb_serial 中去 , 并为每个口分配一个 port, 同时设置好 port 中的项 .  对于这里的 port, 我的理解是有的设备可能有多个口 , 而每个口都有自己的一套 endpoint 用于该口的功能实现 . 因此我们要为每个口分别分配 port 对象 , 并保存该口下的 endpoint 信息 .

接着看 usb_serial_probe()

usb-serial.c:

....

       if (serial->type->read_int_callback) {  // 对于 generic 驱动 , 这里的 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);

                     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;

                     }

                     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,

                                     endpoint->bInterval);

              }

       } else if (num_interrupt_in) {  // 如果有 interrupt in 而没有 read_int_callback 则是错误的

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

       }

     // 从上面这个 if 可以看出 generic 驱动的设备不应该有 interrupt in 端点 , 有也可以但是这个端点将

// 不起作用 .

// 下面这个 if 和上面的这个 if 功能一模一样 .

       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);

                     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;

                     }

                     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,

                                     endpoint->bInterval);

              }

       } else if (num_interrupt_out) {

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

       }

      

       /* if this device type has an attach function, call it */

       if (type->attach) {  // 对于 generic 驱动 , 没有设置这个 attach 函数

              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) {

                     /* quietly accept this device, but don't bind to a serial port

                       * as it's about to disappear */

                     goto exit;

              }

       }

 

       /* register all of the individual ports with the driver core */

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

              port = serial->port[i];

              port->dev.parent = &interface->dev;

              port->dev.driver = NULL;

              port->dev.bus = &usb_serial_bus_type;   // 注册到 usb_serial_bus 上去

              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);

              device_register (&port->dev);    // 把该设备注册到系统中去

       }

 

       usb_serial_console_init (debug, minor);

......

上面这段代码主要就是初始化 interrupt 的端点 , 并且把 port 设备挂到 usb_serial_bus 上注册进系统 ,

接着看 usb_serial_probe()

usb-serial.c:

......

exit:

       /* success */

       usb_set_intfdata (interface, serial);    // 在 interface 对象里保存 usb_serial 对象地址 , 以方便以后使用

       return 0;

 

// 错误处理

probe_error:

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

              port = serial->port[i];

              if (!port)

                     continue;

              if (port->read_urb)

                     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;

              if (port->write_urb)

                     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;

              if (port->interrupt_in_urb)

                     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;

              if (port->interrupt_out_urb)

                     usb_free_urb (port->interrupt_out_urb);

              kfree(port->interrupt_out_buffer);

       }

 

       /* return the minor range that this device had */

       return_serial (serial);

 

       /* free up any memory that we allocated */

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

              kfree(serial->port[i]);

       kfree (serial);

       return -EIO;

 

这样整个 probe 过程就结束了 , 或许你会奇怪 , 好像 usb_serial_bus 都没什么用 , 而且好像没看见这个设备和 init 时的那个 tty_driver 绑定啊 ,  不错 , 这个 probe 函数本身还没有作这些工作 , 我们接着分析 .

usb_serial_probe() 函数在最后调用了 device_register (&port->dev); 把设备注册进了系统 , 在这个注册过程中 , 系统会在次为这个注册的设备进行 probe 过程 .

 

我们先来了解下 probe 的过程

Driver 的 prob 的调用顺序 :  

1 device_add(): 把 device 注册到相应的 bus 上去 , 并创建相应的 device file, 最后调用 bus_attach_device()

2 bus_attach_device() 调用 device_attach(dev)

3 device_attach(): 调用 bus_for_each_drv() 遍历 bus 上的每个 driver, 当找到一个 driver 则用 __device_attach() 来判断是否匹配

4 __device_attach(): 直接调用 driver_probe_device(drv, dev)

5 driver_probe_device(): 首先如果 driver 所在总线有 match 函数则先调用这个 match 来匹配 , 如不匹配则直接返回错误 , 否则接着调用 really_probe(dev,drv)

6 really_probe(): 先判断 dev 所在总线是否有 probe 函数 , 有则调用它来匹配 , 失败则返回 , 正确则成功 , 如果总线没有 probe 则判断 drv 是否有 probe 函数 , 有则调用并匹配它 .

7 drv->prob():  一般它是一类设备的 probe, 在它里面它会调用具体某个 drv 的 probe 函数 , 这个函数是在我们的驱动程序里面注册的 .  

 

device_register() 里会调用 device_add(). 因此由 5 可知 系统会先调用总线上的 match 函数来匹配 . 对于我们的总线就是 usb_serial_bus, 它的 match 函数就是 usb_serial_device_match(), 至于为什么是这条总线 , 通过前面的代码分析 , 我们知道我们的设备也好 , 我们的驱动也好都是注册在这条总线上的 .

下面我们就来分析 usb_serial_device_match()

Bus.c:

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

{

  struct usb_serial_driver *driver;

  const struct usb_serial_port *port;

 

  /*

    * drivers are already assigned to ports in serial_probe so it's

    * a simple check here.

    */

  port = to_usb_serial_port(dev);    // 获取 usb_serial_port 对象

  if (!port)

         return 0;

 

  driver = to_usb_serial_driver(drv);   // 获取 usb_serial_driver 对象

 

  if (driver == port->serial->type)   // 匹配否 ?

         return 1;

 

  return 0;

}

很显然 , 通过前面的分析可知 , 这里肯定是匹配的 .

接着通过上面 probe 过程的 6 可知会调用总线的 probe 函数 , 这里就是 usb_serial_device_probe

Bus.c:

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) {

              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;

       }

 

       minor = port->number;

    tty_register_device (usb_serial_tty_driver, minor, dev);  // 呵呵 , 这里总算把 tty_driver 和 device 绑定

// 起来了

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

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

                driver->description, minor);

 

exit:

       return retval;

}

到了这一步该设备就和 tty_driver 绑定在了一起了 , 同时在 /dev 下也创建了相应的设备文件了 , 也就是说应用层可以使用这个设备了 .

这样整个对设备的 probe 过程才算真的完成了 .

OK, 设备从 USB 口连上系统 , 并被系统认出及 probe 的真个过程就基本完成了 , 从此以后设备就进入了就绪状态 . 下面我们就开始分析对设备的操作流程了 .

 

要使用设备当然要先打开这个设备了 , 应用层调用 open 系统调用来打开这个设备 , 它最终会跑到我们 tty_driver 的 open 函数里面

static struct tty_operations serial_ops = {

       .open =                 serial_open,

       .close =          serial_close,

       .write =          serial_write,

       .write_room =       serial_write_room,

       .ioctl =           serial_ioctl,

       .set_termios =        serial_set_termios,

       .throttle =              serial_throttle,

       .unthrottle =          serial_unthrottle,

       .break_ctl =           serial_break,

       .chars_in_buffer = serial_chars_in_buffer,

       .read_proc =          serial_read_proc,

       .tiocmget =            serial_tiocmget,

       .tiocmset =            serial_tiocmset,

};

这里就是 serial_open 了 .

usb_serial.c:

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__);

 

       /* get the serial object associated with this tty pointer */

       serial = usb_serial_get_by_index(tty->index);   // 获取 usb_serial 对象 , 根据上面的分析 , 不难理解

       if (!serial) {

              tty->driver_data = NULL;

              return -ENODEV;

       }

 

       portNumber = tty->index - serial->minor;

       port = serial->port[portNumber];   // 获取设备对应的 port 对象 , 这也不难理解了

       if (!port)

              return -ENODEV;

 

       if (down_interruptible(&port->sem))

              return -ERESTARTSYS;

        

       ++port->open_count;  // 跟踪打开次数

 

       if (port->open_count == 1) {

 

              /* set up our port structure making the tty driver

                * remember our port object, and us it */

              tty->driver_data = port;  // 赋值 , 为以后的操作方便引用

              port->tty = tty; 

 

              /* lock this module before we call it

                * this may fail, which means we must bail out,

                * safe because we are called with BKL held */

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

                     retval = -ENODEV;

                     goto bailout_kref_put;

              }

 

              /* only call the device specific open if this

                * is the first time the port is opened */

              retval = serial->type->open(port, filp);   // 调用 usb_serial_driver 的 open 函数 , 在前面分析过了

              if (retval)

                     goto bailout_module_put;

       }

 

       up(&port->sem);

       return 0;

 

bailout_module_put:

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

bailout_kref_put:

       kref_put(&serial->kref, destroy_serial);

       port->open_count = 0;

       up(&port->sem);

       return retval;

}

可以看到这个 open 动作主要就是保存一些信息 , 以方便后面使用 , 接着调用 usb_serial_driver 的 open 函数 , 这里该 open 是 usb_serial_generic_open, 前面分析过了

generic.c:

int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)

{

       struct usb_serial *serial = port->serial;

       int result = 0;

 

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

 

       /* force low_latency on so that our tty_push actually forces the data through,

          otherwise it is scheduled, and with high data rates (like with OHCI) data

          can get lost. */

       if (port->tty)

              port->tty->low_latency = 1;

 

       /* if we have a bulk interrupt, start reading from it */

    // 如果有 bulk in 的端点的话 , 就提交这个端点的 urb, 即让系统开始在这个端点上接收来自设备段

// 发过来的数据 , 当数据收到后会调用 serial->type->read_bulk_callback 函数 .

       if (serial->num_bulk_in) {

              /* Start reading from the device */

              usb_fill_bulk_urb (port->read_urb, serial->dev,

                               usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),

                               port->read_urb->transfer_buffer,

                               port->read_urb->transfer_buffer_length,

                               ((serial->type->read_bulk_callback) ?

                                 serial->type->read_bulk_callback :

                                 usb_serial_generic_read_bulk_callback),

                               port);

              result = usb_submit_urb(port->read_urb, GFP_KERNEL);   // 提交

              if (result)

              

抱歉!评论已关闭.