S5PV210 Uart Driver

Linux Kernel Uart Driver Development 相关情况交代如下:

1、需要阅读文档:linux/Documentation/serial/...    ,另外非常需要阅读的文档就是S5PV210 user manual。

2、参考的uart driver文件:s3c2440.c,s5pv210.c

3、底层相关源码:serial_core.h,serial.h,regs-serial.h,samsung.c,samsung.h,

4、Linux kernel version:linux-2.6.32

5、platform:S5PV210

6、本文章分析了uart 的内核驱动和简单的串口应用测试

其他串口设备相关文件:devs.c、tty_io.c

就现有的uart driver相关源码做分析,望能给读者些许帮助,如有错误或不到之处,请留言指正。


底层驱动分析

【一】samsung.c中的几个重要的数据结构:uart_ops、 uart_driver、s3c24xx_uart_port

samsung.c (@/drivers/serial/) 此文件为串口设备驱动的核心结构体所在位置,需要先研究这个文件.

#define S3C24XX_SERIAL_NAME    "ttySAC"
#define S3C24XX_SERIAL_MAJOR    204
#define S3C24XX_SERIAL_MINOR    64

【1】

static struct uart_ops s3c24xx_serial_ops = {
    .pm        = s3c24xx_serial_pm,  //电源管理
    .tx_empty    = s3c24xx_serial_tx_empty,  //发送缓冲区
    .get_mctrl    = s3c24xx_serial_get_mctrl, //获取modem控制设置参数
    .set_mctrl    = s3c24xx_serial_set_mctrl, //设置modem控制(MCR)
    .stop_tx    = s3c24xx_serial_stop_tx,  //停止接收字符
    .start_tx    = s3c24xx_serial_start_tx,  //开始传输字符
    .stop_rx    = s3c24xx_serial_stop_rx, //停止接手
    .enable_ms    = s3c24xx_serial_enable_ms,  //modem状态中断使能
    .break_ctl    = s3c24xx_serial_break_ctl,  //控制break信号的传输
    .startup    = s3c24xx_serial_startup,    //启动端口
    .shutdown    = s3c24xx_serial_shutdown,  //关闭端口
    .set_termios    = s3c24xx_serial_set_termios,  //改变端口参数
    .type        = s3c24xx_serial_type,  //返回描特定端口的常量字符串指针
    .release_port    = s3c24xx_serial_release_port,  //释放端口占用的的内存和I/O资源
    .request_port    = s3c24xx_serial_request_port,  //申请端口所需的内存和I/O资源
    .config_port    = s3c24xx_serial_config_port,  //执行端口所需的自动配置步骤
    .verify_port    = s3c24xx_serial_verify_port,  //验证新的端口信息
};

URXHn和UTXHn分别为接收和发送数据寄存器;

UTRSTATn分别为发送和接收的状态;

UMCONn是UART的modem控制,设置是否使用RTS流控制,若采用流控制,可选择自动流控制(AFC)或软件控制RTS信号。

【2】

static struct uart_driver s3c24xx_uart_drv = {
    .owner        = THIS_MODULE,
    .dev_name    = "s3c2410_serial",  //设备名称
    .nr        = CONFIG_SERIAL_SAMSUNG_UARTS,  //支持最多的串口设备数,210平台共支持4个UART Port
    .cons        = S3C24XX_SERIAL_CONSOLE,
    .driver_name    = S3C24XX_SERIAL_NAME,  //驱动名称
    .major        = S3C24XX_SERIAL_MAJOR,  //主设备号
    .minor        = S3C24XX_SERIAL_MINOR,  //次设备号
};
【3】
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
    [0] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX0,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 0,  //端口号
        }
    },
    [1] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX1,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 1,
        }
    },
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
    [2] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX2,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 2,
        }
    },
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
    [3] = {
        .port = {
            .lock        = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[3].port.lock),
            .iotype        = UPIO_MEM,
            .irq        = IRQ_S3CUART_RX3,
            .uartclk    = 0,
            .fifosize    = 16,
            .ops        = &s3c24xx_serial_ops,
            .flags        = UPF_BOOT_AUTOCONF,
            .line        = 3,
        }
    }
