UART驱动分析

在linux用户层上要操作底层串口需要对/dev/ttySxxx操作,这里的ttySx指实际的终端串口。

以下以全志A64为实例,分析UART驱动以及浅谈TTY架构。

linux-3.10/drivers/tty/serial/sunxi-uart.c:

 1 static const struct of_device_id sunxi_uart_match[] = {
 2     { .compatible = "allwinner,sun8i-uart", },
 3     { .compatible = "allwinner,sun50i-uart", },
 4     {},
 5 };
 6 MODULE_DEVICE_TABLE(of, sunxi_uart_match);        //设备树查找device并注册
 7 
 8 static struct uart_driver sw_uart_driver = {
 9     .owner = THIS_MODULE,
10     .driver_name = SUNXI_UART_DEV_NAME,    //"uart"
11     .dev_name = "ttyS",
12     .nr = SUNXI_UART_NUM,    //6
13     .cons = SW_CONSOLE,    
14 };
15 
16 static int __init sunxi_uart_init(void)
17 {
18     int ret;
19 
20     ret = uart_register_driver(&sw_uart_driver);        //注册tty_driver
21     if (unlikely(ret)) {
22         SERIAL_MSG("driver initializied\n");
23         return ret;
24     }
25 
26     return platform_driver_register(&sw_uport_platform_driver);        //硬件平台相关相关进入probe
27 }

 

