usb_serial部分分析5

转自http://blog.csdn.net/aaronychen/article/details/3555885

我们先来了解下probe的过程

Driver的probe的调用顺序:  

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)

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

       }

 

       return result;

}

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

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

Generic.c:

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

{

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

       struct usb_serial *serial = port->serial;

       struct tty_struct *tty;

       unsigned char *data = urb->transfer_buffer;

       int result;

 

       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)

{

       struct usb_serial_port *port = tty->driver_data;

       int retval = -EINVAL;

 

       if (!port)

              goto exit;

 

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

 

       if (!port->open_count) {

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

              goto exit;

       }

 

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

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

 

exit:

       return retval;

}

这个函数,直接调用了usb_serial_driver的write函数.

Generic.c:

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

{

       struct usb_serial *serial = port->serial;

       int result;

       unsigned char *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__);

                     return 0;

              }

              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;

 

              return result;

       }

 

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

       return 0;

}

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

接着看这个回调函数:

Generic.c:

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

{

       struct usb_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)

{

       struct usb_serial_port *port = private;

       struct tty_struct *tty;

 

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

      

       if (!port)

              return;

 

       tty = port->tty;

       if (!tty)

              return;

 

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

}

其实很简单,

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

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


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、在win98、winme、win2000,windows XP系统中: 先双击driver里hidcominst程序,它没有任何显示。然后再插上USB线, 根据系统提示及可正确安装。 安装完成之后,进入设备管理器,在“端口”一栏中可以看到虚拟的串口设备 SemiTech USB-HID->COM device (COM X ) ,表示设备已经正确安装完成, 可以正常使用。 2、在WinXP系统中:有些可能失败安装HIDCOM driver 。双击driver里HidcomInst程序,就可以在设备管理器的"端口"一栏中可以看到虚拟的串口设备。semiTech.USB-HID->COM device (COM X ),表示设备已经正确安装完成,可以正常使用. 或者先插上USB线,进入设备管理器,在“人体学输入设备”一栏中可以看到 “HID-compliant Device”和“USB人体学输入设备” (在此之前请确认已经移去所有其他“USB人体学输入设备”), 在“HID-compliant Device”上点击鼠标右键: 选择:更新驱动程序 选择:从列表或指定位置安装,单击下一步, 选择:不要搜索,我要自己选择要安装的驱动程序.单击下一步, 选择:从磁盘安装,打开浏览,从驱动盘中找到hidcom.INF文件,打开,单击确定, 选择下一步,此时系统会提示微软的数字签证,选择仍然继续,单击完成。 在“USB人体学输入设备”上点击鼠标右键: 选择:更新驱动程序 选择:从列表或指定位置安装,单击下一步, 选择:不要搜索,我要自己选择要安装的驱动程序.单击下一步, 选择:从磁盘安装,打开浏览,从驱动盘中找到hidcom.INF文件,打开,单击确定, 选择下一步,此时系统会提示微软的数字签证,选择仍然继续,单击完成。 完成以上两项更新之后,在设备管理器的“端口”一栏中可以看到虚拟的串口设备 SemiTech USB-HID->COM device (COM X ) ,表示设备已经正确安装完成,可以正常使用。 3、如果在win98、winme、win2000系统安装过程中出现设备不能正常使用,请参看WinXP的 安装方法进行程序升级。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值