linuxカーネルにおけるシリアルポート駆動登録プロセス(tty駆動)


原文転入先:http://m.blog.csdn.net/blog/lushengchu2003/9368031
最近暇で、以前のプロジェクトでシリアルポートハードウェアフロー制御の問題に遭遇したこと、ブルートゥースシリアルポート制御の戻りエラー、上層読み書きシリアルポートbufferオーバーフローの問題などを考えて、しばらく振り回されて、最終的にシリアルポート駆動とは関係ないことを証明したが、問題を調べるときにシリアルポート駆動の関連コードを確認することは間違いないので、シリアルポート駆動の流れを一度過ぎた.後で使ってもいいです.解析したのはフルログコードA 20である.コード分析を直接始めましょう.
シリアルドライバコードlinux-3.3/drivers/tty/serialディレクトリの下で、全志は自分のプラットフォームに関するコードを1つのファイルに集中して、sw_と言いますuart.c、それではその__からInitが始まりました:
static int __init sw_uart_init(void)
{
    int ret; 
    u32 i;
    struct sw_uart_pdata *pdata;

    SERIAL_MSG("driver initializied
"); ret = sw_uart_get_devinfo(); if (unlikely(ret)) return ret; ret = uart_register_driver(&sw_uart_driver); if (unlikely(ret)) { SERIAL_MSG("driver initializied
"); return ret; } for (i=0; iused) platform_device_register(&sw_uport_device[i]); } return platform_driver_register(&sw_uport_platform_driver); }

sw_uart_get_devinfoは全志を解析するsys構成スクリプトのシリアルポート構成で、全部で8つのシリアルポートがあり、それを使って直接sysスクリプトに1を設定すればいいのですが、これは全志プラットフォームを使ったことがあるのはすべて知っています.
続いてsw_uart_driver構造体は以下のように定義されています.
static struct uart_driver sw_uart_driver = {                                                                                                                   
    .owner = THIS_MODULE,
    .driver_name = "sw_serial",
    .dev_name = "ttyS",
    .nr = SW_UART_NR,
    .cons = SW_CONSOLE,
};

ここSW_UART_NRは8で、ttySは/dev/ディレクトリの下に表示される名前で、0~7から
次にuart登録関数を見て、その名の通り全志自身のプラットフォームのシリアルポートをシリアルコアserialに登録します.coreの中へ:
int uart_register_driver(struct uart_driver *drv)
{
    struct tty_driver *normal;
    int i, retval;

    BUG_ON(drv->state);

    /*
     * Maybe we should be using a slab cache for this, especially if
     * we have a large number of ports to handle.
     */
    drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
    if (!drv->state)
        goto out;

    normal = alloc_tty_driver(drv->nr);
    if (!normal)
        goto out_kfree;

    drv->tty_driver = normal;

    normal->owner       = drv->owner;
    normal->driver_name = drv->driver_name;
    normal->name        = drv->dev_name;//   ttyS
    normal->major       = drv->major;
    normal->minor_start = drv->minor;
    normal->type        = TTY_DRIVER_TYPE_SERIAL;
    normal->subtype     = SERIAL_TYPE_NORMAL;
    normal->init_termios    = tty_std_termios;
    normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;                                                                                       
    normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
    normal->flags       = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
    normal->driver_state    = drv;
    tty_set_operations(normal, &uart_ops);
    /*
     * Initialise the UART state(s).
     */
    for (i = 0; i < drv->nr; i++) {
        struct uart_state *state = drv->state + i;
        struct tty_port *port = &state->port;

        tty_port_init(port);
        port->ops = &uart_port_ops;
        port->close_delay     = HZ / 2; /* .5 seconds */
        port->closing_wait    = 30 * HZ;/* 30 seconds */
    }

    retval = tty_register_driver(normal);
    if (retval >= 0)
        return retval;

    put_tty_driver(normal);
out_kfree:
    kfree(drv->state);
out:
    return -ENOMEM;
}

まず、NR個のstateを作成し、各stateの初期化を行いますが、これらのstateはまだポート(uart_port)に対応していません.ポートポートポートを初期化したらtty_を呼び出すregister_driver:
int tty_register_driver(struct tty_driver *driver)
{
    int error;
    int i;
    dev_t dev;
    void **p = NULL;
    struct device *d;

    if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
        p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
        if (!p)
            return -ENOMEM;
    }

    if (!driver->major) {
        error = alloc_chrdev_region(&dev, driver->minor_start,
                        driver->num, driver->name);
        if (!error) {
            driver->major = MAJOR(dev);
            driver->minor_start = MINOR(dev);
        }
    } else {
        dev = MKDEV(driver->major, driver->minor_start);
        error = register_chrdev_region(dev, driver->num, driver->name);
    }
    if (error < 0) {
        kfree(p);                                                                                                                                              
        return error;
    }

    if (p) {
        driver->ttys = (struct tty_struct **)p;
        driver->termios = (struct ktermios **)(p + driver->num);
    } else {
        driver->ttys = NULL;
        driver->termios = NULL;
    }

    cdev_init(&driver->cdev, &tty_fops);
    driver->cdev.owner = driver->owner;
    error = cdev_add(&driver->cdev, dev, driver->num);
    if (error) {
        unregister_chrdev_region(dev, driver->num);
        driver->ttys = NULL;
        driver->termios = NULL;
        kfree(p);
        return error;
    }

    mutex_lock(&tty_mutex);
    list_add(&driver->tty_drivers, &tty_drivers);
    mutex_unlock(&tty_mutex);

    if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
        for (i = 0; i < driver->num; i++) {
            d = tty_register_device(driver, i, NULL);
            if (IS_ERR(d)) {
                error = PTR_ERR(d);
                goto err;
            }
        }
    }
    proc_tty_register_driver(driver);
    driver->flags |= TTY_DRIVER_INSTALLED;
    return 0;

ここにいるよchrdev_regionは、プライマリスレーブデバイス番号を動的に割り当て、cdev_init、file_operations構造はそれに関連付けられており、open/dev/ttySノードでopen関数が呼び出されます.まず、この構造体を見てみましょう.
static const struct file_operations tty_fops = {                                                                                                               
    .llseek     = no_llseek,
    .read       = tty_read,
    .write      = tty_write,
    .poll       = tty_poll,
    .unlocked_ioctl = tty_ioctl,
    .compat_ioctl   = tty_compat_ioctl,
    .open       = tty_open,
    .release    = tty_release,
    .fasync     = tty_fasync,
};

続いてcdev_addはfileをoperationsはデバイス番号に関連付けられています.デバイスノードはまだ作成されていませんが、driver->major、driver->minorがあります.startが割り当てたもので、後でノードを作成すると、この2つのプライマリスレーブデバイス番号が使用されます.続いてlist_addこれを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関数を見てみましょう
static int __devinit sw_uart_probe(struct platform_device *pdev)
{
    u32 id = pdev->id;
    struct uart_port *port;
    struct sw_uart_port *sw_uport;
    struct clk *apbclk;
    int ret = -1;

    if (unlikely(pdev->id < 0 || pdev->id >= SW_UART_NR))
        return -ENXIO;
    port = &sw_uart_port[id].port;
    port->dev = &pdev->dev;
    sw_uport = UART_TO_SPORT(port);
    sw_uport->id = id;
    sw_uport->ier = 0;
    sw_uport->lcr = 0;
    sw_uport->mcr = 0;
    sw_uport->fcr = 0;
    sw_uport->dll = 0;
    sw_uport->dlh = 0;

    /* request system resource and init them */
    ret = sw_uart_request_resource(sw_uport);
    if (unlikely(ret)) {                                                                                                                                       
        SERIAL_MSG("uart%d error to get resource
", id); return -ENXIO; } apbclk = clk_get(&pdev->dev, CLK_SYS_APB1); if (IS_ERR(apbclk)) { SERIAL_MSG("uart%d error to get source clock
", id); return -ENXIO; } ret = clk_set_parent(sw_uport->mclk, apbclk); if (ret) { SERIAL_MSG("uart%d set mclk parent error
", id); clk_put(apbclk); return -ENXIO; } port->uartclk = clk_get_rate(apbclk); clk_put(apbclk); port->type = PORT_SW; port->flags = UPF_BOOT_AUTOCONF; port->mapbase = sw_uport->pdata->base; port->irq = sw_uport->pdata->irq; platform_set_drvdata(pdev, port); #ifdef CONFIG_PROC_FS sw_uart_procfs_attach(sw_uport); #endif SERIAL_DBG("add uart%d port, port_type %d, uartclk %d
", id, port->type, port->uartclk); return uart_add_one_port(&sw_uart_driver, port); }

sw_uart_request_ResourceはGPIOの構成を申請し、uart_add_one_port:
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;

    BUG_ON(in_interrupt());

    if (uport->line >= drv->nr)
        return -EINVAL;

    state = drv->state + uport->line;//state     uart_register_driver kmalloc   
    port = &state->port;

    mutex_lock(&port_mutex);
    mutex_lock(&port->mutex);
    if (state->uart_port) {
        ret = -EINVAL;
        goto out; 
    }    

    state->uart_port = uport;
    state->pm_state = -1;
                                                                                                                                                               
    uport->cons = drv->cons;
    uport->state = state;

    /*   
     * If this port is a console, then the spinlock is already
     * initialised.
     */
    if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
        spin_lock_init(&uport->lock);
        lockdep_set_class(&uport->lock, &port_lock_key);
    }    

    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_register_device(drv->tty_driver, uport->line, uport->dev);
    if (likely(!IS_ERR(tty_dev))) {
        device_set_wakeup_capable(tty_dev, 1);
    } else {
        printk(KERN_ERR "Cannot register tty device on line %d
", uport->line); } /* * Ensure UPF_DEAD is not set. */ uport->flags &= ~UPF_DEAD; out: mutex_unlock(&port->mutex); mutex_unlock(&port_mutex); return ret; }

この関数は名前を見るとuartと推測されます.driverはポートを追加し、stateの状態はまだuart_と話していません.port対応だと、ここでstate->uart_port=uportは対応して、portの配置はそれに関心を持っていないで、このようにuart_driverはportによるops構造体の操作関数により下位層を制御し、最後にtty_を呼び出すことができる.register_device:
struct device *tty_register_device(struct tty_driver *driver, unsigned index,
                   struct device *device)
{   
    char name[64];
    dev_t dev = MKDEV(driver->major, driver->minor_start) + index;

    if (index >= driver->num) {
        printk(KERN_ERR "Attempt to register invalid tty line number "
               " (%d).
", index); return ERR_PTR(-EINVAL); } if (driver->type == TTY_DRIVER_TYPE_PTY) pty_line_name(driver, index, name); else tty_line_name(driver, index, name); return device_create(tty_class, device, dev, NULL, name); }

ここのtty_line_name関数は次のように定義されます.
static void tty_line_name(struct tty_driver *driver, int index, char *p)                                                                                       
{
    sprintf(p, "%s%d", driver->name, index + driver->name_base);//   uart_register_driver   
}

前にも言ったような名前のttyS 0~ttyS 7が見えます.
これにより、上位openノードが呼び出されると、前のfile_operations構造体関数tty_Open、ttyコアレイヤの呼び出しです.
static int tty_open(struct inode *inode, struct file *filp)
{
    struct tty_struct *tty;
    int noctty, retval;
    struct tty_driver *driver = NULL;
    int index;
    dev_t device = inode->i_rdev;
    unsigned saved_flags = filp->f_flags;

    nonseekable_open(inode, filp);

retry_open:
    retval = tty_alloc_file(filp);
    if (retval)
        return -ENOMEM;

    noctty = filp->f_flags & O_NOCTTY;
    index  = -1;
    retval = 0;

    mutex_lock(&tty_mutex);
    tty_lock();

    tty = tty_open_current_tty(device, filp);

     ...................................

    if (tty->ops->open)
        retval = tty->ops->open(tty, filp);
    else

    ........................................

            return retval;

        schedule();
        /*
         * Need to reset f_op in case a hangup happened.
         */
        tty_lock();
        if (filp->f_op == &hung_up_tty_fops)
            filp->f_op = &tty_fops;                                                                                                                            
        tty_unlock();
        goto retry_open;
     .................................

まずtty_を検索しますdriverチェーンテーブル、前に追加したtty_を見つけます.driver、それから彼のops->open関数を呼び出して、このopsは前のuart_に値を割り当てますregister_driver関数:
tty_set_operations(normal, &uart_ops);

だからuart_に入りますops構造体のopen関数です.ここではttyコアからserialコアに移動し、次のレベルに進みます.
static int uart_open(struct tty_struct *tty, struct file *filp)
{
    struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
    int retval, line = tty->index;
    struct uart_state *state = drv->state + line;
    struct tty_port *port = &state->port;
     
      ................................

    /*
     * Start up the serial port.
     */
    retval = uart_startup(tty, state, 0);

    .....................................
 

uart_startup:
static int uart_startup(struct tty_struct *tty, struct uart_state *state,                                                                                      
        int init_hw)
{
    struct tty_port *port = &state->port;
    int retval;

    if (port->flags & ASYNC_INITIALIZED)
        return 0;

    /*
     * Set the TTY IO error marker - we will only clear this
     * once we have successfully opened the port.
     */
    set_bit(TTY_IO_ERROR, &tty->flags);

    retval = uart_port_startup(tty, state, init_hw);
    if (!retval) {
        set_bit(ASYNCB_INITIALIZED, &port->flags);
        clear_bit(TTY_IO_ERROR, &tty->flags);
    } else if (retval > 0)
        retval = 0;

    return retval;
}

uart_port_startup:
static int uart_port_startup(struct tty_struct *tty, struct uart_state *state,                                                                                 
        int init_hw)
{
    struct uart_port *uport = state->uart_port;
    struct tty_port *port = &state->port;
    unsigned long page;
    int retval = 0;
    retval = uport->ops->startup(uport);
    if (retval == 0) {
        if (uart_console(uport) && uport->cons->cflag) {
            tty->termios->c_cflag = uport->cons->cflag;
            uport->cons->cflag = 0;
        }
        /*
         * Initialise the hardware port settings.
         */
        uart_change_speed(tty, state, NULL);

        if (init_hw) {
            /*
             * Setup the RTS and DTR signals once the
             * port is open and ready to respond.
             */
            if (tty->termios->c_cflag & CBAUD)
                uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
        }

        if (port->flags & ASYNC_CTS_FLOW) {
            spin_lock_irq(&uport->lock);
            if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
                tty->hw_stopped = 1;
            spin_unlock_irq(&uport->lock);
        }
    }

 

最終的に呼び出されたことがわかります uport->ops->startupは、sericalコア層からプラットフォームのシリアルポート駆動層、すなわち最下層の駆動に移行します.この関数はsw_に定義されています.uart.cのsw_uart_port構造体:
static struct sw_uart_port sw_uart_port[] = {                                                                                                                  
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 0, },
      .pdata = &sw_uport_pdata[0], },
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 1, },
      .pdata = &sw_uport_pdata[1], },
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 2, },
      .pdata = &sw_uport_pdata[2], },
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 3, },
      .pdata = &sw_uport_pdata[3], },
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 4, },
      .pdata = &sw_uport_pdata[4], },
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 5, },
      .pdata = &sw_uport_pdata[5], },
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 6, },
      .pdata = &sw_uport_pdata[6], },
    { .port = { .iotype = UPIO_MEM, .ops = &sw_uart_ops, .fifosize = 64, .line = 7, },
      .pdata = &sw_uport_pdata[7], },
};

