linux内核中串口驱动注册过程(tty驱动)

 

 

分类: kernel 160人阅读 评论(0) 收藏 举报

       最近闲来无事情做,想到以前项目中遇到串口硬件流控制的问题,蓝牙串口控制返回错误,上层读写串口buffer溢出的问题等,也折腾了一阵子,虽然最终证明与串口驱动无关,但是排查问题时候毫无疑问会查看串口驱动的相关代码,所以把串口驱动的流程过了一遍,方便以后再用到时拿来用。分析的是全志代码A20。直接开始代码分析吧。

串口驱动代码在linux-3.3/drivers/tty/serial目录下,全志把自己平台相关的代码集中到了一个文件中,叫sw_uart.c,那就从它的__init开始了:

 

[cpp] view plaincopy
 
  1. static int __init sw_uart_init(void)  
  2. {  
  3.     int ret;   
  4.     u32 i;  
  5.     struct sw_uart_pdata *pdata;  
  6.   
  7.     SERIAL_MSG("driver initializied\n");  
  8.     ret = sw_uart_get_devinfo();  
  9.     if (unlikely(ret))  
  10.         return ret;   
  11.   
  12.     ret = uart_register_driver(&sw_uart_driver);  
  13.     if (unlikely(ret)) {  
  14.         SERIAL_MSG("driver initializied\n");  
  15.         return ret;   
  16.     }      
  17.   
  18.     for (i=0; i<SW_UART_NR; i++) {  
  19.         pdata = &sw_uport_pdata[i];  
  20.         if (pdata->used)  
  21.             platform_device_register(&sw_uport_device[i]);  
  22.     }      
  23.   
  24.     return platform_driver_register(&sw_uport_platform_driver);  
  25. }  
sw_uart_get_devinfo是解析全志的sys配置脚本中的串口配置,一共有八个串口,要用到那个直接在sys脚本中设置1就行了,这个用过全志平台的都知道

 

接着sw_uart_driver结构体定义如下:

 

[cpp] view plaincopy
 
  1. static struct uart_driver sw_uart_driver = {                                                                                                                     
  2.     .owner = THIS_MODULE,  
  3.     .driver_name = "sw_serial",  
  4.     .dev_name = "ttyS",  
  5.     .nr = SW_UART_NR,  
  6.     .cons = SW_CONSOLE,  
  7. };  
这里SW_UART_NR为8,ttyS就是将要显示在/dev/目录下的名字了,从0~7

 

接着看uart注册函数,顾名思义是把全志自己平台的串口注册到串口核心serial_core中去:

 

[cpp] view plaincopy
 
  1. int uart_register_driver(struct uart_driver *drv)  
  2. {  
  3.     struct tty_driver *normal;  
  4.     int i, retval;  
  5.   
  6.     BUG_ON(drv->state);  
  7.   
  8.     /* 
  9.      * Maybe we should be using a slab cache for this, especially if 
  10.      * we have a large number of ports to handle. 
  11.      */  
  12.     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);  
  13.     if (!drv->state)  
  14.         goto out;  
  15.   
  16.     normal = alloc_tty_driver(drv->nr);  
  17.     if (!normal)  
  18.         goto out_kfree;  
  19.   
  20.     drv->tty_driver = normal;  
  21.   
  22.     normal->owner       = drv->owner;  
  23.     normal->driver_name = drv->driver_name;  
  24.     normal->name        = drv->dev_name;//名字为ttyS  
  25.     normal->major       = drv->major;  
  26.     normal->minor_start = drv->minor;  
  27.     normal->type        = TTY_DRIVER_TYPE_SERIAL;  
  28.     normal->subtype     = SERIAL_TYPE_NORMAL;  
  29.     normal->init_termios    = tty_std_termios;  
  30.     normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;                                                                                         
  31.     normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;  
  32.     normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;  
  33.     normal->driver_state    = drv;  
  34.     tty_set_operations(normal, &uart_ops);  
  35.     /* 
  36.      * Initialise the UART state(s). 
  37.      */  
  38.     for (i = 0; i < drv->nr; i++) {  
  39.         struct uart_state *state = drv->state + i;  
  40.         struct tty_port *port = &state->port;  
  41.   
  42.         tty_port_init(port);  
  43.         port->ops = &uart_port_ops;  
  44.         port->close_delay     = HZ / 2; /* .5 seconds */  
  45.         port->closing_wait    = 30 * HZ;/* 30 seconds */  
  46.     }  
  47.   
  48.     retval = tty_register_driver(normal);  
  49.     if (retval >= 0)  
  50.         return retval;  
  51.   
  52.     put_tty_driver(normal);  
  53. out_kfree:  
  54.     kfree(drv->state);  
  55. out:  
  56.     return -ENOMEM;  
  57. }  

先列出来吧,以用到时候再回来看,这里先创建了NR个state, 并为每个state做一些初始化,但是这些state还没有和端口(uart_port)对应起来;初始化完port口后,调用tty_register_driver:

 

 

