Chinaunix首页 | 论坛 | 博客
  • 博客访问: 20889
  • 博文数量: 7
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 10
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-02 19:09
文章分类
文章存档

2013年(7)

我的朋友

分类: LINUX

2013-12-28 14:51:31

原文地址:/dev/console打开流程分析 作者:lwchsz

static int tty_open(struct inode *inode, struct file *filp)
{
    int ret;

    lock_kernel();
    ret = __tty_open(inode, filp);
    unlock_kernel();
    return ret;
}

static int __tty_open(struct inode *inode, struct file *filp)
{
    struct tty_struct *tty = NULL;
    int noctty, retval;
    struct tty_driver *driver;
    int index;
    dev_t device = inode->i_rdev;
    unsigned saved_flags = filp->f_flags;

    nonseekable_open(inode, filp);

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

    mutex_lock(&tty_mutex);

    if (device == MKDEV(TTYAUX_MAJOR, 0)) {
        tty = get_current_tty();
        if (!tty) {
            mutex_unlock(&tty_mutex);
            return -ENXIO;
        }
        driver = tty_driver_kref_get(tty->driver);
        index = tty->index;
        filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
        /* noctty = 1; */
        /* FIXME: Should we take a driver reference ? */
        tty_kref_put(tty);
        goto got_driver;
    }
#ifdef CONFIG_VT
    if (device == MKDEV(TTY_MAJOR, 0)) {
        extern struct tty_driver *console_driver;
        driver = tty_driver_kref_get(console_driver);
        index = fg_console;
        noctty = 1;
        goto got_driver;
    }
#endif
    if (device == MKDEV(TTYAUX_MAJOR, 1)) {//打开/dev/console
        struct tty_driver *console_driver = console_device(&index);//定位具体控制台设备
        if (console_driver) {
            driver = tty_driver_kref_get(console_driver);
            if (driver) {
                /* Don't let /dev/console block */
                filp->f_flags |= O_NONBLOCK;
                noctty = 1;
                goto got_driver;
            }
        }
        mutex_unlock(&tty_mutex);
        return -ENODEV;
    }

    driver = get_tty_driver(device, &index);
    if (!driver) {
        mutex_unlock(&tty_mutex);
        return -ENODEV;
    }
got_driver:
    if (!tty) {
        /* check whether we're reopening an existing tty */
        tty = tty_driver_lookup_tty(driver, inode, index);

        if (IS_ERR(tty)) {
            mutex_unlock(&tty_mutex);
            return PTR_ERR(tty);
        }
    }

    if (tty) {
        retval = tty_reopen(tty);
        if (retval)
            tty = ERR_PTR(retval);
    } else
        tty = tty_init_dev(driver, index, 0);//第一次打开设备时

    mutex_unlock(&tty_mutex);
    tty_driver_kref_put(driver);
    if (IS_ERR(tty))
        return PTR_ERR(tty);

    filp->private_data = tty;
    file_move(filp, &tty->tty_files);
    check_tty_count(tty, "tty_open");
    if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
        tty->driver->subtype == PTY_TYPE_MASTER)
        noctty = 1;
#ifdef TTY_DEBUG_HANGUP
    printk(KERN_DEBUG "opening %s...", tty->name);
#endif
    if (!retval) {
        if (tty->ops->open)//这里其实是uart_open
            retval = tty->ops->open(tty, filp);
        else
            retval = -ENODEV;
    }
    filp->f_flags = saved_flags;

    if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
                        !capable(CAP_SYS_ADMIN))
        retval = -EBUSY;

    if (retval) {
#ifdef TTY_DEBUG_HANGUP
        printk(KERN_DEBUG "error %d in opening %s...", retval,
               tty->name);
#endif
        tty_release_dev(filp);
        if (retval != -ERESTARTSYS)
            return retval;
        if (signal_pending(current))
            return retval;
        schedule();
        /*
         * Need to reset f_op in case a hangup happened.
         */
        if (filp->f_op == &hung_up_tty_fops)
            filp->f_op = &tty_fops;
        goto retry_open;
    }

    mutex_lock(&tty_mutex);
    spin_lock_irq(¤t->sighand->siglock);
    if (!noctty &&
        current->signal->leader &&
        !current->signal->tty &&
        tty->session == NULL)
        __proc_set_tty(current, tty);
    spin_unlock_irq(¤t->sighand->siglock);
    mutex_unlock(&tty_mutex);
    return 0;
}