彼の.startup関数を見てください.
static int sw_uart_startup(struct uart_port *port)                                                                                                             
{
    struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
    int ret;

    SERIAL_DBG("start up ...
"); snprintf(sw_uport->name, sizeof(sw_uport->name), "sw_serial%d", port->line); ret = request_irq(port->irq, sw_uart_irq, 0, sw_uport->name, port); if (unlikely(ret)) { SERIAL_MSG("uart%d cannot get irq %d
", sw_uport->id, port->irq); return ret; } sw_uport->msr_saved_flags = 0; /* * PTIME mode to select the THRE trigger condition: * if PTIME=1(IER[7]), the THRE interrupt will be generated when the * the water level of the TX FIFO is lower than the threshold of the * TX FIFO. and if PTIME=0, the THRE interrupt will be generated when * the TX FIFO is empty. * In addition, when PTIME=1, the THRE bit of the LSR register will not * be set when the THRE interrupt is generated. You must check the * interrupt id of the IIR register to decide whether some data need to * send. */ sw_uport->ier = SW_UART_IER_RLSI | SW_UART_IER_RDI; #ifdef CONFIG_SW_UART_PTIME_MODE sw_uport->ier |= SW_UART_IER_PTIME; #endif return 0; }

ここでは最終的にARMチップを初期化するレジスタ操作であり,割り込み関数が申請されたことがわかり,後続の読み書き操作は割り込みサービス関数と密接に関連している.
次にopenの後、上位レベルのwrite関数呼び出しプロセスを見て、まずttyコア層のwriteを呼び出します.
static ssize_t tty_write(struct file *file, const char __user *buf,                                                                                            
                        size_t count, loff_t *ppos)
{
    struct inode *inode = file->f_path.dentry->d_inode;
    struct tty_struct *tty = file_tty(file);
    struct tty_ldisc *ld;
    ssize_t ret;

    if (tty_paranoia_check(tty, inode, "tty_write"))
        return -EIO;
    if (!tty || !tty->ops->write ||
        (test_bit(TTY_IO_ERROR, &tty->flags)))
            return -EIO;
    /* Short term debug to catch buggy drivers */
    if (tty->ops->write_room == NULL)
        printk(KERN_ERR "tty driver %s lacks a write_room method.
", tty->driver->name); ld = tty_ldisc_ref_wait(tty); if (!ld->ops->write) ret = -EIO; else ret = do_tty_write(ld->ops->write, tty, file, buf, count); tty_ldisc_deref(ld); return ret; }

ここを見てtty_write関数:
static inline ssize_t do_tty_write(
    ssize_t (*write)(struct tty_struct *, struct file *, const unsigned char *, size_t),
    struct tty_struct *tty,
    struct file *file,
    const char __user *buf,
    size_t count)
{
    
    ............................

    for (;;) {
        size_t size = count;
        if (size > chunk)
            size = chunk;
        ret = -EFAULT;
        if (copy_from_user(tty->write_buf, buf, size))
            break;
        ret = write(tty, file, tty->write_buf, size);
        if (ret <= 0)
            break;

    ...............................


copy_from_userは書くデータをカーネル空間のwrite_にコピーしますbufではwriteが関数ポインタでありld->ops->writeを指す.
ここのld->opsはn_を指していますtty.c中構造体:
struct tty_ldisc_ops tty_ldisc_N_TTY = {
    .magic           = TTY_LDISC_MAGIC,
    .name            = "n_tty",
    .open            = n_tty_open,
    .close           = n_tty_close,
    .flush_buffer    = n_tty_flush_buffer,
    .chars_in_buffer = n_tty_chars_in_buffer,
    .read            = n_tty_read,
    .write           = n_tty_write,                                                                                                                            
    .ioctl           = n_tty_ioctl,
    .set_termios     = n_tty_set_termios,
    .poll            = n_tty_poll,
    .receive_buf     = n_tty_receive_buf,
    .write_wakeup    = n_tty_write_wakeup
};

呼び出しは回線規程のwriteです
static ssize_t n_tty_write(struct tty_struct *tty, struct file *file,
               const unsigned char *buf, size_t nr)
{ 
    ..........................
                b++; nr--;
            }
            if (tty->ops->flush_chars)
                tty->ops->flush_chars(tty);
        } else {
            while (nr > 0) {
                c = tty->ops->write(tty, b, nr);
                if (c < 0) {
                    retval = c;
                    goto break_out;
                }
                if (!c)
                    break;
                b += c;
                nr -= c;
            }
        }
    ............................

回線規程からtty駆動層のwriteに移行する:
static int uart_write(struct tty_struct *tty,
                    const unsigned char *buf, int count)
{

    ......................

    if (!circ->buf)
        return 0;

    spin_lock_irqsave(&port->lock, flags);
    while (1) {
        c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE);
        if (count < c) 
            c = count;
        if (c <= 0)
            break;
        memcpy(circ->buf + circ->head, buf, c);
        circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1);
        buf += c;
        count -= c;
        ret += c;
    }
    spin_unlock_irqrestore(&port->lock, flags);

    uart_start(tty);
    return ret;
}

データmemcpyをループキューに格納すると、ポートに対応するstateのxmitのbufにデータが保存され、ループキューであることがわかります.次にuart_を呼び出すstart:
static void uart_start(struct tty_struct *tty)
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->uart_port;
    unsigned long flags;

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

データの転送が始まるようですので、この操作にロックをかけました.
static void __uart_start(struct tty_struct *tty)                                                                                                               
{
    struct uart_state *state = tty->driver_data;
    struct uart_port *port = state->uart_port;

    if (port->ops->wake_peer)
        port->ops->wake_peer(port);

    if (!uart_circ_empty(&state->xmit) && state->xmit.buf &&
        !tty->stopped && !tty->hw_stopped)
        port->ops->start_tx(port);
}

最終的に駆動層の操作関数、すなわちポートに対応する転送関数を呼び出してデータ送信をトリガーします.
static void sw_uart_start_tx(struct uart_port *port)                                                                                                           
{
    struct sw_uart_port *sw_uport = UART_TO_SPORT(port);

    if (!(sw_uport->ier & SW_UART_IER_THRI)) {
        sw_uport->ier |= SW_UART_IER_THRI;
        SERIAL_DBG("start tx, ier %x
", sw_uport->ier); serial_out(port, sw_uport->ier, SW_UART_IER); } }

serial_out関数は最下位のレジスタ操作に対応しています.ここではdefine SW_UART_IER (0x04),SW_UART_IER_THRIは割り込み可能に対応しており、具体的には全志のA 20 CPUマニュアルを参照してください.
static inline void serial_out(struct uart_port *port, unsigned char value, int offs)                                                                           
{
    __raw_writeb(value, port->membase + offs);
}

構成をレジスタに書き、割り込み可能にした後、割り込みサービス関数は自動的にbufのデータを送信し、前の分析ではmemcpyがこのポートに対応するstateのcirc_buf構造体の中にあるので、前のopenで分析した割り込みサービス関数に入ります.
static irqreturn_t sw_uart_irq(int irq, void *dev_id)
{
    struct uart_port *port = dev_id;
    struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
    unsigned int iir = 0, lsr = 0;
    unsigned long flags;

    spin_lock_irqsave(&port->lock, flags);

    iir = serial_in(port, SW_UART_IIR) & SW_UART_IIR_IID_MASK;                                                                                                 
    lsr = serial_in(port, SW_UART_LSR);
    SERIAL_DBG("irq: iir %x lsr %x
", iir, lsr); if (iir == SW_UART_IIR_IID_BUSBSY) { /* handle busy */ // SERIAL_MSG("uart%d busy...
", sw_uport->id); serial_in(port, SW_UART_USR); #ifdef CONFIG_SW_UART_FORCE_LCR sw_uart_force_lcr(sw_uport, 10); #else serial_out(port, sw_uport->lcr, SW_UART_LCR); #endif } else { if (lsr & (SW_UART_LSR_DR | SW_UART_LSR_BI)) lsr = sw_uart_handle_rx(sw_uport, lsr); sw_uart_modem_status(sw_uport); #ifdef CONFIG_SW_UART_PTIME_MODE if (iir == SW_UART_IIR_IID_THREMP) #else if (lsr & SW_UART_LSR_THRE) #endif sw_uart_handle_tx(sw_uport); } spin_unlock_irqrestore(&port->lock, flags); return IRQ_HANDLED; }

私たちがしなければならないのは送信操作なので、swに入ります.uart_handle_tx:
static void sw_uart_handle_tx(struct sw_uart_port *sw_uport)
{
    struct circ_buf *xmit = &sw_uport->port.state->xmit;
    int count;

    if (sw_uport->port.x_char) {                                                                                                                               
        serial_out(&sw_uport->port, sw_uport->port.x_char, SW_UART_THR);
        sw_uport->port.icount.tx++;
        sw_uport->port.x_char = 0;
#ifdef CONFIG_SW_UART_DUMP_DATA
        sw_uport->dump_buff[sw_uport->dump_len++] = sw_uport->port.x_char;
        SERIAL_DUMP(sw_uport, "Tx");
#endif
        return;
    }
    if (uart_circ_empty(xmit) || uart_tx_stopped(&sw_uport->port)) {
        sw_uart_stop_tx(&sw_uport->port);
        return;
    }
    count = sw_uport->port.fifosize / 2;
    do {
#ifdef CONFIG_SW_UART_DUMP_DATA
        sw_uport->dump_buff[sw_uport->dump_len++] = xmit->buf[xmit->tail];
#endif
        serial_out(&sw_uport->port, xmit->buf[xmit->tail], SW_UART_THR);
        xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
        sw_uport->port.icount.tx++;
        if (uart_circ_empty(xmit)) {
            break;
        }
    } while (--count > 0);

    SERIAL_DUMP(sw_uport, "Tx");
    if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) {
        spin_unlock(&sw_uport->port.lock);
        uart_write_wakeup(&sw_uport->port);
        spin_lock(&sw_uport->port.lock);
    }
    ..........................
 

見ましたserialoutはやっぱりcircを取り出してbufのbufデータは、do{}while文で送信を完了します.
static inline void serial_out(struct uart_port *port, unsigned char value, int offs)                                                                           
{
    __raw_writeb(value, port->membase + offs);
}

これで送信したデータを対応するレジスタに書き込むと、ハードウェアは自動的にデータ送信操作を完了します.
 
上層readの操作時に呼び出しとwriteの差は多くなく、異なる点はreadがリングbufのデータを読み取ることであり、データが到着すると中断が発生し、サービス関数を中断して自動的にデータを受信し、bufに格納することである.前に書いたときはbufに自発的にデータを書きますので、まず割り込みサービス関数から入力をどのように受信するかを見てみましょう.
static unsigned int sw_uart_handle_rx(struct sw_uart_port *sw_uport, unsigned int lsr)
{
    struct tty_struct *tty = sw_uport->port.state->port.tty;
    unsigned char ch = 0;
    int max_count = 256;
    char flag;

    do {
        if (likely(lsr & SW_UART_LSR_DR)) {
            ch = serial_in(&sw_uport->port, SW_UART_RBR);

       ..........................

        if (uart_handle_sysrq_char(&sw_uport->port, ch))
            goto ignore_char;
        uart_insert_char(&sw_uport->port, lsr, SW_UART_LSR_OE, ch, flag);

       ............................

 

 
レジスタから文字を読み出してchに割り当て、uart_insert_charはこの文字を処理します.実はこのデータをuart層に置きます.
void uart_insert_char(struct uart_port *port, unsigned int status,
         unsigned int overrun, unsigned int ch, unsigned int flag)
{
    struct tty_struct *tty = port->state->port.tty;

    if ((status & port->ignore_status_mask & ~overrun) == 0)
        tty_insert_flip_char(tty, ch, flag);

    /*
     * Overrun is special.  Since it's reported immediately,
     * it doesn't affect the current character.
     */
    if (status & ~port->ignore_status_mask & overrun)
        tty_insert_flip_char(tty, 0, TTY_OVERRUN);
}

 
static inline int tty_insert_flip_char(struct tty_struct *tty,                                                                                                 
                    unsigned char ch, char flag)
{
    struct tty_buffer *tb = tty->buf.tail;
    if (tb && tb->used < tb->size) {
        tb->flag_buf_ptr[tb->used] = flag;
        tb->char_buf_ptr[tb->used++] = ch;
        return 1;
    }
    return tty_insert_flip_string_flags(tty, &ch, &flag, 1);
}

現在のtty_bufferスペースが足りない場合にtty_を呼び出すinsert_flip_string_flags、この関数で次のttyを探します.buffer、次のtty_にデータを配置bufferのchar_buf_ptrの中.ここでbuf_ptrのデータはどのように線路規程のread_に置かれますか?bufの中のは?それはtty open操作の時、tty_init_dev -> initialize_tty_struct -> initialize_tty_struct -> tty_buffer_init:
void tty_buffer_init(struct tty_struct *tty)                                                                                                                   
{
    spin_lock_init(&tty->buf.lock);
    tty->buf.head = NULL;
    tty->buf.tail = NULL;
    tty->buf.free = NULL;
    tty->buf.memory_used = 0;
    INIT_WORK(&tty->buf.work, flush_to_ldisc);
}


ワークキューが初期化されていることがわかりますが、ワークキューを呼び出すタイミングは、ここで操作が完了した後、sw_を続行します.uart_handle_rx関数のtty_flip_buffer_Push時:
void tty_flip_buffer_push(struct tty_struct *tty)
{
    unsigned long flags;
    spin_lock_irqsave(&tty->buf.lock, flags);
    if (tty->buf.tail != NULL)
        tty->buf.tail->commit = tty->buf.tail->used;
    spin_unlock_irqrestore(&tty->buf.lock, flags);

    if (tty->low_latency)
        flush_to_ldisc(&tty->buf.work);
    else
        schedule_work(&tty->buf.work);
}
EXPORT_SYMBOL(tty_flip_buffer_push); 

ここでは、リンク・ルール・レイヤにデータを報告する2つの方法がありますが、実際にはほとんど差がありません.これにより、データはリンク・ルールに報告され、このワークキュー関数を見てみましょう.
static void flush_to_ldisc(struct work_struct *work)
{
    ..........................

                count = tty->receive_room;
            char_buf = head->char_buf_ptr + head->read;
            flag_buf = head->flag_buf_ptr + head->read;
            head->read += count;
            spin_unlock_irqrestore(&tty->buf.lock, flags);
            disc->ops->receive_buf(tty, char_buf,
                            flag_buf, count);

    ............................

リンク規程のreceive_buf関数:
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,                                                                                 
                  char *fp, int count)
{
    const unsigned char *p;
    char *f, flags = TTY_NORMAL;
    int i;
    char    buf[64];
    unsigned long cpuflags;

    if (!tty->read_buf)
        return;

    ................................

        memcpy(tty->read_buf + tty->read_head, cp, i);
        tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
        tty->read_cnt += i;


    ...........................

        }
        if (tty->ops->flush_chars)
            tty->ops->flush_chars(tty);
    }

    n_tty_set_room(tty);



明らかにmemcpyがread_にデータをコピーしたbufにあります.
次に、read関数がどのようにデータを読み出すかを上層部から振り返ってみましょう.プロセスもttyコア->リンク規程です.
static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
             unsigned char __user *buf, size_t nr)
{
    unsigned char __user *b = buf; 

    .......................................

                c = tty->read_buf[tty->read_tail];

    ...............................

            uncopied = copy_from_read_buf(tty, &b, &nr);
            uncopied += copy_from_read_buf(tty, &b, &nr);

    ..............................
 

 
static int copy_from_read_buf(struct tty_struct *tty,
                      unsigned char __user **b,
                      size_t *nr)

{
    int retval;
    size_t n;
    unsigned long flags;

    retval = 0;
    spin_lock_irqsave(&tty->read_lock, flags);
    n = min(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail);
    n = min(*nr, n);
    spin_unlock_irqrestore(&tty->read_lock, flags);
    if (n) {
        retval = copy_to_user(*b, &tty->read_buf[tty->read_tail], n);                                                                                          
        n -= retval;
        tty_audit_add_data(tty, &tty->read_buf[tty->read_tail], n);
        spin_lock_irqsave(&tty->read_lock, flags);
        tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
        tty->read_cnt -= n;
        /* Turn single EOF into zero-length read */
        if (L_EXTPROC(tty) && tty->icanon && n == 1) {
            if (!tty->read_cnt && (*b)[n-1] == EOF_CHAR(tty))
                n--;
        }
        spin_unlock_irqrestore(&tty->read_lock, flags);
        *b += n;
        *nr -= n;
    }
    return retval;
} 

copyが見えましたto_user関数はread_をbufのデータはユーザ空間にコピーされた.ここまで来ると、シリアルの読み書きの流れがはっきりしていて、一目瞭然です.