[cpp] view plaincopy
 
  1. int tty_register_driver(struct tty_driver *driver)  
  2. {  
  3.     int error;  
  4.     int i;  
  5.     dev_t dev;  
  6.     void **p = NULL;  
  7.     struct device *d;  
  8.   
  9.     if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {  
  10.         p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);  
  11.         if (!p)  
  12.             return -ENOMEM;  
  13.     }  
  14.   
  15.     if (!driver->major) {  
  16.         error = alloc_chrdev_region(&dev, driver->minor_start,  
  17.                         driver->num, driver->name);  
  18.         if (!error) {  
  19.             driver->major = MAJOR(dev);  
  20.             driver->minor_start = MINOR(dev);  
  21.         }  
  22.     } else {  
  23.         dev = MKDEV(driver->major, driver->minor_start);  
  24.         error = register_chrdev_region(dev, driver->num, driver->name);  
  25.     }  
  26.     if (error < 0) {  
  27.         kfree(p);                                                                                                                                                
  28.         return error;  
  29.     }  
  30.   
  31.     if (p) {  
  32.         driver->ttys = (struct tty_struct **)p;  
  33.         driver->termios = (struct ktermios **)(p + driver->num);  
  34.     } else {  
  35.         driver->ttys = NULL;  
  36.         driver->termios = NULL;  
  37.     }  
  38.   
  39.     cdev_init(&driver->cdev, &tty_fops);  
  40.     driver->cdev.owner = driver->owner;  
  41.     error = cdev_add(&driver->cdev, dev, driver->num);  
  42.     if (error) {  
  43.         unregister_chrdev_region(dev, driver->num);  
  44.         driver->ttys = NULL;  
  45.         driver->termios = NULL;  
  46.         kfree(p);  
  47.         return error;  
  48.     }  
  49.   
  50.     mutex_lock(&tty_mutex);  
  51.     list_add(&driver->tty_drivers, &tty_drivers);  
  52.     mutex_unlock(&tty_mutex);  
  53.   
  54.     if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {  
  55.         for (i = 0; i < driver->num; i++) {  
  56.             d = tty_register_device(driver, i, NULL);  
  57.             if (IS_ERR(d)) {  
  58.                 error = PTR_ERR(d);  
  59.                 goto err;  
  60.             }  
  61.         }  
  62.     }  
  63.     proc_tty_register_driver(driver);  
  64.     driver->flags |= TTY_DRIVER_INSTALLED;  
  65.     return 0;  

这里alloc_chrdev_region是动态分配了主从设备号,接着cdev_init,它file_operations结构提和它关联了起来,以后我们open /dev/ttyS节点时候会调用他的open函数,先看看这个结构体:

 

 

[cpp] view plaincopy
 
  1. static const struct file_operations tty_fops = {                                                                                                                 
  2.     .llseek     = no_llseek,  
  3.     .read       = tty_read,  
  4.     .write      = tty_write,  
  5.     .poll       = tty_poll,  
  6.     .unlocked_ioctl = tty_ioctl,  
  7.     .compat_ioctl   = tty_compat_ioctl,  
  8.     .open       = tty_open,  
  9.     .release    = tty_release,  
  10.     .fasync     = tty_fasync,  
  11. };  

接着cdev_add就把file_operations和设备号关联起来了,我们现在还没有创建设备节点,不过看到有为driver->major,driver->minor_start赋值的,后面创建节点就是用这两个主从设备号。接着list_add把这个tty_driver添加到链表中来,方便后续查找。

 

 

接着if语句判断TTY_DRIVER_DYNAMIC_DEV标志,我们前面有赋值:

     normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;

所以这里的if条件不成立的,最后创建proc下的节点,就返回了。按我的理解,tty_register_driver是注册了一个tty的驱动,这个驱动有了逻辑能力,但是这个时候这个驱动还没有对应任何设备,所以后续还要添加对应的端口(也就是芯片的物理串口),并创建/dev/下的设备节点,上层用tty_driver驱动的逻辑来操作对应的端口。

 

回到sw_uart.c中,继续__init函数,platform_device_register函数,如果sys配置文件中那个串口配置了1,才会注册相应的平台设备;

接着platform_driver_register,看它的probe函数了:

 

[cpp] view plaincopy
 
  1. static int __devinit sw_uart_probe(struct platform_device *pdev)  
  2. {  
  3.     u32 id = pdev->id;  
  4.     struct uart_port *port;  
  5.     struct sw_uart_port *sw_uport;  
  6.     struct clk *apbclk;  
  7.     int ret = -1;  
  8.   
  9.     if (unlikely(pdev->id < 0 || pdev->id >= SW_UART_NR))  
  10.         return -ENXIO;  
  11.     port = &sw_uart_port[id].port;  
  12.     port->dev = &pdev->dev;  
  13.     sw_uport = UART_TO_SPORT(port);  
  14.     sw_uport->id = id;  
  15.     sw_uport->ier = 0;  
  16.     sw_uport->lcr = 0;  
  17.     sw_uport->mcr = 0;  
  18.     sw_uport->fcr = 0;  
  19.     sw_uport->dll = 0;  
  20.     sw_uport->dlh = 0;  
  21.   
  22.     /* request system resource and init them */  
  23.     ret = sw_uart_request_resource(sw_uport);  
  24.     if (unlikely(ret)) {                                                                                                                                         
  25.         SERIAL_MSG("uart%d error to get resource\n", id);  
  26.         return -ENXIO;  
  27.     }  
  28.   
  29.     apbclk = clk_get(&pdev->dev, CLK_SYS_APB1);  
  30.     if (IS_ERR(apbclk)) {  
  31.         SERIAL_MSG("uart%d error to get source clock\n", id);  
  32.         return -ENXIO;  
  33.     }  
  34.     ret = clk_set_parent(sw_uport->mclk, apbclk);  
  35.     if (ret) {  
  36.         SERIAL_MSG("uart%d set mclk parent error\n", id);  
  37.         clk_put(apbclk);  
  38.         return -ENXIO;  
  39.     }  
  40.     port->uartclk = clk_get_rate(apbclk);  
  41.     clk_put(apbclk);  
  42.   
  43.     port->type = PORT_SW;  
  44.     port->flags = UPF_BOOT_AUTOCONF;  
  45.     port->mapbase = sw_uport->pdata->base;  
  46.     port->irq = sw_uport->pdata->irq;  
  47.     platform_set_drvdata(pdev, port);  
  48. #ifdef CONFIG_PROC_FS  
  49.     sw_uart_procfs_attach(sw_uport);  
  50. #endif  
  51.     SERIAL_DBG("add uart%d port, port_type %d, uartclk %d\n",  
  52.             id, port->type, port->uartclk);  
  53.     return uart_add_one_port(&sw_uart_driver, port);  
  54. }  