#endif
};

【4】

分析一下s3c24xx_uart_port数据结构
struct s3c24xx_uart_port {
    unsigned char            rx_claimed;
    unsigned char            tx_claimed;
    unsigned int            pm_level;
    unsigned long            baudclk_rate;

    unsigned int            rx_irq;
    unsigned int            tx_irq;

    struct s3c24xx_uart_info    *info;
    struct s3c24xx_uart_clksrc    *clksrc;
    struct clk            *clk;
    struct clk            *baudclk;
    struct uart_port        port;

#ifdef CONFIG_CPU_FREQ
    struct notifier_block        freq_transition;
#endif
};

struct s3c24xx_uart_info {
    char            *name;
    unsigned int        type;
    unsigned int        fifosize;
    unsigned long        rx_fifomask;
    unsigned long        rx_fifoshift;
    unsigned long        rx_fifofull;
    unsigned long        tx_fifomask;
    unsigned long        tx_fifoshift;
    unsigned long        tx_fifofull;

    /* uart port features */

    unsigned int        has_divslot:1;

    /* clock source control */

    int (*get_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);
    int (*set_clksrc)(struct uart_port *, struct s3c24xx_uart_clksrc *clk);

    /* uart controls */
    int (*reset_port)(struct uart_port *, struct s3c2410_uartcfg *);
};

在s3c2410_uartcfg中封装了UCONn、ULCONn、UFCONn等寄存器,如下:

struct   s3c2410_uartcfg {
    unsigned char       hwport;       /* hardware port number */  //硬件端口号
    unsigned char       unused;
    unsigned short       flags;
#if !defined(CONFIG_CPU_S5PV210)
    upf_t           uart_flags;     /* default uart flags */
#else
        unsigned long       uart_flags;      /* default uart flags */
#endif

    unsigned long       ucon;     /* value of ucon for port */
    unsigned long       ulcon;     /* value of ulcon for port */
    unsigned long       ufcon;     /* value of ufcon for port */

    struct s3c24xx_uart_clksrc *clocks;
    unsigned int            clocks_size;
};


【二】串口驱动初始化与释放

UART_DRIVER.C

@Your Direction

【1】

驱动加载过程中会调用uart_register_driver()注册s3c24xx_uart_drv这个uart driver。同时经过

s3c2410_serial_init()

    --> s3c24xx_serial_init()

        --> platform_driver_register()的调用会导致s3c24xx_serial_probe()(@sumsung.c)被调用执行。

而s3c24xx_serial_probe()函数会调用s3c24xx_serial_init_port()来初始化UART端口,同时通过uart_add_one_port()添加端口。加载过程如:

static int s3c24xx_serial_init(struct platform_driver *drv, struct s3c24xx_uart_info *info)
{
    printk("uart platform driver register\n");
    retrurn platform_driver_register(drv);//needed identify
}

static inline int s3c2410_serial_init(void)
{
    return s3c24xx_serial_init(&s5p_serial_drv,&s5p_uart_inf);  //needed modify
}

static int __init PDA_ScanSerialPort_init(void)
{
    int ret;

    ret = s3c_gpio_scan_isr_setup();
    if(!ret)
        printk("Scan triger IRQ request successfully\n");
    else
        printk("Scan triger IRQ request failed");

    //register uart dirver
    ret = uart_register_driver(&s3c24xx_uart_drv);
    if(ret < 0)
    {
        printk("failed to register Uart for Scan\n");
        return -1;
    }

    //init serial port
    s3c2410_serial_init();
    //s3c24xx_serial_init(&s5p_serial_drv, *s5p_uart_inf);  //needed modify
    
    return 0;
}

【三】串口收发数据

s5pv210平台驱动uart_ops结构体的startup()成员函数s3c24xx_serial_startup()用于启动串口,申请端口的发送/接收中断,使能端口的发送和接收:

【1】