struct tty_driver *console_device(int *index)
{
    struct console *c;
    struct tty_driver *driver = NULL;

    acquire_console_sem();
    for (c = console_drivers; c != NULL; c = c->next) {
        if (!c->device)
            continue;
        driver = c->device(c, index);//对8250就是uart_console_device
        if (driver)
            break;
    }
    release_console_sem();
    return driver;
}

struct tty_driver *uart_console_device(struct console *co, int *index)
{
    struct uart_driver *p = co->data;
    *index = co->index;//返回0
    return p->tty_driver;
}

static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
        struct inode *inode, int idx)
{
    struct tty_struct *tty;

    if (driver->ops->lookup)//对uart操作集lookup为null
        return driver->ops->lookup(driver, inode, idx);

    tty = driver->ttys[idx];
    return tty;
}

struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
                                int first_ok)
{
    struct tty_struct *tty;
    int retval;

    /* Check if pty master is being opened multiple times */
    if (driver->subtype == PTY_TYPE_MASTER &&
        (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
        return ERR_PTR(-EIO);

    /*
     * First time open is complex, especially for PTY devices.
     * This code guarantees that either everything succeeds and the
     * TTY is ready for operation, or else the table slots are vacated
     * and the allocated memory released.  (Except that the termios
     * and locked termios may be retained.)
     */

    if (!try_module_get(driver->owner))
        return ERR_PTR(-ENODEV);

    tty = alloc_tty_struct();//分配tty
    if (!tty)
        goto fail_no_mem;
    initialize_tty_struct(tty, driver, idx);//初始化tty

    retval = tty_driver_install_tty(driver, tty);//初始化tty相关参数,安装tty到driver
    if (retval < 0) {
        free_tty_struct(tty);
        module_put(driver->owner);
        return ERR_PTR(retval);
    }

    /*
     * Structures all installed ... call the ldisc open routines.
     * If we fail here just call release_tty to clean up.  No need
     * to decrement the use counts, as release_tty doesn't care.
     */

    retval = tty_ldisc_setup(tty, tty->link);//调用tty_ldisc_setup, 在串口情景下tty->link为NULL
    if (retval)
        goto release_mem_out;
    return tty;

fail_no_mem:
    module_put(driver->owner);
    return ERR_PTR(-ENOMEM);

    /* call the tty release_tty routine to clean out this slot */
release_mem_out:
    if (printk_ratelimit())
        printk(KERN_INFO "tty_init_dev: ldisc open failed, "
                 "clearing slot %d\n", idx);
    release_tty(tty, idx);
    return ERR_PTR(retval);
}

void initialize_tty_struct(struct tty_struct *tty,
        struct tty_driver *driver, int idx)
{
    memset(tty, 0, sizeof(struct tty_struct));
    kref_init(&tty->kref);
    tty->magic = TTY_MAGIC;
    tty_ldisc_init(tty);//初始化tty的ldisc
    tty->session = NULL;
    tty->pgrp = NULL;
    tty->overrun_time = jiffies;
    tty->buf.head = tty->buf.tail = NULL;
    tty_buffer_init(tty);初始化tty的buff
    mutex_init(&tty->termios_mutex);
    mutex_init(&tty->ldisc_mutex);
    init_waitqueue_head(&tty->write_wait);
    init_waitqueue_head(&tty->read_wait);
    INIT_WORK(&tty->hangup_work, do_tty_hangup);
    mutex_init(&tty->atomic_read_lock);
    mutex_init(&tty->atomic_write_lock);
    mutex_init(&tty->output_lock);
    mutex_init(&tty->echo_lock);
    spin_lock_init(&tty->read_lock);
    spin_lock_init(&tty->ctrl_lock);
    INIT_LIST_HEAD(&tty->tty_files);
    INIT_WORK(&tty->SAK_work, do_SAK_work);

    tty->driver = driver;
    tty->ops = driver->ops;//初始化tty的ops,对UART这里设备就是uart_ops
    tty->index = idx;
    tty_line_name(driver, idx, tty->name);
}

void tty_ldisc_init(struct tty_struct *tty)
{
    struct tty_ldisc *ld = tty_ldisc_get(N_TTY);//其实就是tty_ldisc.c中tty_ldisc_N_TTY
    if (IS_ERR(ld))
        panic("n_tty: init_tty");
    tty_ldisc_assign(tty, ld);
}

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_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
}

static int tty_driver_install_tty(struct tty_driver *driver,
                        struct tty_struct *tty)
{
    int idx = tty->index;

    if (driver->ops->install)//uart_ops中install为NULL
        return driver->ops->install(driver, tty);

    if (tty_init_termios(tty) == 0) {
        tty_driver_kref_get(driver);
        tty->count++;
        driver->ttys[idx] = tty;//把tty保存到ttys中
        return 0;
    }
    return -ENOMEM;
}

int tty_init_termios(struct tty_struct *tty)
{
    struct ktermios *tp;
    int idx = tty->index;

    tp = tty->driver->termios[idx];//初始为NULL
    if (tp == NULL) {
        tp = kzalloc(sizeof(struct ktermios[2]), GFP_KERNEL);//分配ktermios
        if (tp == NULL)
            return -ENOMEM;
        memcpy(tp, &tty->driver->init_termios,
                        sizeof(struct ktermios));
        tty->driver->termios[idx] = tp;
    }
    tty->termios = tp;
    tty->termios_locked = tp + 1;

    /* Compatibility until drivers always set this */
    tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);//设置初始波特率,此时为//9600,其在uart_register_driver被初始化
    tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
    return 0;
}