sw_uart_request_resource是申请配置GPIO,接着uart_add_one_port:

 

 

[cpp] view plaincopy
 
  1. int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)  
  2. {  
  3.     struct uart_state *state;  
  4.     struct tty_port *port;  
  5.     int ret = 0;   
  6.     struct device *tty_dev;  
  7.   
  8.     BUG_ON(in_interrupt());  
  9.   
  10.     if (uport->line >= drv->nr)  
  11.         return -EINVAL;  
  12.   
  13.     state = drv->state + uport->line;//state是在函数 uart_register_driver中kmalloc初始化  
  14.     port = &state->port;  
  15.   
  16.     mutex_lock(&port_mutex);  
  17.     mutex_lock(&port->mutex);  
  18.     if (state->uart_port) {  
  19.         ret = -EINVAL;  
  20.         goto out;   
  21.     }      
  22.   
  23.     state->uart_port = uport;  
  24.     state->pm_state = -1;  
  25.                                                                                                                                                                  
  26.     uport->cons = drv->cons;  
  27.     uport->state = state;  
  28.   
  29.     /*    
  30.      * If this port is a console, then the spinlock is already 
  31.      * initialised. 
  32.      */  
  33.     if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {  
  34.         spin_lock_init(&uport->lock);  
  35.         lockdep_set_class(&uport->lock, &port_lock_key);  
  36.     }      
  37.   
  38.     uart_configure_port(drv, state, uport);  
  39.   
  40.     /*    
  41.      * Register the port whether it's detected or not.  This allows 
  42.      * setserial to be used to alter this ports parameters. 
  43.      */  
  44.     tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);  
  45.     if (likely(!IS_ERR(tty_dev))) {  
  46.         device_set_wakeup_capable(tty_dev, 1);  
  47.     } else {  
  48.         printk(KERN_ERR "Cannot register tty device on line %d\n",  
  49.                uport->line);  
  50.     }  
  51.   
  52.     /* 
  53.      * Ensure UPF_DEAD is not set. 
  54.      */  
  55.     uport->flags &= ~UPF_DEAD;  
  56.   
  57.  out:  
  58.     mutex_unlock(&port->mutex);  
  59.     mutex_unlock(&port_mutex);  
  60.   
  61.     return ret;  
  62. }  

这个函数看名字就猜到是为uart_driver添加端口,前面说过state的状态还没有和uart_port对应起来,那么这里state->uart_port = uport就对应了,port的配置就不去关心它了,这样,uart_driver就可以通过port对ops结构体的操作函数控制底层了,最后调用tty_register_device:

 

 

[cpp] view plaincopy
 
  1. struct device *tty_register_device(struct tty_driver *driver, unsigned index,  
  2.                    struct device *device)  
  3. {     
  4.     char name[64];  
  5.     dev_t dev = MKDEV(driver->major, driver->minor_start) + index;  
  6.   
  7.     if (index >= driver->num) {  
  8.         printk(KERN_ERR "Attempt to register invalid tty line number "  
  9.                " (%d).\n", index);  
  10.         return ERR_PTR(-EINVAL);  
  11.     }     
  12.       
  13.     if (driver->type == TTY_DRIVER_TYPE_PTY)  
  14.         pty_line_name(driver, index, name);  
  15.     else  
  16.         tty_line_name(driver, index, name);  
  17.   
  18.     return device_create(tty_class, device, dev, NULL, name);  
  19. }  

这里的tty_line_name函数定义如下:

 

 

[cpp] view plaincopy
 
  1. static void tty_line_name(struct tty_driver *driver, int index, char *p)                                                                                         
  2. {  
  3.     sprintf(p, "%s%d", driver->name, index + driver->name_base);//名字在uart_register_driver中赋值  
  4. }  
可以看到就是前面说的名字ttyS0~ttyS7。

 

这样,如果上层open节点,会调用到前面的file_operations结构体函数tty_open,这是tty核心层的调用:

 