static int s3c24xx_serial_startup(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    int ret;

    dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
        port->mapbase, port->membase);

    rx_enabled(port) = 1;  //接收使能

    ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,    //申请接收中断

             s3c24xx_serial_portname(port), ourport); 

    if (ret != 0) {
        printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
        return ret;
    }

    ourport->rx_claimed = 1;

    dbg("requesting tx irq...\n");

    tx_enabled(port) = 1;  //使能发送中断

    ret = request_irq(ourport->tx_irq, s3c24xx_serial_tx_chars, 0,   //申请发送中断
              s3c24xx_serial_portname(port), ourport);

    if (ret) {
        printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
        goto err;
    }

    ourport->tx_claimed = 1;

    dbg("s3c24xx_serial_startup ok\n");

    /* the port reset code should have done the correct
     * register setup for the port controls */

  //端口复位代码应为端口控制配置了正确的寄存器值

    return ret;

 err:
    s3c24xx_serial_shutdown(port);
    return ret;
}

这里的s3c24xx_serial_startup 的反操作函数为 s3c24xx_serial_shutdown,其分析为如下:

static void s3c24xx_serial_shutdown(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);


    if (ourport->tx_claimed) {
        free_irq(ourport->tx_irq, ourport);
        tx_enabled(port) = 0;     //置发送使能为关闭状态
        ourport->tx_claimed = 0;
    }

    if (ourport->rx_claimed) {
        free_irq(ourport->rx_irq, ourport);
        ourport->rx_claimed = 0;
        rx_enabled(port) = 0;    //置接收使能为关闭状态
    }
}

【2】

uart_ops结构体的tx_empty()成员函数s3c24xx_serial_tx_empty()用于判断发送缓冲区是否为空。

当使能FIFO模式的时候,判断UFSTATn寄存器,否则判断UFSTATn寄存器的相应位。分析如下:

static unsigned int s3c24xx_serial_tx_empty(struct uart_port *port)
{
    struct s3c24xx_uart_info *info = s3c24xx_port_to_info(port);
    unsigned long ufstat = rd_regl(port, S3C2410_UFSTAT);  //寄存器
    unsigned long ufcon = rd_regl(port, S3C2410_UFCON);

    if (ufcon & S3C2410_UFCON_FIFOMODE) {     //FIFO模式,检查UFSTATn寄存器
        if ((ufstat & info->tx_fifomask) != 0 ||    //Tx FIFO 数据非0
            (ufstat & info->tx_fifofull))    //Tx FIFO 溢出
            return 0;   //0:非空

        return 1;  //1:空
    }

    return s3c24xx_serial_txempty_nofifo(port);
}

这里的s3c24xx_serial_txempty_nofifo展开:

//非FIFO模式,返回UTRSTATn寄存器状态

static int s3c24xx_serial_txempty_nofifo(struct uart_port *port)
{
    return (rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXE);
}

【3】

uart_ops结构体的start_tx()成员函数s3c24xx_serial_start_tx()用于启动发送,

                            而stop_tx()成员函数s3c24xx_serial_stop_tx()用于停止发送;

static void s3c24xx_serial_start_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);

    if (!tx_enabled(port)) {  //如果端口发送未能使能
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_disable(port);

        enable_irq(ourport->tx_irq);  //使能发送中断
        tx_enabled(port) = 1;    //端口发送使能
    }
}

static void s3c24xx_serial_stop_tx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);

    if (tx_enabled(port)) {    //如果端口发送使能
        disable_irq_nosync(ourport->tx_irq);      //清发送使能状态
        tx_enabled(port) = 0;    //关闭端口发送使能
        if (port->flags & UPF_CONS_FLOW)
            s3c24xx_serial_rx_enable(port);
    }
}

【4】

uart_ops结构体的stop_rx()成员函数s3c24xx_serial_stop_rx()用于清接收中断使能;

注意:成员函数中无start_rx()成员函数

