usb

来源:互联网 发布:软件著作权 技术入股 编辑:程序博客网 时间:2024/04/30 02:31

通用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 forthe USB serial device drivers.

#

 

# Object filelists.

 

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 对特定设备单独的操作,相当于是设备自己的驱动程序, 由于很多设备具有通用性, 所以对于没有特殊要求的设备都可以使用这个驱动来作为自己设备的驱动程序. 它有两个参数vendorproduct,上面提过了.

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:

structtty_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 driverfailed", __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_driverfailed", __FUNCTION__);

              goto exit_reg_driver;

       }

 

       /* register the USB driver */

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

       if (result < 0) {

              err("%s - usb_registerfailed", __FUNCTION__);

              goto exit_tty;

       }

 

       /* register the generic driver, if weshould */

       result =usb_serial_generic_register(debug);  //注册generic驱动程序

       if (result < 0) {

              err("%s - registering genericdriver 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 structtty_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_typeusb_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:

intusb_serial_generic_register (int _debug)

{

       int retval = 0;

 

       debug = _debug;

#ifdefCONFIG_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 withourselves */

       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:

structusb_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:

intusb_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 whenregistering driver %s", retval, driver->description);

              list_del(&driver->driver_list);

       }

       else

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

 

       return retval;

}

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

Usb-serial.c:

#defineset_to_generic_if_null(type, function)                         /

       do {                                                  /

              if (!type->function) {                                /

                     type->function =usb_serial_generic_##function; /

                     dbg("Had to overridethe " #function          /

                             " usb serial operation with the genericone.");/

                     }                                        /

       } while (0)

static voidfixup_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:

intusb_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 structusb_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 intgeneric_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;

}

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

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

usb-serial.c:

intusb_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 structusb_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 knowndevice */

       list_for_each(p, &usb_serial_driver_list){

              t = list_entry(p, structusb_serial_driver, driver_list);

              id = usb_match_id(iface,t->id_table);   //看设备列表是否匹配

              if (id != NULL) {

                     dbg("descriptormatches");

                     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 probefunction, 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 driverrejected device");

                     kfree (serial);

                     return retval;

              }

       }

....

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

usb-serial.c:

static structusb_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 theendpoints 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 inendpoint */   //bulk in 的端点

                     dbg("found bulk in onendpoint %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 outendpoint */   //bulk out的端点

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

                     bulk_out_endpoint[num_bulk_out]= endpoint;

                     ++num_bulk_out;

              }

             

              if ((endpoint->bEndpointAddress& 0x80) &&

                 ((endpoint->bmAttributes & 3) == 0x03)) {

                     /* we found a interrupt inendpoint */    //中断 in 端点

                     dbg("found interruptin 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 interruptout endpoint */ //中断 out 端点

                     dbg("found interruptout 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("foundinterrupt 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 willwork

               * properly during a later invocation ofusb_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) {  //这个ifTRUE(上面分析过了)

              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)

{

       unsignedint i, j;

       intgood_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,而一般的设备仅有//一个bulkout 的端点, 因此这个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对象地址保

//存在其中.

              returnserial;

       }

       returnNULL;

}

通过这个函数我们找到了一个空闲的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 thanports */

   //对于generic的驱动来说一般都只有一个bulk in,一个bulk out,一个interruptin,一个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)

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

       //初始化bulkin 端点, 并把它保存到相应的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");

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

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

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

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

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

                            gotoprobe_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("thedevice claims to support interrupt in transfers, but read_int_callback is notdefined");

       }

     //从上面这个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");

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

                            gotoprobe_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("thedevice claims to support interrupt out transfers, but write_int_callback is notdefined");

       }

      

       /*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");

                     gotoprobe_error;

              }

              retval= type->attach (serial);

              module_put(type->driver.owner);

              if(retval < 0)

                     gotoprobe_error;

              if(retval > 0) {

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

                      * as it's about to disappear */

                     gotoexit;

              }

       }

 

       /*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对象地址,以方便以后使用

       return0;

 

//错误处理

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的过程

Driverprob的调用顺序:  

1 device_add(): device注册到相应的bus上去,并创建相应的devicefile,最后调用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,在它里面它会调用具体某个drvprobe函数,这个函数是在我们的驱动程序里面注册的.  

 

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)

{

  structusb_serial_driver *driver;

  conststruct usb_serial_port *port;

 

  /*

   * drivers are already assigned to ports inserial_probe so it's

   * a simple check here.

   */

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

  if (!port)

         return0;

 

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

 

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

         return1;

 

  return 0;

}

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

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

Bus.c:

static int usb_serial_device_probe (structdevice *dev)

{

       structusb_serial_driver *driver;

       structusb_serial_port *port;

       intretval = 0;

       intminor;

 

       port= to_usb_serial_port(dev);

       if(!port) {

              retval= -ENODEV;

              gotoexit;

       }

 

       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;

                     gotoexit;

              }

              retval= driver->port_probe (port);

              module_put(driver->driver.owner);

              if(retval)

                     gotoexit;

       }

 

       minor= port->number;

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

//起来了

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

               "%s converter now attached tottyUSB%d/n",

               driver->description, minor);

 

exit:

       returnretval;

}

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

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

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

 

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

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)

{

       structusb_serial *serial;

       structusb_serial_port *port;

       unsignedint portNumber;

       intretval;

      

       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;

                     gotobailout_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_driveropen函数, 在前面分析过了

              if(retval)

                     gotobailout_module_put;

       }

 

       up(&port->sem);

       return0;

 

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

       returnretval;

}

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