[cpp] view plaincopy
 
  1. static int tty_open(struct inode *inode, struct file *filp)  
  2. {  
  3.     struct tty_struct *tty;  
  4.     int noctty, retval;  
  5.     struct tty_driver *driver = NULL;  
  6.     int index;  
  7.     dev_t device = inode->i_rdev;  
  8.     unsigned saved_flags = filp->f_flags;  
  9.   
  10.     nonseekable_open(inode, filp);  
  11.   
  12. retry_open:  
  13.     retval = tty_alloc_file(filp);  
  14.     if (retval)  
  15.         return -ENOMEM;  
  16.   
  17.     noctty = filp->f_flags & O_NOCTTY;  
  18.     index  = -1;  
  19.     retval = 0;  
  20.   
  21.     mutex_lock(&tty_mutex);  
  22.     tty_lock();  
  23.   
  24.     tty = tty_open_current_tty(device, filp);  
  25.   
  26.      ...................................  
  27.   
  28.     if (tty->ops->open)  
  29.         retval = tty->ops->open(tty, filp);  
  30.     else  
  31.   
  32.     ........................................  
  33.   
  34.             return retval;  
  35.   
  36.         schedule();  
  37.         /* 
  38.          * Need to reset f_op in case a hangup happened. 
  39.          */  
  40.         tty_lock();  
  41.         if (filp->f_op == &hung_up_tty_fops)  
  42.             filp->f_op = &tty_fops;                                                                                                                              
  43.         tty_unlock();  
  44.         goto retry_open;  
  45.      .................................  

可以看到,首先查找tty_driver链表,找到前面添加的tty_driver,然后调用他的ops->open函数,这个ops赋值是在前面的uart_register_driver函数中:

 

 

[cpp] view plaincopy
 
  1. tty_set_operations(normal, &uart_ops);  

所以进入uart_ops结构体的open函数,这里就是从tty核心转到serial核心,往下走了一层:

 

 

[cpp] view plaincopy
 
  1. static int uart_open(struct tty_struct *tty, struct file *filp)  
  2. {  
  3.     struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;  
  4.     int retval, line = tty->index;  
  5.     struct uart_state *state = drv->state + line;  
  6.     struct tty_port *port = &state->port;  
  7.        
  8.       ................................  
  9.   
  10.     /* 
  11.      * Start up the serial port. 
  12.      */  
  13.     retval = uart_startup(tty, state, 0);  
  14.   
  15.     .....................................  
  16.    

uart_startup:

 

 

[cpp] view plaincopy
 
  1. static int uart_startup(struct tty_struct *tty, struct uart_state *state,                                                                                        
  2.         int init_hw)  
  3. {  
  4.     struct tty_port *port = &state->port;  
  5.     int retval;  
  6.   
  7.     if (port->flags & ASYNC_INITIALIZED)  
  8.         return 0;  
  9.   
  10.     /* 
  11.      * Set the TTY IO error marker - we will only clear this 
  12.      * once we have successfully opened the port. 
  13.      */  
  14.     set_bit(TTY_IO_ERROR, &tty->flags);  
  15.   
  16.     retval = uart_port_startup(tty, state, init_hw);  
  17.     if (!retval) {  
  18.         set_bit(ASYNCB_INITIALIZED, &port->flags);  
  19.         clear_bit(TTY_IO_ERROR, &tty->flags);  
  20.     } else if (retval > 0)  
  21.         retval = 0;  
  22.   
  23.     return retval;  
  24. }  

uart_port_startup:

 

 

[cpp] view plaincopy
 
  1. static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,                                                                                   
  2.         int init_hw)  
  3. {  
  4.     struct uart_port *uport = state->uart_port;  
  5.     struct tty_port *port = &state->port;  
  6.     unsigned long page;  
  7.     int retval = 0;  
  8.     retval = uport->ops->startup(uport);  
  9.     if (retval == 0) {  
  10.         if (uart_console(uport) && uport->cons->cflag) {  
  11.             tty->termios->c_cflag = uport->cons->cflag;  
  12.             uport->cons->cflag = 0;  
  13.         }  
  14.         /* 
  15.          * Initialise the hardware port settings. 
  16.          */  
  17.         uart_change_speed(tty, state, NULL);  
  18.   
  19.         if (init_hw) {  
  20.             /* 
  21.              * Setup the RTS and DTR signals once the 
  22.              * port is open and ready to respond. 
  23.              */  
  24.             if (tty->termios->c_cflag & CBAUD)  
  25.                 uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);  
  26.         }  
  27.   
  28.         if (port->flags & ASYNC_CTS_FLOW) {  
  29.             spin_lock_irq(&uport->lock);  
  30.             if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))  
  31.                 tty->hw_stopped = 1;  
  32.             spin_unlock_irq(&uport->lock);  
  33.         }  
  34.     }  
  35.   
  36.    
可以看到,最终调用了 uport->ops->startup,这样就从serical核心层往下走到了平台的串口驱动层,也就是最底层的驱动了,这个函数定义在sw_uart.c的sw_uart_port结构体中:

 

 

[cpp] view plaincopy
 
  1. static struct sw_uart_port sw_uart_port[] = {                                                                                                                    
  2.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 0, },  
  3.       .pdata = &sw_uport_pdata[0], },  
  4.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 1, },  
  5.       .pdata = &sw_uport_pdata[1], },  
  6.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 2, },  
  7.       .pdata = &sw_uport_pdata[2], },  
  8.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 3, },  
  9.       .pdata = &sw_uport_pdata[3], },  
  10.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 4, },  
  11.       .pdata = &sw_uport_pdata[4], },  
  12.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 5, },  
  13.       .pdata = &sw_uport_pdata[5], },  
  14.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 6, },  
  15.       .pdata = &sw_uport_pdata[6], },  
  16.     { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 7, },  
  17.       .pdata = &sw_uport_pdata[7], },  
  18. };  

看他的.startup函数:

 

 