static void s3c24xx_serial_stop_rx(struct uart_port *port)
{
    struct s3c24xx_uart_port *ourport = to_ourport(port);

    if (rx_enabled(port)) {    //如果端口接收为使能
        dbg("s3c24xx_serial_stop_rx: port=%p\n", port);
        disable_irq_nosync(ourport->rx_irq);     //清端口接收使能
        rx_enabled(port) = 0;    //关闭端口接收使能
    }
}

【5】

与数据发送关系最密切的不是上述uart_ops成员函数,而是s3c24xx_serial_startup()为发送和接收中断注册的中断处理函数

s3c24xx_serial_rx_chars()和s3c24xx_serial_tx_chars()。

其中s3c24xx_serial_rx_chars()读取URXHn寄存器以获得接收到的数据,并调用uart_insert_char()将该字符添加到tty设备的flip缓冲区中,

当接收到64个字符或不能再接收字符后,调用tty_flip_buffer_push()函数向上“推”tty设备的flip缓冲。过程分析如下:
static irqreturn_t  s3c24xx_serial_rx_chars(int irq, void *dev_id)
{
    struct s3c24xx_uart_port *ourport = dev_id;
    struct uart_port *port = &ourport->port;  //获得uart_port
    struct tty_struct *tty = port->state->port.tty;   //获得tty
    unsigned int ufcon, ch, flag, ufstat, uerstat;
    int max_count = 64;

    while (max_count-- > 0) {    //如果收到0个字符
        ufcon = rd_regl(port, S3C2410_UFCON);
        ufstat = rd_regl(port, S3C2410_UFSTAT);

        if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
            break;

        uerstat = rd_regl(port, S3C2410_UERSTAT);
        ch = rd_regb(port, S3C2410_URXH);    //读出字符数据

        if (port->flags & UPF_CONS_FLOW) {
            int txe = s3c24xx_serial_txempty_nofifo(port);

            if (rx_enabled(port)) {    //如果端口为接收使能状态
                if (!txe) {    //如果发送缓冲区为空
                    rx_enabled(port) = 0;    //清接收使能
                    continue;
                }
            } else {    //否则端口为禁止接收
                if (txe) {     //如果发送缓冲区非空
                    ufcon |= S3C2410_UFCON_RESETRX;
                    wr_regl(port, S3C2410_UFCON, ufcon);
                    rx_enabled(port) = 1;    //使能端口接收
                    goto out;
                }
                continue;
            }
        }

        /* insert the character into the buffer */

             //将接收到的数据写入到buffer中
        flag = TTY_NORMAL;
        port->icount.rx++;

        if (unlikely(uerstat & S3C2410_UERSTAT_ANY)) {
            dbg("rxerr: port ch=0x%02x, rxs=0x%08x\n",
                ch, uerstat);

            /* check for break */
            if (uerstat & S3C2410_UERSTAT_BREAK) {
                dbg("break!\n");
                port->icount.brk++;
                if (uart_handle_break(port))
                    goto ignore_char;
            }

            if (uerstat & S3C2410_UERSTAT_FRAME)
                port->icount.frame++;
            if (uerstat & S3C2410_UERSTAT_OVERRUN)
                port->icount.overrun++;

            uerstat &= port->read_status_mask;

            if (uerstat & S3C2410_UERSTAT_BREAK)
                flag = TTY_BREAK;
            else if (uerstat & S3C2410_UERSTAT_PARITY)
                flag = TTY_PARITY;
            else if (uerstat & (S3C2410_UERSTAT_FRAME |
                        S3C2410_UERSTAT_OVERRUN))
                flag = TTY_FRAME;
        }

        if (uart_handle_sysrq_char(port, ch))    //处理sysrq字符
            goto ignore_char;

        uart_insert_char(port, uerstat, S3C2410_UERSTAT_OVERRUN,  ch, flag);    //插入字符到tty设备的flip缓冲

 ignore_char:
        continue;
    }
    tty_flip_buffer_push(tty);    //刷新tty设备的flip缓冲

 out:
    return IRQ_HANDLED;
}

其中:uart_insert_char()是对串口核心层对tty_insert_flip_char()的封装,它作为内联函数被定义在serial_core.h中。