int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
    struct tty_ldisc *ld = tty->ldisc;
    int retval;

    retval = tty_ldisc_open(tty, ld);//调用tty_ldisc_open
    if (retval)
        return retval;

    if (o_tty) {
        retval = tty_ldisc_open(o_tty, o_tty->ldisc);
        if (retval) {
            tty_ldisc_close(tty, ld);
            return retval;
        }
        tty_ldisc_enable(o_tty);
    }
    tty_ldisc_enable(tty);
    return 0;
}

static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
    WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
    if (ld->ops->open)
        return ld->ops->open(tty);//其实就是n_tty_open
    return 0;
}

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
};

static int n_tty_open(struct tty_struct *tty)
{
    if (!tty)
        return -EINVAL;

    /* These are ugly. Currently a malloc failure here can panic */
    if (!tty->read_buf) {//分配缓冲区
        tty->read_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);
        if (!tty->read_buf)
            return -ENOMEM;
    }
    if (!tty->echo_buf) {
        tty->echo_buf = kzalloc(N_TTY_BUF_SIZE, GFP_KERNEL);

        if (!tty->echo_buf)
            return -ENOMEM;
    }
    reset_buffer_flags(tty);//设置buff标志
    tty->column = 0;
    n_tty_set_termios(tty, NULL);
    tty->minimum_to_wake = 1;
    tty->closing = 0;
    return 0;
}