[cpp] view plaincopy
 
  1. static int sw_uart_startup(struct uart_port *port)                                                                                                               
  2. {  
  3.     struct sw_uart_port *sw_uport = UART_TO_SPORT(port);  
  4.     int ret;  
  5.   
  6.     SERIAL_DBG("start up ...\n");  
  7.     snprintf(sw_uport->name, sizeof(sw_uport->name),  
  8.          "sw_serial%d", port->line);  
  9.     ret = request_irq(port->irq, sw_uart_irq, 0, sw_uport->name, port);  
  10.     if (unlikely(ret)) {  
  11.         SERIAL_MSG("uart%d cannot get irq %d\n", sw_uport->id, port->irq);  
  12.         return ret;  
  13.     }  
  14.   
  15.     sw_uport->msr_saved_flags = 0;  
  16.     /* 
  17.      * PTIME mode to select the THRE trigger condition: 
  18.      * if PTIME=1(IER[7]), the THRE interrupt will be generated when the 
  19.      * the water level of the TX FIFO is lower than the threshold of the 
  20.      * TX FIFO. and if PTIME=0, the THRE interrupt will be generated when 
  21.      * the TX FIFO is empty. 
  22.      * In addition, when PTIME=1, the THRE bit of the LSR register will not 
  23.      * be set when the THRE interrupt is generated. You must check the 
  24.      * interrupt id of the IIR register to decide whether some data need to 
  25.      * send. 
  26.      */  
  27.     sw_uport->ier = SW_UART_IER_RLSI | SW_UART_IER_RDI;  
  28.     #ifdef CONFIG_SW_UART_PTIME_MODE  
  29.     sw_uport->ier |= SW_UART_IER_PTIME;  
  30.     #endif  
  31.   
  32.     return 0;  
  33. }  

这里最终就是初始化ARM芯片的寄存器操作了,可以看到申请了中断函数,后续的读写操作就和中断服务函数密切相关了。

 

接着open之后,看看上层的write函数调用过程,首先调用了tty核心层的write:

 

[cpp] view plaincopy
 
  1. static ssize_t tty_write(struct file *file, const char __user *buf,                                                                                              
  2.                         size_t count, loff_t *ppos)  
  3. {  
  4.     struct inode *inode = file->f_path.dentry->d_inode;  
  5.     struct tty_struct *tty = file_tty(file);  
  6.     struct tty_ldisc *ld;  
  7.     ssize_t ret;  
  8.   
  9.     if (tty_paranoia_check(tty, inode, "tty_write"))  
  10.         return -EIO;  
  11.     if (!tty || !tty->ops->write ||  
  12.         (test_bit(TTY_IO_ERROR, &tty->flags)))  
  13.             return -EIO;  
  14.     /* Short term debug to catch buggy drivers */  
  15.     if (tty->ops->write_room == NULL)  
  16.         printk(KERN_ERR "tty driver %s lacks a write_room method.\n",  
  17.             tty->driver->name);  
  18.     ld = tty_ldisc_ref_wait(tty);  
  19.     if (!ld->ops->write)  
  20.         ret = -EIO;  
  21.     else  
  22.         ret = do_tty_write(ld->ops->write, tty, file, buf, count);  
  23.     tty_ldisc_deref(ld);  
  24.     return ret;  
  25. }  

看这里do_tty_write函数:

 

[cpp] view plaincopy
 
  1. static inline ssize_t do_tty_write(  
  2.     ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),  
  3.     struct tty_struct *tty,  
  4.     struct file *file,  
  5.     const char __user *buf,  
  6.     size_t count)  
  7. {  
  8.       
  9.     ............................  
  10.   
  11.     for (;;) {  
  12.         size_t size = count;  
  13.         if (size > chunk)  
  14.             size = chunk;  
  15.         ret = -EFAULT;  
  16.         if (copy_from_user(tty->write_buf, buf, size))  
  17.             break;  
  18.         ret = write(tty, file, tty->write_buf, size);  
  19.         if (ret <= 0)  
  20.             break;  
  21.   
  22.     ...............................  

copy_from_user就把要写的数据拷贝到了内核空间的write_buf中来,接着write是函数指针,指向ld->ops->write。

 

这里的ld->ops指向的是n_tty.c 中结构体:

 

 

[cpp] view plaincopy
 
  1. struct tty_ldisc_ops tty_ldisc_N_TTY = {  
  2.     .magic           = TTY_LDISC_MAGIC,  
  3.     .name            = "n_tty",  
  4.     .open            = n_tty_open,  
  5.     .close           = n_tty_close,  
  6.     .flush_buffer    = n_tty_flush_buffer,  
  7.     .chars_in_buffer = n_tty_chars_in_buffer,  
  8.     .read            = n_tty_read,  
  9.     .write           = n_tty_write,                                                                                                                              
  10.     .ioctl           = n_tty_ioctl,  
  11.     .set_termios     = n_tty_set_termios,  
  12.     .poll            = n_tty_poll,  
  13.     .receive_buf     = n_tty_receive_buf,  
  14.     .write_wakeup    = n_tty_write_wakeup  
  15. };  
所以调用了调用的是线路规程的write:

 

 

[cpp] view plaincopy
 
  1. static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,  
  2.                const unsigned char *buf, size_t nr)  
  3. {   
  4.     ..........................  
  5.                 b++; nr--;  
  6.             }  
  7.             if (tty->ops->flush_chars)  
  8.                 tty->ops->flush_chars(tty);  
  9.         } else {  
  10.             while (nr > 0) {  
  11.                 c = tty->ops->write(tty, b, nr);  
  12.                 if (c < 0) {  
  13.                     retval = c;  
  14.                     goto break_out;  
  15.                 }  
  16.                 if (!c)  
  17.                     break;  
  18.                 b += c;  
  19.                 nr -= c;  
  20.             }  
  21.         }  
  22.     ............................  