【6】

s3c24xx_serial_tx_chars()函数需要读取uart_info中的环形缓冲区中的字符,写入UTXHn寄存器,分析如下:

static irqreturn_t s3c24xx_serial_tx_chars(int irq, void *id)
{
    struct s3c24xx_uart_port *ourport = id;
    struct uart_port *port = &ourport->port;
    struct circ_buf *xmit = &port->state->xmit;    //得到环形缓冲区
    int count = 256;    //最多一次发送256个字符

    if (port->x_char) {      //如果定义了x_char,发送
        wr_regb(port, S3C2410_UTXH, port->x_char);
        port->icount.tx++;
        port->x_char = 0;
        goto out;
    }

    /* if there isnt anything more to transmit, or the uart is now stopped, disable the uart and exit */
    //如果没有更多的数据需要发送,或者uart tx 停止,则停止uart 并且退出

    if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
        s3c24xx_serial_stop_tx(port);
        goto out;
    }

    /* try and drain the buffer... */
    //尝试把环形buffer中的数据发完

    while (!uart_circ_empty(xmit) && count-- > 0) {
        if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
            break;

        wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        port->icount.tx++;
    }
   

    //如果环形buffer中剩余的字符少于WAKEUP_CHARS,唤醒上层

    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
        uart_write_wakeup(port);

    if (uart_circ_empty(xmit))    //如果环形buffer为空
        s3c24xx_serial_stop_tx(port);    //停止发送

 out:
    return IRQ_HANDLED;
}

其中:WAKEUP_CHARS的含义为,当发送环形buffer中的字符数少于该数时,驱动将请求上层向下传递更多的数据,

uart_write_wakeup(port)来完成这个任务。


【四】串口相关寄存器设置

【1】

uart_ops结构体的set_termios()成员函数用于改变端口的参数设置,包括波特率,字长,停止位,奇偶校验等参数,并将它接收到的

port、termios参数成员的值设置ARM处理器的uart的相关 regs(如,ULCONn、UCONn、UMCONn……)

static void s3c24xx_serial_set_termios(struct uart_port *port,
                       struct ktermios *termios,
                       struct ktermios *old)
{
    struct s3c2410_uartcfg *cfg = s3c24xx_port_to_cfg(port);
    struct s3c24xx_uart_port *ourport = to_ourport(port);
    struct s3c24xx_uart_clksrc *clksrc = NULL;
    struct clk *clk = NULL;
    unsigned long flags;
    unsigned int baud, quot;
    unsigned int ulcon;
    unsigned int umcon;
    unsigned int udivslot = 0;
//davied add for bt
    unsigned int ucon;
//--

    //不止此modem控制信号线

    termios->c_cflag &= ~(HUPCL | CMSPAR);
    termios->c_cflag |= CLOCAL;

   //计算端口分频,以便产生对应的波特率。
//davied changed 115200*8 to 1843200*2 for bt
    baud = uart_get_baud_rate(port, termios, old, 0, 1843200*2);//115200*8);

    if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
        quot = port->custom_divisor;
    else
        quot = s3c24xx_serial_getclk(port, &clksrc, &clk, baud);

    // 检查是否需要改变时钟源

    if (ourport->clksrc != clksrc || ourport->baudclk != clk) {
        dbg("selecting clock %p\n", clk);
        s3c24xx_serial_setsource(port, clksrc);

        if (ourport->baudclk != NULL && !IS_ERR(ourport->baudclk)) {
            clk_disable(ourport->baudclk);
            ourport->baudclk  = NULL;
        }

        clk_enable(clk);

        ourport->clksrc = clksrc;
        ourport->baudclk = clk;
        ourport->baudclk_rate = clk ? clk_get_rate(clk) : 0;
    }

    if (ourport->info->has_divslot) {
        unsigned int div = ourport->baudclk_rate / baud;

        udivslot = udivslot_table[div & 15];
        dbg("udivslot = %04x (div %d)\n", udivslot, div & 15);
    }