static void n_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
{
    int canon_change = 1;
    BUG_ON(!tty);

    if (old)
        canon_change = (old->c_lflag ^ tty->termios->c_lflag) & ICANON;
    if (canon_change) {
        memset(&tty->read_flags, 0, sizeof tty->read_flags);
        tty->canon_head = tty->read_tail;
        tty->canon_data = 0;
        tty->erasing = 0;
    }
//tty中各种标志取自初始化的 tty_std_termios,c_lflag含有ICANON
    if (canon_change && !L_ICANON(tty) && tty->read_cnt)
        wake_up_interruptible(&tty->read_wait);

    tty->icanon = (L_ICANON(tty) != 0);//   tty->icanon=1
    if (test_bit(TTY_HW_COOK_IN, &tty->flags)) {//false
        tty->raw = 1;
        tty->real_raw = 1;
        n_tty_set_room(tty);
        return;
    }
    if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
        I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
        I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
        I_PARMRK(tty)) {
        memset(tty->process_char_map, 0, 256/8);

        if (I_IGNCR(tty) || I_ICRNL(tty))//tty_std_termios,c_iflag含有ICRNL
            set_bit('\r', tty->process_char_map);
        if (I_INLCR(tty))//tty_std_termios,c_iflag不含有INLCR
            set_bit('\n', tty->process_char_map);

        if (L_ICANON(tty)) {//tty_std_termios,c_lflag含有ICANON
            set_bit(ERASE_CHAR(tty), tty->process_char_map);
            set_bit(KILL_CHAR(tty), tty->process_char_map);
            set_bit(EOF_CHAR(tty), tty->process_char_map);
            set_bit('\n', tty->process_char_map);
            set_bit(EOL_CHAR(tty), tty->process_char_map);
            if (L_IEXTEN(tty)) {//条件TRUE
                set_bit(WERASE_CHAR(tty),
                    tty->process_char_map);
                set_bit(LNEXT_CHAR(tty),
                    tty->process_char_map);
                set_bit(EOL2_CHAR(tty),
                    tty->process_char_map);
                if (L_ECHO(tty))
                    set_bit(REPRINT_CHAR(tty),
                        tty->process_char_map);
            }
        }
        if (I_IXON(tty)) {//条件TRUE
            set_bit(START_CHAR(tty), tty->process_char_map);
            set_bit(STOP_CHAR(tty), tty->process_char_map);
        }
        if (L_ISIG(tty)) {//条件TRUE
            set_bit(INTR_CHAR(tty), tty->process_char_map);
            set_bit(QUIT_CHAR(tty), tty->process_char_map);
            set_bit(SUSP_CHAR(tty), tty->process_char_map);
        }
        clear_bit(__DISABLED_CHAR, tty->process_char_map);
        tty->raw = 0;
        tty->real_raw = 0;
    } else {
        tty->raw = 1;
        if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
            (I_IGNPAR(tty) || !I_INPCK(tty)) &&
            (tty->driver->flags & TTY_DRIVER_REAL_RAW))
            tty->real_raw = 1;
        else
            tty->real_raw = 0;
    }
    n_tty_set_room(tty);
    /* The termios change make the tty ready for I/O */
    wake_up_interruptible(&tty->write_wait);
    wake_up_interruptible(&tty->read_wait);
}

struct ktermios tty_std_termios = {    /* for the benefit of tty drivers  */
    .c_iflag = ICRNL | IXON,
    .c_oflag = OPOST | ONLCR,
    .c_cflag = B38400 | CS8 | CREAD | HUPCL,
    .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
           ECHOCTL | ECHOKE | IEXTEN,
    .c_cc = INIT_C_CC,
    .c_ispeed = 38400,
    .c_ospeed = 38400
};