从线路规程转到tty驱动层的write:

 

 

[cpp] view plaincopy
 
  1. static int uart_write(struct tty_struct *tty,  
  2.                     const unsigned char *buf, int count)  
  3. {  
  4.   
  5.     ......................  
  6.   
  7.     if (!circ->buf)  
  8.         return 0;  
  9.   
  10.     spin_lock_irqsave(&port->lock, flags);  
  11.     while (1) {  
  12.         c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);  
  13.         if (count < c)   
  14.             c = count;  
  15.         if (c <= 0)  
  16.             break;  
  17.         memcpy(circ->buf + circ->head, buf, c);  
  18.         circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);  
  19.         buf += c;  
  20.         count -= c;  
  21.         ret += c;  
  22.     }  
  23.     spin_unlock_irqrestore(&port->lock, flags);  
  24.   
  25.     uart_start(tty);  
  26.     return ret;  
  27. }  

可以看到,把数据memcpy到环形队列中来,这样数据就保存到了该端口对应的state的xmit的buf中,这是一个环形队列。接着调用uart_start:

 

 

[cpp] view plaincopy
 
  1. static void uart_start(struct tty_struct *tty)  
  2. {  
  3.     struct uart_state *state = tty->driver_data;  
  4.     struct uart_port *port = state->uart_port;  
  5.     unsigned long flags;  
  6.   
  7.     spin_lock_irqsave(&port->lock, flags);  
  8.     __uart_start(tty);  
  9.     spin_unlock_irqrestore(&port->lock, flags);  
  10. }  
看来要开始传输数据了,所以对这个操作加锁了:

 

 

[cpp] view plaincopy
 
  1. static void __uart_start(struct tty_struct *tty)                                                                                                                 
  2. {  
  3.     struct uart_state *state = tty->driver_data;  
  4.     struct uart_port *port = state->uart_port;  
  5.   
  6.     if (port->ops->wake_peer)  
  7.         port->ops->wake_peer(port);  
  8.   
  9.     if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&  
  10.         !tty->stopped && !tty->hw_stopped)  
  11.         port->ops->start_tx(port);  
  12. }  

最终调用驱动层的操作函数,也就是该端口对应的传输函数来触发数据发送:

 

 

[cpp] view plaincopy
 
  1. static void sw_uart_start_tx(struct uart_port *port)                                                                                                             
  2. {  
  3.     struct sw_uart_port *sw_uport = UART_TO_SPORT(port);  
  4.   
  5.     if (!(sw_uport->ier & SW_UART_IER_THRI)) {  
  6.         sw_uport->ier |= SW_UART_IER_THRI;  
  7.         SERIAL_DBG("start tx, ier %x\n", sw_uport->ier);  
  8.         serial_out(port, sw_uport->ier, SW_UART_IER);  
  9.     }      
  10. }  

serial_out函数对应的就是最底层的寄存器操作了,这里#define SW_UART_IER (0x04),SW_UART_IER_THRI它对应的是使能中断,具体要参考全志的A20 CPU手册:

 

 

[cpp] view plaincopy
 
  1. static inline void serial_out(struct uart_port *port, unsigned char value, int offs)                                                                             
  2. {  
  3.     __raw_writeb(value, port->membase + offs);  
  4. }  
把配置写到了寄存器中,使能中断后,中断服务函数就自动把buf的数据发送出去了,前面分析看到数据是memcpy到了该端口对应的state的circ_buf结构体中的,所以进入前面open时候分析的中断服务函数中去:

 

 

[cpp] view plaincopy
 
  1. static irqreturn_t sw_uart_irq(int irq, void *dev_id)  
  2. {  
  3.     struct uart_port *port = dev_id;  
  4.     struct sw_uart_port *sw_uport = UART_TO_SPORT(port);  
  5.     unsigned int iir = 0, lsr = 0;  
  6.     unsigned long flags;  
  7.   
  8.     spin_lock_irqsave(&port->lock, flags);  
  9.   
  10.     iir = serial_in(port, SW_UART_IIR) & SW_UART_IIR_IID_MASK;                                                                                                   
  11.     lsr = serial_in(port, SW_UART_LSR);  
  12.     SERIAL_DBG("irq: iir %x lsr %x\n", iir, lsr);  
  13.   
  14.     if (iir == SW_UART_IIR_IID_BUSBSY) {  
  15.         /* handle busy */  
  16. //      SERIAL_MSG("uart%d busy...\n", sw_uport->id);  
  17.         serial_in(port, SW_UART_USR);  
  18.   
  19.         #ifdef CONFIG_SW_UART_FORCE_LCR  
  20.         sw_uart_force_lcr(sw_uport, 10);  
  21.         #else  
  22.         serial_out(port, sw_uport->lcr, SW_UART_LCR);  
  23.         #endif  
  24.     } else {  
  25.         if (lsr & (SW_UART_LSR_DR | SW_UART_LSR_BI))  
  26.             lsr = sw_uart_handle_rx(sw_uport, lsr);  
  27.         sw_uart_modem_status(sw_uport);  
  28.         #ifdef CONFIG_SW_UART_PTIME_MODE  
  29.         if (iir == SW_UART_IIR_IID_THREMP)  
  30.         #else  
  31.         if (lsr & SW_UART_LSR_THRE)  
  32.         #endif  
  33.             sw_uart_handle_tx(sw_uport);  
  34.     }  
  35.   
  36.     spin_unlock_irqrestore(&port->lock, flags);  
  37.   
  38.     return IRQ_HANDLED;  
  39. }  