    //设置字长
    switch (termios->c_cflag & CSIZE) {
    case CS5:
        dbg("config: 5bits/char\n");
        ulcon = S3C2410_LCON_CS5;
        break;
    case CS6:
        dbg("config: 6bits/char\n");
        ulcon = S3C2410_LCON_CS6;
        break;
    case CS7:
        dbg("config: 7bits/char\n");
        ulcon = S3C2410_LCON_CS7;
        break;
    case CS8:
    default:
        dbg("config: 8bits/char\n");
        ulcon = S3C2410_LCON_CS8;
        break;
    }

    //保留以前的LCON IR 参数
    ulcon |= (cfg->ulcon & S3C2410_LCON_IRM);

    if (termios->c_cflag & CSTOPB)
        ulcon |= S3C2410_LCON_STOPB;
   

    //设置是否采用RTS、CTS等流控制
    umcon = (termios->c_cflag & CRTSCTS) ? S3C2410_UMCOM_AFC : 0;

    if (termios->c_cflag & PARENB) {
        if (termios->c_cflag & PARODD)
            ulcon |= S3C2410_LCON_PODD;    //奇校验
        else
            ulcon |= S3C2410_LCON_PEVEN;   //偶校验
    } else {
        ulcon |= S3C2410_LCON_PNONE;    //无校验
    }

    spin_lock_irqsave(&port->lock, flags);

    dbg("setting ulcon to %08x, brddiv to %d, udivslot %08x\n",
        ulcon, quot, udivslot);

//davied add for bt
    if(port->irq == IRQ_S3CUART_RX0)
    {
            ucon = rd_regl(port, S3C2410_UCON),
        wr_regl(port, S3C2410_UCON, ucon|(1<<10));
    }
//--

    wr_regl(port, S3C2410_ULCON, ulcon);
    wr_regl(port, S3C2410_UBRDIV, quot);
    wr_regl(port, S3C2410_UMCON, umcon);

    if (ourport->info->has_divslot)
        wr_regl(port, S3C2443_DIVSLOT, udivslot);

    dbg("uart: ulcon = 0x%08x, ucon = 0x%08x, ufcon = 0x%08x\n",
        rd_regl(port, S3C2410_ULCON),
        rd_regl(port, S3C2410_UCON),
        rd_regl(port, S3C2410_UFCON));

    // 更新端口超时
    uart_update_timeout(port, termios->c_cflag, baud);

    /*
     * Which character status flags are we interested in?
     */
    port->read_status_mask = S3C2410_UERSTAT_OVERRUN;
    if (termios->c_iflag & INPCK)
        port->read_status_mask |= S3C2410_UERSTAT_FRAME | S3C2410_UERSTAT_PARITY;

    /*
     * Which character status flags should we ignore?
     */
    port->ignore_status_mask = 0;
    if (termios->c_iflag & IGNPAR)
        port->ignore_status_mask |= S3C2410_UERSTAT_OVERRUN;
    if (termios->c_iflag & IGNBRK && termios->c_iflag & IGNPAR)
        port->ignore_status_mask |= S3C2410_UERSTAT_FRAME;

    // CREAD未设置,则忽略所有字符.
    if ((termios->c_cflag & CREAD) == 0)
        port->ignore_status_mask |= RXSTAT_DUMMY_READ;

    spin_unlock_irqrestore(&port->lock, flags);
}

【2】

uart_ops结构体的get_mctrl()、set_mctrl()成员函数实现非常简单的modem控制信号线。get_mctrl()返回DSR一直有效,而CTS则根据UMSTATn寄存器

的内容获得,set_mctrl()当下版本为空函数。

static unsigned int s3c24xx_serial_get_mctrl(struct uart_port *port)
{
    unsigned int umstat = rd_regb(port, S3C2410_UMSTAT);

    if (umstat & S3C2410_UMSTAT_CTS)    //如果CTS信号有效(low level)
        return TIOCM_CAR | TIOCM_DSR | TIOCM_CTS;
    else
        return TIOCM_CAR | TIOCM_DSR;
}