static int uart_open(struct tty_struct *tty, struct file *filp)
{
    struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
    struct uart_state *state;
    int retval, line = tty->index;

    BUG_ON(!kernel_locked());
    pr_debug("uart_open(%d) called\n", line);

    /*
     * tty->driver->num won't change, so we won't fail here with
     * tty->driver_data set to something non-NULL (and therefore
     * we won't get caught by uart_close()).
     */
    retval = -ENODEV;
    if (line >= tty->driver->num)//打开设备号不能超过设备总数
        goto fail;

    /*
     * We take the semaphore inside uart_get to guarantee that we won't
     * be re-entered while allocating the info structure, or while we
     * request any IRQs that the driver may need.  This also has the nice
     * side-effect that it delays the action of uart_hangup, so we can
     * guarantee that info->port.tty will always contain something reasonable.
     */
    state = uart_get(drv, line);//获取打开端口对应的state
    if (IS_ERR(state)) {
        retval = PTR_ERR(state);
        goto fail;
    }

    /*
     * Once we set tty->driver_data here, we are guaranteed that
     * uart_close() will decrement the driver module use count.
     * Any failures from here onwards should not touch the count.
     */
    tty->driver_data = state;
    state->port->info = &state->info;
    tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0;
    tty->alt_speed = 0;
    state->info.port.tty = tty;

    /*
     * If the port is in the middle of closing, bail out now.
     */
    if (tty_hung_up_p(filp)) {
        retval = -EAGAIN;
        state->count--;
        mutex_unlock(&state->mutex);
        goto fail;
    }

    /*
     * Make sure the device is in D0 state.
     */
    if (state->count == 1)//第一次打开需对端口上电,count 在uart_get已经被加1
        uart_change_pm(state, 0);

    /*
     * Start up the serial port.
     *///对uart做初始化
    retval = uart_startup(state, 0);

    /*
     * If we succeeded, wait until the port is ready.
     */
    if (retval == 0)
        retval = uart_block_til_ready(filp, state);
    mutex_unlock(&state->mutex);

    /*
     * If this is the first open to succeed, adjust things to suit.
     */
    if (retval == 0 && !(state->info.flags & UIF_NORMAL_ACTIVE)) {
        state->info.flags |= UIF_NORMAL_ACTIVE;

        uart_update_termios(state);
    }

 fail:
    return retval;
}