我们要做的是发送操作,所以进入sw_uart_handle_tx:

 

 

[cpp] view plaincopy
 
  1. static void sw_uart_handle_tx(struct sw_uart_port *sw_uport)  
  2. {  
  3.     struct circ_buf *xmit = &sw_uport->port.state->xmit;  
  4.     int count;  
  5.   
  6.     if (sw_uport->port.x_char) {                                                                                                                                 
  7.         serial_out(&sw_uport->port, sw_uport->port.x_char, SW_UART_THR);  
  8.         sw_uport->port.icount.tx++;  
  9.         sw_uport->port.x_char = 0;  
  10. #ifdef CONFIG_SW_UART_DUMP_DATA  
  11.         sw_uport->dump_buff[sw_uport->dump_len++] = sw_uport->port.x_char;  
  12.         SERIAL_DUMP(sw_uport, "Tx");  
  13. #endif  
  14.         return;  
  15.     }  
  16.     if (uart_circ_empty(xmit) || uart_tx_stopped(&sw_uport->port)) {  
  17.         sw_uart_stop_tx(&sw_uport->port);  
  18.         return;  
  19.     }  
  20.     count = sw_uport->port.fifosize / 2;  
  21.     do {  
  22. #ifdef CONFIG_SW_UART_DUMP_DATA  
  23.         sw_uport->dump_buff[sw_uport->dump_len++] = xmit->buf[xmit->tail];  
  24. #endif  
  25.         serial_out(&sw_uport->port, xmit->buf[xmit->tail], SW_UART_THR);  
  26.         xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);  
  27.         sw_uport->port.icount.tx++;  
  28.         if (uart_circ_empty(xmit)) {  
  29.             break;  
  30.         }  
  31.     } while (--count > 0);  
  32.   
  33.     SERIAL_DUMP(sw_uport, "Tx");  
  34.     if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {  
  35.         spin_unlock(&sw_uport->port.lock);  
  36.         uart_write_wakeup(&sw_uport->port);  
  37.         spin_lock(&sw_uport->port.lock);  
  38.     }  
  39.     ..........................  
  40.    
看到了,serial_out果然是取出circ_buf的buf数据,在do{}while语句中完成发送:

 

 

[cpp] view plaincopy
 
  1. static inline void serial_out(struct uart_port *port, unsigned char value, int offs)                                                                             
  2. {  
  3.     __raw_writeb(value, port->membase + offs);  
  4. }  
这样就把发送的数据写到了相应的寄存器中,硬件会自动完成数据发送操作

 

 

而上层read操作时候调用和write差不多,不同之处在于read是读取一个环形buf的数据,因为数据到了会产生中断,是中断服务函数自动接收数据并把它存储在buf中的;而前面写的时候是主动把数据写到buf去,所以先从中断服务函数中看是如何接收输入的:

 