static void s3c24xx_serial_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
    /* todo - possibly remove AFC and do manual CTS */
}

最后TTY 驱动的主题是围绕tty_driver这个结构体的成员函数展开的,主要是应去实现其中的数据发送和接收流程以及tty设备端口接口函数。

针对串口,linux kernel实现了通用的tty_driver,所以串口驱动的主要任务就是从tty_driver转移到uart_driver上来。


UART发送接收数据分析


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


细节分析:

【1】读、写函数

rd_regl() 

    --> #define rd_regl(port, reg) (__raw_readl(portaddr(port, reg))) 

wr_regl()

    -->#define wr_regl(port, reg, val) __raw_writel(val,portaddr(port, reg))


        -->#define portaddr(port, reg) ((port)->membase + (reg))

        -->port --> struct uart_port

struct uart_port {      /* @ linux/drivers/char/serial_core.h  */
    spinlock_t        lock;            /* port lock */
    unsigned long        iobase;            /* in/out[bwl] */
    unsigned char __iomem    * membase;        /* read/write[bwl] */
    unsigned int        (*serial_in)(struct uart_port *, int);
    void            (*serial_out)(struct uart_port *, int, int);
    unsigned int        irq;            /* irq number */
    unsigned long        irqflags;        /* irq flags  */
    unsigned int        uartclk;        /* base uart clock */
    unsigned int        fifosize;        /* tx fifo size */
    unsigned char        x_char;            /* xon/xoff char */
    unsigned char        regshift;        /* reg offset shift */
    unsigned char        iotype;            /* io access style */
    unsigned char        unused1;
    unsigned int        read_status_mask;    /* driver specific */
    unsigned int        ignore_status_mask;    /* driver specific */
    struct uart_state    *state;            /* pointer to parent state */
    struct uart_icount    icount;            /* statistics */

    struct console        *cons;            /* struct console, if any */

    //……

    //……

    unsigned int        mctrl;            /* current modem ctrl settings */
    unsigned int        timeout;        /* character-based timeout */
    unsigned int        type;            /* port type */
    const struct uart_ops    *ops;
    unsigned int        custom_divisor;
    unsigned int        line;            /* port index */
    resource_size_t        mapbase;        /* for ioremap */
    struct device        *dev;            /* parent device */
    unsigned char        hub6;            /* this should be in the 8250 driver */
    unsigned char        suspended;
    unsigned char        unused[2];
    void            *private_data;        /* generic platform data pointer */
};

看一下实例,你就能明白好多了,以函数s5pv210_serial_resetport()为例:

static int s5pv210_serial_resetport(struct uart_port  *port,  struct s3c2410_uartcfg *cfg)
{
    unsigned long ucon = rd_regl(port, S3C2410_UCON);

    ucon &= S5PV210_UCON_CLKMASK;
    wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);
    wr_regl(port, S3C2410_ULCON, cfg->ulcon);

    /* reset both fifos */
    wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
    wr_regl(port, S3C2410_UFCON, cfg->ufcon);

    return 0;
}

其中:S3C2410_UCON、S3C2410_ULCON、S3C2410_UFCON等寄存器地址在头文件

linux/arch/arm/plat-samsung/include/plat/regs-serial.h中定义。

【2】经过编译后的内核下载烧写到ARM主板,运行主板,进入文件系统后敲入命令:

ls dev/ |grep tty*

可以看到,如下打印信息:

……

rfkill                                                                         
vga_arbiter                                                                    
snd                                                                            
ttyS3                                                                          
ttyS2                                                                          
ttyS1                                                                          
ttyS0                                                                          
s3c2410_serial3                                                                
s3c2410_serial2                                                                
s3c2410_serial1                                                                
s3c2410_serial0                                                                
usbdev1.1                                                                      
usb1                                                                           
rtc0                                                                           
graphics                                                                       
block                                                                          
i2c-5                                                                          
i2c-4 
……

可以看到有我们需要的串口驱动“s3c2410_serialx





未完,待续~~



参考文章1

参考文章2


你可能感兴趣的:(嵌入式硬件)