generic.c:

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

{

       structusb_serial *serial = port->serial;

       intresult = 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 highdata 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)

                     dev_err(&port->dev,"%s - failed resubmitting read urb, error %d/n", __FUNCTION__,result);

       }

 

       returnresult;

}

这个函数后, 系统就开始在bulk in的端点上接收数据了, 如果有数据到来的话就会调用我们的回调函数.

接下来我们来看这个回调函数:

Generic.c:

void usb_serial_generic_read_bulk_callback(struct urb *urb, struct pt_regs *regs)

{

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

       structusb_serial *serial = port->serial;

       structtty_struct *tty;

       unsignedchar *data = urb->transfer_buffer;

       intresult;

 

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

 

       if(urb->status) {   //这次通信的状态

              dbg("%s- nonzero read bulk status received: %d", __FUNCTION__, urb->status);

              return;

       }

 

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

 

       tty= port->tty;

       if(tty && urb->actual_length) {

       //如果有数据接收到的话, 就把它存入tty驱动的缓冲中去

              tty_buffer_request_room(tty,urb->actual_length);

              tty_insert_flip_string(tty,data, urb->actual_length);

            tty_flip_buffer_push(tty);

       }

 

       /*Continue trying to always read  */

   //继续下一个接收操作.

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

       if(result)

              dev_err(&port->dev,"%s - failed resubmitting read urb, error %d/n", __FUNCTION__,result);

}

很明显, 这个回调就是把从设备端收到的数据push到相对上层的tty驱动中去, 随后tty子系统会把这些数据返回给用户.

OK, 看完接收过程后我们在来看下发送过程.

发送过程由用户发起, 用户可以调用write系统调用来发数据, 最后会跑到tty驱动的write函数中去.

Usb-serial.c:

static int serial_write (struct tty_struct* tty, const unsigned char *buf, int count)

{

       structusb_serial_port *port = tty->driver_data;

       intretval = -EINVAL;

 

       if(!port)

              gotoexit;

 

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

 

       if(!port->open_count) {

              dbg("%s- port not opened", __FUNCTION__);

              gotoexit;

       }

 

       /*pass on to the driver specific version of this function */

       retval= port->serial->type->write(port, buf, count);  //仅仅是调用usb_serial_driverwrite函数

 

exit:

       returnretval;

}

这个函数,直接调用了usb_serial_driverwrite函数.

Generic.c:

int usb_serial_generic_write(structusb_serial_port *port, const unsigned char *buf, int count)

{

       structusb_serial *serial = port->serial;

       intresult;

       unsignedchar *data;

 

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

 

       if(count == 0) {

              dbg("%s- write request of 0 bytes", __FUNCTION__);

              return(0);

       }

 

       /*only do something if we have a bulk out endpoint */

   //仅在有bulk out的端点上才能发送

       if(serial->num_bulk_out) {

              spin_lock(&port->lock);

              if(port->write_urb_busy) {

                     spin_unlock(&port->lock);

                     dbg("%s- already writing", __FUNCTION__);

                     return0;

              }

              port->write_urb_busy= 1;

              spin_unlock(&port->lock);

       

       //把要发送的数据, 发送的字节数存入urb

              count= (count > port->bulk_out_size) ? port->bulk_out_size : count;

 

              memcpy(port->write_urb->transfer_buffer, buf, count);

              data= port->write_urb->transfer_buffer;

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

 

              /*set up our urb */

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

                               usb_sndbulkpipe (serial->dev,

                                              port->bulk_out_endpointAddress),

                               port->write_urb->transfer_buffer,count,

                               ((serial->type->write_bulk_callback) ?

                                 serial->type->write_bulk_callback :

                                 usb_serial_generic_write_bulk_callback),port);

 

              /*send the data out the bulk port */

              port->write_urb_busy= 1;

              result= usb_submit_urb(port->write_urb, GFP_ATOMIC);   //提交一个发送过程

              if(result) {

                     dev_err(&port->dev,"%s - failed submitting write urb, error %d/n", __FUNCTION__,result);

                     /*don't have to grab the lock here, as we will retry if != 0 */

                     port->write_urb_busy= 0;

              }else

                     result= count;

 

              returnresult;

       }

 

       /*no bulk out, so return 0 bytes written */

       return0;

}

该函数把用户要发送的数据封装到一个urb中去, 并把该urb提交给系统以执行一个发送的操作, 发送完成后会调用serial->type->write_bulk_callback函数.

接着看这个回调函数:

Generic.c:

voidusb_serial_generic_write_bulk_callback (struct urb *urb, struct pt_regs *regs)

{

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

 

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

 

       port->write_urb_busy= 0;

       if(urb->status) {

              dbg("%s- nonzero write bulk status received: %d", __FUNCTION__, urb->status);

              return;

       }

 

       usb_serial_port_softint((void*)port);   //请求传输更多的数据

 

       schedule_work(&port->work);   //调度一个工作

}

Usb-serial.c:

void usb_serial_port_softint(void*private)

{

       structusb_serial_port *port = private;

       structtty_struct *tty;

 

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

      

       if(!port)

              return;

 

       tty= port->tty;

       if(!tty)

              return;

 

       tty_wakeup(tty);   //请求传输更多的数据

}

其实很简单,

其他的函数就不多做分析了,

看懂了这个模块后, 实际上对USB设备的驱动也就大体知道了其流程.