static int uart_startup(struct uart_state *state, int init_hw)
{
    struct uart_info *info = &state->info;
    struct uart_port *port = state->port;
    unsigned long page;
    int retval = 0;

    if (info->flags & UIF_INITIALIZED)
        return 0;

    /*
     * Set the TTY IO error marker - we will only clear this
     * once we have successfully opened the port.  Also set
     * up the tty->alt_speed kludge
     */
    set_bit(TTY_IO_ERROR, &info->port.tty->flags);
//port->type为16550A
    if (port->type == PORT_UNKNOWN)
        return 0;

    /*
     * Initialise and allocate the transmit and temporary
     * buffer.
     */
    if (!info->xmit.buf) {//分配输出缓冲区
        /* This is protected by the per port mutex */
        page = get_zeroed_page(GFP_KERNEL);
        if (!page)
            return -ENOMEM;

        info->xmit.buf = (unsigned char *) page;
        uart_circ_clear(&info->xmit);
    }
//调用端口的startup,此时为serial8250_startup
    retval = port->ops->startup(port);
    if (retval == 0) {
        if (init_hw) {//init_hw为0
            /*
             * Initialise the hardware port settings.
             */
            uart_change_speed(state, NULL);//设置端口速率

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

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

        info->flags |= UIF_INITIALIZED;

        clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
    }

    if (retval && capable(CAP_SYS_ADMIN))
        retval = 0;

    return retval;
}

static int serial8250_startup(struct uart_port *port)
{
    struct uart_8250_port *up = (struct uart_8250_port *)port;
    unsigned long flags;
#if !defined(CONFIG_ARCH_CNS3XXX)
    unsigned char lsr;
#endif
    unsigned char iir;
    int retval;
//up->port.type=16550A
    up->capabilities = uart_config[up->port.type].flags;
    up->mcr = 0;

    if (up->port.iotype != up->cur_iotype)
        set_io_from_upio(port);

    if (up->port.type == PORT_16C950) {
        /* Wake up and initialize UART */
        up->acr = 0;
        serial_outp(up, UART_LCR, 0xBF);
        serial_outp(up, UART_EFR, UART_EFR_ECB);
        serial_outp(up, UART_IER, 0);
        serial_outp(up, UART_LCR, 0);
        serial_icr_write(up, UART_CSR, 0); /* Reset the UART */
        serial_outp(up, UART_LCR, 0xBF);
        serial_outp(up, UART_EFR, UART_EFR_ECB);
        serial_outp(up, UART_LCR, 0);
    }

#ifdef CONFIG_SERIAL_8250_RSA//无定义
    /*
     * If this is an RSA port, see if we can kick it up to the
     * higher speed clock.
     */
    enable_rsa(up);
#endif

    /*
     * Clear the FIFO buffers and disable them.
     * (they will be reenabled in set_termios())
     */
    serial8250_clear_fifos(up);//清FIFO

    /*
     * Clear the interrupt registers.
     */
    (void) serial_inp(up, UART_LSR);
    (void) serial_inp(up, UART_RX);
    (void) serial_inp(up, UART_IIR);
    (void) serial_inp(up, UART_MSR);

    /*
     * At this point, there's no way the LSR could still be 0xff;
     * if it is, then bail out, because there's likely no UART
     * here.
     */
    if (!(up->port.flags & UPF_BUGGY_UART) &&
        (serial_inp(up, UART_LSR) == 0xff)) {
        printk(KERN_INFO "ttyS%d: LSR safety check engaged!\n",
               serial_index(&up->port));
        return -ENODEV;
    }

    /*
     * For a XR16C850, we need to set the trigger levels
     */
    if (up->port.type == PORT_16850) {
        unsigned char fctr;

        serial_outp(up, UART_LCR, 0xbf);

        fctr = serial_inp(up, UART_FCTR) & ~(UART_FCTR_RX|UART_FCTR_TX);
        serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_RX);
        serial_outp(up, UART_TRG, UART_TRG_96);
        serial_outp(up, UART_FCTR, fctr | UART_FCTR_TRGD | UART_FCTR_TX);
        serial_outp(up, UART_TRG, UART_TRG_96);

        serial_outp(up, UART_LCR, 0);
    }
//此时up->port.irq=45,所以条件为真
    if (is_real_interrupt(up->port.irq)) {
        unsigned char iir1;
        /*
         * Test for UARTs that do not reassert THRE when the
         * transmitter is idle and the interrupt has already
         * been cleared.  Real 16550s should always reassert
         * this interrupt whenever the transmitter is idle and
         * the interrupt is enabled.  Delays are necessary to
         * allow register changes to become visible.
         */
        spin_lock_irqsave(&up->port.lock, flags);
        if (up->port.flags & UPF_SHARE_IRQ)
            disable_irq_nosync(up->port.irq);

        wait_for_xmitr(up, UART_LSR_THRE);
        serial_out_sync(up, UART_IER, UART_IER_THRI);
        udelay(1); /* allow THRE to set */
        iir1 = serial_in(up, UART_IIR);
        serial_out(up, UART_IER, 0);
        serial_out_sync(up, UART_IER, UART_IER_THRI);
        udelay(1); /* allow a working UART time to re-assert THRE */
        iir = serial_in(up, UART_IIR);
        serial_out(up, UART_IER, 0);

        if (up->port.flags & UPF_SHARE_IRQ)
            enable_irq(up->port.irq);
        spin_unlock_irqrestore(&up->port.lock, flags);

        /*
         * If the interrupt is not reasserted, setup a timer to
         * kick the UART on a regular basis.
         */
        if (!(iir1 & UART_IIR_NO_INT) && (iir & UART_IIR_NO_INT)) {
            up->bugs |= UART_BUG_THRE;//对cns3xxx会设置这个
            pr_debug("ttyS%d - using backup timer\n",
                 serial_index(port));
        }
    }

    /*
     * The above check will only give an accurate result the first time
     * the port is opened so this value needs to be preserved.
     */
    if (up->bugs & UART_BUG_THRE) {
        up->timer.function = serial8250_backup_timeout;
        up->timer.data = (unsigned long)up;
        mod_timer(&up->timer, jiffies +
              poll_timeout(up->port.timeout) + HZ / 5);//启动timer
    }

    /*
     * If the "interrupt" for this port doesn't correspond with any
     * hardware interrupt, we use a timer-based system.  The original
     * driver used to do this with IRQ0.
     */
    if (!is_real_interrupt(up->port.irq)) {
        up->timer.data = (unsigned long)up;
        mod_timer(&up->timer, jiffies + poll_timeout(up->port.timeout));
    } else {
        retval = serial_link_irq_chain(up);//调用安装IRQ出来函数
        if (retval)
            return retval;
    }

    /*
     * Now, initialize the UART
     */
    serial_outp(up, UART_LCR, UART_LCR_WLEN8);//8bit数据位

    spin_lock_irqsave(&up->port.lock, flags);
    if (up->port.flags & UPF_FOURPORT) {
        if (!is_real_interrupt(up->port.irq))
            up->port.mctrl |= TIOCM_OUT1;
    } else
        /*
         * Most PC uarts need OUT2 raised to enable interrupts.
         */
        if (is_real_interrupt(up->port.irq))
            up->port.mctrl |= TIOCM_OUT2;

    serial8250_set_mctrl(&up->port, up->port.mctrl);

    /* Serial over Lan (SoL) hack:
       Intel 8257x Gigabit ethernet chips have a
       16550 emulation, to be used for Serial Over Lan.
       Those chips take a longer time than a normal
       serial device to signalize that a transmission
       data was queued. Due to that, the above test generally
       fails. One solution would be to delay the reading of
       iir. However, this is not reliable, since the timeout
       is variable. So, let's just don't test if we receive
       TX irq. This way, we'll never enable UART_BUG_TXEN.
     */
    if (up->port.flags & UPF_NO_TXEN_TEST)
        goto dont_test_tx_en;

#if !defined(CONFIG_ARCH_CNS3XXX)
    /*
     * Do a quick test to see if we receive an
     * interrupt when we enable the TX irq.
     */
    serial_outp(up, UART_IER, UART_IER_THRI);
    lsr = serial_in(up, UART_LSR);
    iir = serial_in(up, UART_IIR);
    serial_outp(up, UART_IER, 0);

    if (lsr & UART_LSR_TEMT && iir & UART_IIR_NO_INT) {
        if (!(up->bugs & UART_BUG_TXEN)) {
            up->bugs |= UART_BUG_TXEN;
            pr_debug("ttyS%d - enabling bad tx status workarounds\n",
                 serial_index(port));
        }
    } else {
        up->bugs &= ~UART_BUG_TXEN;
    }
#endif

dont_test_tx_en:
    spin_unlock_irqrestore(&up->port.lock, flags);

    /*
     * Clear the interrupt registers again for luck, and clear the
     * saved flags to avoid getting false values from polling
     * routines or the previous session.
     */
    serial_inp(up, UART_LSR);
    serial_inp(up, UART_RX);
    serial_inp(up, UART_IIR);
    serial_inp(up, UART_MSR);
    up->lsr_saved_flags = 0;
    up->msr_saved_flags = 0;

    /*
     * Finally, enable interrupts.  Note: Modem status interrupts
     * are set via set_termios(), which will be occurring imminently
     * anyway, so we don't enable them here.
     */
    up->ier = UART_IER_RLSI | UART_IER_RDI;//使能接受数据中断
    serial_outp(up, UART_IER, up->ier);

    if (up->port.flags & UPF_FOURPORT) {
        unsigned int icp;
        /*
         * Enable interrupts on the AST Fourport board
         */
        icp = (up->port.iobase & 0xfe0) | 0x01f;
        outb_p(0x80, icp);
        (void) inb_p(icp);
    }

    return 0;
}