[cpp] view plaincopy
 
  1. static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr)  
  2. {  
  3.     struct tty_struct *tty = sw_uport->port.state->port.tty;  
  4.     unsigned char ch = 0;  
  5.     int max_count = 256;  
  6.     char flag;  
  7.   
  8.     do {  
  9.         if (likely(lsr & SW_UART_LSR_DR)) {  
  10.             ch = serial_in(&sw_uport->port, SW_UART_RBR);  
  11.   
  12.        ..........................  
  13.   
  14.         if (uart_handle_sysrq_char(&sw_uport->port, ch))  
  15.             goto ignore_char;  
  16.         uart_insert_char(&sw_uport->port, lsr, SW_UART_LSR_OE, ch, flag);  
  17.   
  18.        ............................  
  19.   
  20.    

从寄存器读取字符后赋值给ch, 接着uart_insert_char来处理该字符,其实就是把这个数据放到uart层去:

 

[cpp] view plaincopy
 
  1. void uart_insert_char(struct uart_port *port, unsigned int status,  
  2.          unsigned int overrun, unsigned int ch, unsigned int flag)  
  3. {  
  4.     struct tty_struct *tty = port->state->port.tty;  
  5.   
  6.     if ((status & port->ignore_status_mask & ~overrun) == 0)  
  7.         tty_insert_flip_char(tty, ch, flag);  
  8.   
  9.     /* 
  10.      * Overrun is special.  Since it's reported immediately, 
  11.      * it doesn't affect the current character. 
  12.      */  
  13.     if (status & ~port->ignore_status_mask & overrun)  
  14.         tty_insert_flip_char(tty, 0, TTY_OVERRUN);  
  15. }  

[cpp] view plaincopy
 
  1. static inline int tty_insert_flip_char(struct tty_struct *tty,                                                                                                   
  2.                     unsigned char ch, char flag)  
  3. {  
  4.     struct tty_buffer *tb = tty->buf.tail;  
  5.     if (tb && tb->used < tb->size) {  
  6.         tb->flag_buf_ptr[tb->used] = flag;  
  7.         tb->char_buf_ptr[tb->used++] = ch;  
  8.         return 1;  
  9.     }  
  10.     return tty_insert_flip_string_flags(tty, &ch, &flag, 1);  
  11. }  
当前的tty_buffer空间不够时调用tty_insert_flip_string_flags,在这个函数里会去查找下一个tty_buffer,并将数据放到下一个tty_buffer的char_buf_ptr里。
这里char_buf_ptr的数据是如何放到线路规程的read_buf中的呢?那是在tty open操作的时候,tty_init_dev -> initialize_tty_struct -> initialize_tty_struct -> tty_buffer_init:

 

 

[cpp] view plaincopy
 
  1. void tty_buffer_init(struct tty_struct *tty)                                                                                                                     
  2. {  
  3.     spin_lock_init(&tty->buf.lock);  
  4.     tty->buf.head = NULL;  
  5.     tty->buf.tail = NULL;  
  6.     tty->buf.free = NULL;  
  7.     tty->buf.memory_used = 0;  
  8.     INIT_WORK(&tty->buf.work, flush_to_ldisc);  
  9. }  
可以看到初始化了工作队列的,而调用工作队列的时机是在这里操作完成后,继续sw_uart_handle_rx函数的tty_flip_buffer_push时候:

 

 

[cpp] view plaincopy
 
  1. void tty_flip_buffer_push(struct tty_struct *tty)  
  2. {  
  3.     unsigned long flags;  
  4.     spin_lock_irqsave(&tty->buf.lock, flags);  
  5.     if (tty->buf.tail != NULL)  
  6.         tty->buf.tail->commit = tty->buf.tail->used;  
  7.     spin_unlock_irqrestore(&tty->buf.lock, flags);  
  8.   
  9.     if (tty->low_latency)  
  10.         flush_to_ldisc(&tty->buf.work);  
  11.     else  
  12.         schedule_work(&tty->buf.work);  
  13. }  
  14. EXPORT_SYMBOL(tty_flip_buffer_push);   

这里就有两种方法把数据上报给链路规程层,其实都差不多,这样数据就上报到了链路规程中,看看这个工作队列函数:

 

 

[cpp] view plaincopy
 
  1. static void flush_to_ldisc(struct work_struct *work)  
  2. {  
  3.     ..........................  
  4.   
  5.                 count = tty->receive_room;  
  6.             char_buf = head->char_buf_ptr + head->read;  
  7.             flag_buf = head->flag_buf_ptr + head->read;  
  8.             head->read += count;  
  9.             spin_unlock_irqrestore(&tty->buf.lock, flags);  
  10.             disc->ops->receive_buf(tty, char_buf,  
  11.                             flag_buf, count);  
  12.   
  13.     ............................  

链路规程的receive_buf函数:

 

 

[cpp] view plaincopy
 
  1. static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,                                                                                   
  2.                   char *fp, int count)  
  3. {  
  4.     const unsigned char *p;  
  5.     char *f, flags = TTY_NORMAL;  
  6.     int i;  
  7.     char    buf[64];  
  8.     unsigned long cpuflags;  
  9.   
  10.     if (!tty->read_buf)  
  11.         return;  
  12.   
  13.     ................................  
  14.   
  15.         memcpy(tty->read_buf + tty->read_head, cp, i);  
  16.         tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);  
  17.         tty->read_cnt += i;  
  18.   
  19.   
  20.     ...........................  
  21.   
  22.         }  
  23.         if (tty->ops->flush_chars)  
  24.             tty->ops->flush_chars(tty);  
  25.     }  
  26.   
  27.     n_tty_set_room(tty);  

可以看到,很明显,memcpy将数据拷贝到了read_buf中。

 


现在,再回头从上层看read函数是如何读取数据的,流程也是tty核心 ->链路规程 :

 

[cpp] view plaincopy
 
  1. static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,  
  2.              unsigned char __user *buf, size_t nr)  
  3. {  
  4.     unsigned char __user *b = buf;   
  5.   
  6.     .......................................  
  7.   
  8.                 c = tty->read_buf[tty->read_tail];  
  9.   
  10.     ...............................  
  11.   
  12.             uncopied = copy_from_read_buf(tty, &b, &nr);  
  13.             uncopied += copy_from_read_buf(tty, &b, &nr);  
  14.   
  15.     ..............................  
  16.    

[cpp] view plaincopy
 
  1. static int copy_from_read_buf(struct tty_struct *tty,  
  2.                       unsigned char __user **b,  
  3.                       size_t *nr)  
  4.   
  5. {  
  6.     int retval;  
  7.     size_t n;  
  8.     unsigned long flags;  
  9.   
  10.     retval = 0;  
  11.     spin_lock_irqsave(&tty->read_lock, flags);  
  12.     n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);  
  13.     n = min(*nr, n);  
  14.     spin_unlock_irqrestore(&tty->read_lock, flags);  
  15.     if (n) {  
  16.         retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);                                                                                            
  17.         n -= retval;  
  18.         tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);  
  19.         spin_lock_irqsave(&tty->read_lock, flags);  
  20.         tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);  
  21.         tty->read_cnt -= n;  
  22.         /* Turn single EOF into zero-length read */  
  23.         if (L_EXTPROC(tty) && tty->icanon && n == 1) {  
  24.             if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))  
  25.                 n--;  
  26.         }  
  27.         spin_unlock_irqrestore(&tty->read_lock, flags);  
  28.         *b += n;  
  29.         *nr -= n;  
  30.     }  
  31.     return retval;  
  32. }   

看到了copy_to_user函数,就是把read_buf的数据拷贝到了用户空间。

 

到这里,串口的读写流程就很清楚,一目了然了

posted on 2013-09-12 18:36  Nickleback  阅读(511)  评论(0)    收藏  举报