先看看注册tty_driver里面做了什么操作,删减部分代码linux-3.10/drivers/tty/serial/serial_core.c:

 1 static const struct tty_operations uart_ops = {
 2     .open        = uart_open,
 3     .close        = uart_close,
 4     .write        = uart_write,
 5     .put_char    = uart_put_char,
 6     .flush_chars    = uart_flush_chars,
 7     .write_room    = uart_write_room,
 8     .chars_in_buffer = uart_chars_in_buffer,
 9     .flush_buffer    = uart_flush_buffer,
10     .ioctl        = uart_ioctl,
11     .throttle    = uart_throttle,
12     .unthrottle    = uart_unthrottle,
13     .send_xchar    = uart_send_xchar,
14     .set_termios    = uart_set_termios,
15     .set_ldisc    = uart_set_ldisc,
16     .stop        = uart_stop,
17     .start        = uart_start,
18     .hangup        = uart_hangup,
19     .break_ctl    = uart_break_ctl,
20     .wait_until_sent = uart_wait_until_sent,
21 #ifdef CONFIG_PROC_FS
22     .proc_fops    = &uart_proc_fops,
23 #endif
24     .tiocmget    = uart_tiocmget,
25     .tiocmset    = uart_tiocmset,
26     .get_icount    = uart_get_icount,
27 #ifdef CONFIG_CONSOLE_POLL
28     .poll_init    = uart_poll_init,
29     .poll_get_char    = uart_poll_get_char,
30     .poll_put_char    = uart_poll_put_char,
31 #endif
32 };
33 
34 int uart_register_driver(struct uart_driver *drv)
35 {
36     struct tty_driver *normal;
37     int i, retval;
38 
39     drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
40 
41     normal = alloc_tty_driver(drv->nr);
42 
43     drv->tty_driver = normal;
44 
45     normal->driver_name    = drv->driver_name;
46     normal->name        = drv->dev_name;
47     normal->major        = drv->major;
48     normal->minor_start    = drv->minor;
49     normal->type        = TTY_DRIVER_TYPE_SERIAL;
50     normal->subtype        = SERIAL_TYPE_NORMAL;
51     normal->init_termios    = tty_std_termios;
52     normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
53     normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
54     normal->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
55       //TTY_DRIVER_DYNAMIC_DEV不立刻注册设备,只初始化
56     normal->driver_state    = drv;
57     tty_set_operations(normal, &uart_ops);    //normal->ops = uart_ops
58 
59     /*
60      * Initialise the UART state(s).
61      */
62     for (i = 0; i < drv->nr; i++) {
63         struct uart_state *state = drv->state + i;
64         struct tty_port *port = &state->port;
65         //初始化tty_port的等待队列open_wait,close_wait,delta_msr_wait和buffer工作队列flush_to_ldisc
66         tty_port_init(port);
67 
68         port->ops = &uart_port_ops;        //tty_port->ops = uart_port_ops
69         port->close_delay     = HZ / 2;    /* .5 seconds */
70         port->closing_wait    = 30 * HZ;/* 30 seconds */
71     }
72 
73     retval = tty_register_driver(normal);    //初始化tty字符设备但不注册
74     return retval;

以上完成tty的初始化,从以上代码可以知道tty_driver->ops = uart_ops,uart_ops是串口底层操作。

 

回到硬件平台的probe函数,部分删减代码linux-3.10/drivers/tty/serial/sunxi-uart.c:

 1 static int sw_uart_probe(struct platform_device *pdev)
 2 {
 3     struct device_node *np = pdev->dev.of_node;
 4     struct uart_port *port;
 5     struct sw_uart_port *sw_uport;
 6     struct sw_uart_pdata *pdata;
 7     struct resource *res;
 8     char uart_para[16] = {0};
 9     int ret = -1;
10 
11     pdev->id = of_alias_get_id(np, "serial");    //通过别名获取UART的ID
12 
13     port = &sw_uart_ports[pdev->id].port;
14     port->dev = &pdev->dev;
15     pdata = &sw_uport_pdata[pdev->id];
16     sw_uport = UART_TO_SPORT(port);
17     sw_uport->pdata = pdata;
18     sw_uport->id = pdev->id;
19     sw_uport->ier = 0;
20     sw_uport->lcr = 0;
21     sw_uport->mcr = 0;
22     sw_uport->fcr = 0;
23     sw_uport->dll = 0;
24     sw_uport->dlh = 0;
25     snprintf(sw_uport->name, 16, SUNXI_UART_DEV_NAME"%d", pdev->id);
26     pdev->dev.init_name = sw_uport->name;
27     pdev->dev.platform_data = sw_uport->pdata;
28 
29     sw_uport->mclk = of_clk_get(np, 0);
30 
31     port->uartclk = clk_get_rate(sw_uport->mclk);
32 
33     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
34 
35     port->mapbase = res->start;
36 
37     port->irq = platform_get_irq(pdev, 0);
38 
39     snprintf(uart_para, sizeof(uart_para), "uart%d_port", pdev->id);    
40     ret = of_property_read_u32(np, uart_para, &port->line);    //设备树uartx_port定义第几路
41 
42     snprintf(uart_para, sizeof(uart_para), "uart%d_type", pdev->id);    
43     ret = of_property_read_u32(np, uart_para, &pdata->io_num);        //设备树uartx_type是2线还是4线
44 
45     pdata->used = 1;
46     port->iotype = UPIO_MEM;
47     port->type = PORT_SUNXI;
48     port->flags = UPF_BOOT_AUTOCONF;
49     port->ops = &sw_uart_ops;
50     port->fifosize = 64;
51     platform_set_drvdata(pdev, port);
52 
53     sunxi_uart_sysfs(pdev);
54 
55     return uart_add_one_port(&sw_uart_driver, port);    //把串口底层代码向上映射
56 }

以上代码基本是从设备树获取硬件相关的初始化代码,最后的uart_add_one_port把driver和uart_port参数作为形参向上映射。

 

删减部分代码linux-3.10/drivers/tty/serial/serial_core.c:

int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
{
    struct uart_state *state;
    struct tty_port *port;
    int ret = 0;
    struct device *tty_dev;

    state = drv->state + uport->line;
    port = &state->port;

    state->uart_port = uport;
    state->pm_state = UART_PM_STATE_UNDEFINED;

    uport->cons = drv->cons;
    uport->state = state;

    //通过port->ops->config_port =sw_uart_config_port通过设备树申请复用io口资源
    uart_configure_port(drv, state, uport);        

    /*
     * Register the port whether it's detected or not.  This allows
     * setserial to be used to alter this ports parameters.
     */
    tty_dev = tty_port_register_device_attr(port, drv->tty_driver,    //注册ttySx字符设备
            uport->line, uport->dev, port, tty_dev_attr_groups);

    device_set_wakeup_capable(tty_dev, 1);
    /*
     * Ensure UPF_DEAD is not set.
     */
    uport->flags &= ~UPF_DEAD;
    return ret;
}

现在可以在用户层看到/dev多了ttySx字符设备,可以像普通的字符设备进行读写操作了。但事实并不是tty->uart这样,在tty_driver->ops里面并不是直接调用uart_port->ops,其中又用映射线路规程(Line discipline)。

 

在kernel初始化终端的时候会调用:

1 void tty_ldisc_begin(void)
2 {
3     /* Setup the default TTY line discipline. */
4     (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY); //串口都是tty_ldisc_N_TTY
5 }

 

串口都会用默认线路N_TTY进行规程。

对/dev/ttySxxx字符设备操作的时候,通过N_TTY---->TTY_DRIVER---->UART_PORT---->硬件平台相关。

 

对N_TTY分析后续补上,未完!

 

posted @ 2016-08-18 11:47  Kevin_Hwang  阅读(4289)  评论(1编辑  收藏  举报