static const struct serial8250_config uart_config[] ={
    [PORT_UNKNOWN] = {
        .name        = "unknown",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_8250] = {
        .name        = "8250",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16450] = {
        .name        = "16450",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16550] = {
        .name        = "16550",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16550A] = {
        .name        = "16550A",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_CIRRUS] = {
        .name        = "Cirrus",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16650] = {
        .name        = "ST16650",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_16650V2] = {
        .name        = "ST16650V2",
        .fifo_size    = 32,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
                  UART_FCR_T_TRIG_00,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_16750] = {
        .name        = "TI16750",
        .fifo_size    = 64,
        .tx_loadsz    = 64,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10 |
                  UART_FCR7_64BYTE,
        .flags        = UART_CAP_FIFO | UART_CAP_SLEEP | UART_CAP_AFE,
    },
    [PORT_STARTECH] = {
        .name        = "Startech",
        .fifo_size    = 1,
        .tx_loadsz    = 1,
    },
    [PORT_16C950] = {
        .name        = "16C950/954",
        .fifo_size    = 128,
        .tx_loadsz    = 128,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_16654] = {
        .name        = "ST16654",
        .fifo_size    = 64,
        .tx_loadsz    = 32,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_01 |
                  UART_FCR_T_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_16850] = {
        .name        = "XR16850",
        .fifo_size    = 128,
        .tx_loadsz    = 128,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_CAP_EFR | UART_CAP_SLEEP,
    },
    [PORT_RSA] = {
        .name        = "RSA",
        .fifo_size    = 2048,
        .tx_loadsz    = 2048,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_11,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_NS16550A] = {
        .name        = "NS16550A",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_NATSEMI,
    },
    [PORT_XSCALE] = {
        .name        = "XScale",
        .fifo_size    = 32,
        .tx_loadsz    = 32,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO | UART_CAP_UUE,
    },
    [PORT_RM9000] = {
        .name        = "RM9000",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_OCTEON] = {
        .name        = "OCTEON",
        .fifo_size    = 64,
        .tx_loadsz    = 64,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10,
        .flags        = UART_CAP_FIFO,
    },
    [PORT_AR7] = {
        .name        = "AR7",
        .fifo_size    = 16,
        .tx_loadsz    = 16,
        .fcr        = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_00,
        .flags        = UART_CAP_FIFO | UART_CAP_AFE,
    },
}

static void serial8250_clear_fifos(struct uart_8250_port *p)
{
    if (p->capabilities & UART_CAP_FIFO) {
        serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO);
        serial_outp(p, UART_FCR, UART_FCR_ENABLE_FIFO |
                   UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT);
        serial_outp(p, UART_FCR, 0);
    }
}

static int serial_link_irq_chain(struct uart_8250_port *up)
{
    struct hlist_head *h;
    struct hlist_node *n;
    struct irq_info *i;
    int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0;

    mutex_lock(&hash_mutex);

    h = &irq_lists[up->port.irq % NR_IRQ_HASH];

    hlist_for_each(n, h) {
        i = hlist_entry(n, struct irq_info, node);
        if (i->irq == up->port.irq)
            break;
    }

    if (n == NULL) {
        i = kzalloc(sizeof(struct irq_info), GFP_KERNEL);
        if (i == NULL) {
            mutex_unlock(&hash_mutex);
            return -ENOMEM;
        }
        spin_lock_init(&i->lock);
        i->irq = up->port.irq;
        hlist_add_head(&i->node, h);
    }
    mutex_unlock(&hash_mutex);

    spin_lock_irq(&i->lock);

    if (i->head) {
        list_add(&up->list, i->head);
        spin_unlock_irq(&i->lock);

        ret = 0;
    } else {
        INIT_LIST_HEAD(&up->list);
        i->head = &up->list;
        spin_unlock_irq(&i->lock);

        ret = request_irq(up->port.irq, serial8250_interrupt,
                  irq_flags, "serial", i);//
        if (ret < 0)
            serial_do_unlink(i, up);
    }

    return ret;
}
阅读(2851) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~