Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1911671
  • 博文数量: 376
  • 博客积分: 2147
  • 博客等级: 大尉
  • 技术积分: 3642
  • 用 户 组: 普通用户
  • 注册时间: 2012-02-06 10:47
文章分类

全部博文(376)

文章存档

2019年(3)

2017年(28)

2016年(15)

2015年(17)

2014年(182)

2013年(16)

2012年(115)

我的朋友

分类: LINUX

2012-03-20 12:44:19

/* UART0 */
#ifdef CONFIG_SERIAL_8250_CONSOLE
static struct uart_port cns3xxx_serial_ports[] = {
    {
        .membase        = (char*) (CNS3XXX_UART0_BASE_VIRT),
        .mapbase        = (CNS3XXX_UART0_BASE),
        .irq            = IRQ_CNS3XXX_UART0,
        .iotype         = UPIO_MEM,
        .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
        .regshift       = 2,//用于访问寄存器做对偏移量进行移位运算
        .uartclk        = 24000000,//时钟24M
        .line           = 0,
        .type           = PORT_16550A,
        .fifosize       = 16
    },
    {
        .membase        = (char*) (CNS3XXX_UART1_BASE_VIRT),
        .mapbase        = (CNS3XXX_UART1_BASE),
        .irq            = IRQ_CNS3XXX_UART1,
        .iotype         = UPIO_MEM,
        .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
        .regshift       = 2,
        .uartclk        = 24000000,
        .line           = 1,
        .type           = PORT_16550A,
        .fifosize       = 16
    },
    {
        .membase        = (char*) (CNS3XXX_UART2_BASE_VIRT),
        .mapbase        = (CNS3XXX_UART2_BASE),
        .irq            = IRQ_CNS3XXX_UART2,
        .iotype         = UPIO_MEM,
        .flags          = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
        .regshift       = 2,
        .uartclk        = 24000000,
        .line           = 2,
        .type           = PORT_16550A,
        .fifosize       = 16
    },
};
#endif

static void __init cns3xxx_map_io(void)
{
    iotable_init(cns3xxx_io_desc, ARRAY_SIZE(cns3xxx_io_desc));

#ifdef CONFIG_SERIAL_8250_CONSOLE//cns3xx有三个16550兼容的串口,使用串口0做控制台,所以定义CONFIG_SERIAL_8250_CONSOLE
    cns3xxx_pwr_power_up(CNS3XXX_PWR_PLL(PLL_USB));
    early_serial_setup(&cns3xxx_serial_ports[0]);//对串口0进行初始化
#if (1 < CONFIG_SERIAL_8250_NR_UARTS)
    HAL_MISC_ENABLE_UART1_PINS();
    cns3xxx_pwr_clk_en(CNS3XXX_PWR_CLK_EN(UART1));
    cns3xxx_pwr_soft_rst(CNS3XXX_PWR_SOFTWARE_RST(UART1));
    early_serial_setup(&cns3xxx_serial_ports[1]);
#endif
#if (2 < CONFIG_SERIAL_8250_NR_UARTS)
    HAL_MISC_ENABLE_UART2_PINS();
    cns3xxx_pwr_clk_en(CNS3XXX_PWR_CLK_EN(UART2));
    cns3xxx_pwr_soft_rst(CNS3XXX_PWR_SOFTWARE_RST(UART2));
    early_serial_setup(&cns3xxx_serial_ports[2]);
#endif
#endif
}

/*
 * early_serial_setup - early registration for 8250 ports
 *
 * Setup an 8250 port structure prior to console initialisation.  Use
 * after console initialisation will cause undefined behaviour.
 */
int __init early_serial_setup(struct uart_port *port)
{
    struct uart_port *p;

    if (port->line >= ARRAY_SIZE(serial8250_ports))
        return -ENODEV;

    serial8250_isa_init_ports();
    p = &serial8250_ports[port->line].port;//对serial8250_ports【0】进行初始化,如UART虚拟地址等
    p->iobase       = port->iobase;
    p->membase      = port->membase;
    p->irq          = port->irq;
    p->uartclk      = port->uartclk;
    p->fifosize     = port->fifosize;
    p->regshift     = port->regshift;
    p->iotype       = port->iotype;
    p->flags        = port->flags;
    p->mapbase      = port->mapbase;
    p->private_data = port->private_data;
    p->type        = port->type;
    p->line        = port->line;

    set_io_from_upio(p);
    if (port->serial_in)
        p->serial_in = port->serial_in;
    if (port->serial_out)
        p->serial_out = port->serial_out;

    return 0;
}

static void __init serial8250_isa_init_ports(void)
{
    struct uart_8250_port *up;
    static int first = 1;
    int i;

    if (!first)
        return;
    first = 0;

    for (i = 0; i < nr_uarts; i++) {
        struct uart_8250_port *up = &serial8250_ports[i];

        up->port.line = i;
        spin_lock_init(&up->port.lock);

        init_timer(&up->timer);
        up->timer.function = serial8250_timeout;

        /*
         * ALPHA_KLUDGE_MCR needs to be killed.
         */
        up->mcr_mask = ~ALPHA_KLUDGE_MCR;
        up->mcr_force = ALPHA_KLUDGE_MCR;

        up->port.ops = &serial8250_pops;//设置UART操作函数集
    }

    for (i = 0, up = serial8250_ports;
         i < ARRAY_SIZE(old_serial_port) && i < nr_uarts;
         i++, up++) {
        up->port.iobase   = old_serial_port[i].port;
        up->port.irq      = irq_canonicalize(old_serial_port[i].irq);
        up->port.uartclk  = old_serial_port[i].baud_base * 16;
        up->port.flags    = old_serial_port[i].flags;
        up->port.hub6     = old_serial_port[i].hub6;
        up->port.membase  = old_serial_port[i].iomem_base;
        up->port.iotype   = old_serial_port[i].io_type;
        up->port.regshift = old_serial_port[i].iomem_reg_shift;
        set_io_from_upio(&up->port);
        if (share_irqs)
            up->port.flags |= UPF_SHARE_IRQ;
    }
}

static struct uart_ops serial8250_pops = {
    .tx_empty    = serial8250_tx_empty,
    .set_mctrl    = serial8250_set_mctrl,
    .get_mctrl    = serial8250_get_mctrl,
    .stop_tx    = serial8250_stop_tx,
    .start_tx    = serial8250_start_tx,
    .stop_rx    = serial8250_stop_rx,
    .enable_ms    = serial8250_enable_ms,
    .break_ctl    = serial8250_break_ctl,
    .startup    = serial8250_startup,
    .shutdown    = serial8250_shutdown,
    .set_termios    = serial8250_set_termios,
    .pm        = serial8250_pm,
    .type        = serial8250_type,
    .release_port    = serial8250_release_port,
    .request_port    = serial8250_request_port,
    .config_port    = serial8250_config_port,
    .verify_port    = serial8250_verify_port,
#ifdef CONFIG_CONSOLE_POLL
    .poll_get_char = serial8250_get_poll_char,
    .poll_put_char = serial8250_put_poll_char,
#endif
};

//两次调用register_console,第一次在vt.c 中con_init注册虚拟控制台,第一次在8250.c中serial8250_console_init注册串口控制台。
void register_console(struct console *console)
{
    int i;
    unsigned long flags;
    struct console *bootconsole = NULL;

    if (console_drivers) {
        if (console->flags & CON_BOOT)//对8250无定义
            return;
        if (console_drivers->flags & CON_BOOT)
            bootconsole = console_drivers;
    }
//第一次注册时设置preferred_console,对set bootargs root=/dev/ram0 rw init=/linuxrc mem=64M //console=ttyS0,115200 lpj=4188160 user_debug=31 console=tty0 ecb_bootflags=1而言,preferred_console=1
    if (preferred_console < 0 || bootconsole || !console_drivers)
        preferred_console = selected_console;

    if (console->early_setup)//对8250就是serial8250_console_early_setup
        console->early_setup();

    /*
     *    See if we want to use this console driver. If we
     *    didn't select a console we take the first one
     *    that registers here.
     */
    if (preferred_console < 0) {
        if (console->index < 0)
            console->index = 0;
        if (console->setup == NULL ||
            console->setup(console, NULL) == 0) {
            console->flags |= CON_ENABLED;
            if (console->device) {
                console->flags |= CON_CONSDEV;
                preferred_console = 0;
            }
        }
    }

    /*
     *    See if this console matches one we selected on
     *    the command line.
     */
    for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
            i++) {
        if (strcmp(console_cmdline[i].name, console->name) != 0)
            continue;
        if (console->index >= 0 &&
            console->index != console_cmdline[i].index)
            continue;
        if (console->index < 0)
            console->index = console_cmdline[i].index;//console->index=0,因为我们使用ttyS0
#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
        if (console_cmdline[i].brl_options) {
            console->flags |= CON_BRL;
            braille_register_console(console,
                    console_cmdline[i].index,
                    console_cmdline[i].options,
                    console_cmdline[i].brl_options);
            return;
        }
#endif
        if (console->setup &&//对8250调用serial8250_console_setup
            console->setup(console, console_cmdline[i].options) != 0)
            break;
        console->flags |= CON_ENABLED;
        console->index = console_cmdline[i].index;
        if (i == selected_console) {
            console->flags |= CON_CONSDEV;
            preferred_console = selected_console;
        }
        break;
    }

    if (!(console->flags & CON_ENABLED))
        return;

    if (bootconsole && (console->flags & CON_CONSDEV)) {
        printk(KERN_INFO "console handover: boot [%s%d] -> real [%s%d]\n",
               bootconsole->name, bootconsole->index,
               console->name, console->index);
        unregister_console(bootconsole);
        console->flags &= ~CON_PRINTBUFFER;
    } else {
        printk(KERN_INFO "console [%s%d] enabled\n",
               console->name, console->index);
    }

    /*
     *    Put this console in the list - keep the
     *    preferred driver at the head of the list.
     */
    acquire_console_sem();
    if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
        console->next = console_drivers;
        console_drivers = console;
        if (console->next)
            console->next->flags &= ~CON_CONSDEV;
    } else {
        console->next = console_drivers->next;
        console_drivers->next = console;
    }
    if (console->flags & CON_PRINTBUFFER) {//对8250flags 包含CON_PRINTBUFFER标志
        /*
         * release_console_sem() will print out the buffered messages
         * for us.
         */
        spin_lock_irqsave(&logbuf_lock, flags);
        con_start = log_start;//设置con_start指向LOG信息头,这样输出内核启动信息
        spin_unlock_irqrestore(&logbuf_lock, flags);
    }
    release_console_sem();
}

static struct console serial8250_console = {
    .name        = "ttyS",
    .write        = serial8250_console_write,
    .device        = uart_console_device,
    .setup        = serial8250_console_setup,
    .early_setup    = serial8250_console_early_setup,
    .flags        = CON_PRINTBUFFER,
    .index        = -1,
    .data        = &serial8250_reg,
};

static int __init serial8250_console_setup(struct console *co, char *options)
{
    struct uart_port *port;
#if defined(CONFIG_ARCH_CNS3XXX)
    int baud = 38400;
#else
    int baud = 9600;
#endif
    int bits = 8;
    int parity = 'n';
    int flow = 'n';

    /*
     * Check whether an invalid uart number has been specified, and
     * if so, search for the first available port that does have
     * console support.
     */
    if (co->index >= nr_uarts)
        co->index = 0;
    port = &serial8250_ports[co->index].port;
    if (!port->iobase && !port->membase)
        return -ENODEV;

    if (options)//分析UART选项
        uart_parse_options(options, &baud, &parity, &bits, &flow);

    return uart_set_options(port, co, baud, parity, bits, flow);//根据选项进行相应设置
}

/**
 *    uart_set_options - setup the serial console parameters
 *    @port: pointer to the serial ports uart_port structure
 *    @co: console pointer
 *    @baud: baud rate
 *    @parity: parity character - 'n' (none), 'o' (odd), 'e' (even)
 *    @bits: number of data bits
 *    @flow: flow control character - 'r' (rts)
 */
int
uart_set_options(struct uart_port *port, struct console *co,
         int baud, int parity, int bits, int flow)
{
    struct ktermios termios;
    static struct ktermios dummy;
    int i;

    /*
     * Ensure that the serial console lock is initialised
     * early.
     */
    spin_lock_init(&port->lock);
    lockdep_set_class(&port->lock, &port_lock_key);

    memset(&termios, 0, sizeof(struct ktermios));

    termios.c_cflag = CREAD | HUPCL | CLOCAL;

    /*
     * Construct a cflag setting.
     */
    for (i = 0; baud_rates[i].rate; i++)
        if (baud_rates[i].rate <= baud)
            break;

    termios.c_cflag |= baud_rates[i].cflag;

    if (bits == 7)
        termios.c_cflag |= CS7;
    else
        termios.c_cflag |= CS8;

    switch (parity) {
    case 'o': case 'O':
        termios.c_cflag |= PARODD;
        /*fall through*/
    case 'e': case 'E':
        termios.c_cflag |= PARENB;
        break;
    }

    if (flow == 'r')
        termios.c_cflag |= CRTSCTS;

    /*
     * some uarts on other side don't support no flow control.
     * So we set * DTR in host uart to make them happy
     */
    port->mctrl |= TIOCM_DTR;

    port->ops->set_termios(port, &termios, &dummy);//调用serial8250_set_termios对硬件进行操作
    /*
     * Allow the setting of the UART parameters with a NULL console
     * too:
     */
    if (co)
        co->cflag = termios.c_cflag;

    return 0;
}

static void
serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
               struct ktermios *old)
{
    struct uart_8250_port *up = (struct uart_8250_port *)port;
    unsigned char cval, fcr = 0;
    unsigned long flags;
    unsigned int baud, quot;

    switch (termios->c_cflag & CSIZE) {//判断多少位数据位
    case CS5:
        cval = UART_LCR_WLEN5;
        break;
    case CS6:
        cval = UART_LCR_WLEN6;
        break;
    case CS7:
        cval = UART_LCR_WLEN7;
        break;
    default:
    case CS8:
        cval = UART_LCR_WLEN8;
        break;
    }

    if (termios->c_cflag & CSTOPB)//是否两位停止位
        cval |= UART_LCR_STOP;
    if (termios->c_cflag & PARENB)//奇偶校验是否使能
        cval |= UART_LCR_PARITY;
    if (!(termios->c_cflag & PARODD))//奇校验
        cval |= UART_LCR_EPAR;
#ifdef CMSPAR
    if (termios->c_cflag & CMSPAR)
        cval |= UART_LCR_SPAR;
#endif

    /*
     * Ask the core to calculate the divisor for us.
     */
    baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);//获取波特率,得到baud =115200
    quot = serial8250_get_divisor(port, baud);//根据波特率计算分频系数得quot =13

    /*
     * Oxford Semi 952 rev B workaround
     */
    if (up->bugs & UART_BUG_QUOT && (quot & 0xff) == 0)
        quot++;

    if (up->capabilities & UART_CAP_FIFO && up->port.fifosize > 1) {//是否支持FIFO
        if (baud < 2400)
            fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
        else
            fcr = uart_config[up->port.type].fcr;
    }

    /*
     * MCR-based auto flow control.  When AFE is enabled, RTS will be
     * deasserted when the receive FIFO contains more characters than
     * the trigger, or the MCR RTS bit is cleared.  In the case where
     * the remote UART is not using CTS auto flow control, we must
     * have sufficient FIFO entries for the latency of the remote
     * UART to respond.  IOW, at least 32 bytes of FIFO.
     */
    if (up->capabilities & UART_CAP_AFE && up->port.fifosize >= 32) {//是否支持硬件流控
        up->mcr &= ~UART_MCR_AFE;
        if (termios->c_cflag & CRTSCTS)
            up->mcr |= UART_MCR_AFE;
    }

    /*
     * Ok, we're now changing the port state.  Do it with
     * interrupts disabled.
     */
    spin_lock_irqsave(&up->port.lock, flags);

    /*
     * Update the per-port timeout.
     */
    uart_update_timeout(port, termios->c_cflag, baud);

    up->port.read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
    if (termios->c_iflag & INPCK)
        up->port.read_status_mask |= UART_LSR_FE | UART_LSR_PE;
    if (termios->c_iflag & (BRKINT | PARMRK))
        up->port.read_status_mask |= UART_LSR_BI;

    /*
     * Characteres to ignore
     */
    up->port.ignore_status_mask = 0;
    if (termios->c_iflag & IGNPAR)
        up->port.ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
    if (termios->c_iflag & IGNBRK) {
        up->port.ignore_status_mask |= UART_LSR_BI;
        /*
         * If we're ignoring parity and break indicators,
         * ignore overruns too (for real raw support).
         */
        if (termios->c_iflag & IGNPAR)
            up->port.ignore_status_mask |= UART_LSR_OE;
    }

    /*
     * ignore all characters if CREAD is not set
     */
    if ((termios->c_cflag & CREAD) == 0)
        up->port.ignore_status_mask |= UART_LSR_DR;

    /*
     * CTS flow control flag and modem status interrupts
     */
    up->ier &= ~UART_IER_MSI;
    if (!(up->bugs & UART_BUG_NOMSR) &&
            UART_ENABLE_MS(&up->port, termios->c_cflag))
        up->ier |= UART_IER_MSI;
    if (up->capabilities & UART_CAP_UUE)
        up->ier |= UART_IER_UUE | UART_IER_RTOIE;

    serial_out(up, UART_IER, up->ier);

    if (up->capabilities & UART_CAP_EFR) {
        unsigned char efr = 0;
        /*
         * TI16C752/Startech hardware flow control.  FIXME:
         * - TI16C752 requires control thresholds to be set.
         * - UART_MCR_RTS is ineffective if auto-RTS mode is enabled.
         */
        if (termios->c_cflag & CRTSCTS)
            efr |= UART_EFR_CTS;

        serial_outp(up, UART_LCR, 0xBF);
        serial_outp(up, UART_EFR, efr);
    }

#ifdef CONFIG_ARCH_OMAP
    /* Workaround to enable 115200 baud on OMAP1510 internal ports */
    if (cpu_is_omap1510() && is_omap_port(up)) {
        if (baud == 115200) {
            quot = 1;
            serial_out(up, UART_OMAP_OSC_12M_SEL, 1);
        } else
            serial_out(up, UART_OMAP_OSC_12M_SEL, 0);
    }
#endif

    if (up->capabilities & UART_NATSEMI) {
        /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */
        serial_outp(up, UART_LCR, 0xe0);
    } else {
        serial_outp(up, UART_LCR, cval | UART_LCR_DLAB);/* set DLAB */
    }

    serial_dl_write(up, quot);

    /*
     * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
     * is written without DLAB set, this mode will be disabled.
     */
    if (up->port.type == PORT_16750)
        serial_outp(up, UART_FCR, fcr);

    serial_outp(up, UART_LCR, cval);        /* reset DLAB */
    up->lcr = cval;                    /* Save LCR */
    if (up->port.type != PORT_16750) {
        if (fcr & UART_FCR_ENABLE_FIFO) {
            /* emulated UARTs (Lucent Venus 167x) need two steps */
            serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
        }
        serial_outp(up, UART_FCR, fcr);        /* set fcr */
    }
    serial8250_set_mctrl(&up->port, up->port.mctrl);
    spin_unlock_irqrestore(&up->port.lock, flags);
    /* Don't rewrite B0 */
    if (tty_termios_baud_rate(termios))
        tty_termios_encode_baud_rate(termios, baud, baud);
}

//获得串口波特率
参数1:uart_port uart端口
2:ktermios 结构
4:最小波特率
5:最大波特率
unsigned int
uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
           struct ktermios *old, unsigned int min, unsigned int max)
{
    unsigned int try, baud, altbaud = 38400;
    int hung_up = 0;
    upf_t flags = port->flags & UPF_SPD_MASK;

    if (flags == UPF_SPD_HI)
        altbaud = 57600;
    if (flags == UPF_SPD_VHI)
        altbaud = 115200;
    if (flags == UPF_SPD_SHI)
        altbaud = 230400;
    if (flags == UPF_SPD_WARP)
        altbaud = 460800;

    for (try = 0; try < 2; try++) {
        baud = tty_termios_baud_rate(termios);//根据termios的设置得到其波特率为115200

        /*
         * The spd_hi, spd_vhi, spd_shi, spd_warp kludge...
         * Die! Die! Die!
         */
        if (baud == 38400)
            baud = altbaud;

        /*
         * Special case: B0 rate.
         */
        if (baud == 0) {
            hung_up = 1;
            baud = 9600;
        }

        if (baud >= min && baud <= max)//判断波特率是否合法,并返回
            return baud;

        /*
         * Oops, the quotient was zero.  Try again with
         * the old baud rate if possible.
         */
        termios->c_cflag &= ~CBAUD;
        if (old) {
            baud = tty_termios_baud_rate(old);
            if (!hung_up)
                tty_termios_encode_baud_rate(termios,
                                baud, baud);
            old = NULL;
            continue;
        }

        /*
         * As a last resort, if the quotient is zero,
         * default to 9600 bps
         */
        if (!hung_up)
            tty_termios_encode_baud_rate(termios, 9600, 9600);
    }

    return 0;
}

static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
{
    unsigned int quot;

    /*
     * Handle magic divisors for baud rates above baud_base on
     * SMSC SuperIO chips.
     */
    if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
        baud == (port->uartclk/4))
        quot = 0x8001;
    else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
         baud == (port->uartclk/8))
        quot = 0x8002;
    else
        quot = uart_get_divisor(port, baud);

    return quot;
}

unsigned int
uart_get_divisor(struct uart_port *port, unsigned int baud)
{
    unsigned int quot;

    /*
     * Old custom speed handling.
     */
    if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST)
        quot = port->custom_divisor;
    else
        quot = (port->uartclk + (8 * baud)) / (16 * baud);

    return quot;
}

static void serial8250_set_mctrl(struct uart_port *port, unsigned int mctrl)
{
    struct uart_8250_port *up = (struct uart_8250_port *)port;
    unsigned char mcr = 0;

    if (mctrl & TIOCM_RTS)
        mcr |= UART_MCR_RTS;
    if (mctrl & TIOCM_DTR)
        mcr |= UART_MCR_DTR;
    if (mctrl & TIOCM_OUT1)
        mcr |= UART_MCR_OUT1;
    if (mctrl & TIOCM_OUT2)
        mcr |= UART_MCR_OUT2;
    if (mctrl & TIOCM_LOOP)
        mcr |= UART_MCR_LOOP;

    mcr = (mcr & up->mcr_mask) | up->mcr_force | up->mcr;

    serial_out(up, UART_MCR, mcr);//配置模式控制寄存器
}

void tty_termios_encode_baud_rate(struct ktermios *termios,
                  speed_t ibaud, speed_t obaud)
{
    int i = 0;
    int ifound = -1, ofound = -1;
    int iclose = ibaud/50, oclose = obaud/50;
    int ibinput = 0;

    if (obaud == 0)            /* CD dropped           */
        ibaud = 0;        /* Clear ibaud to be sure */

    termios->c_ispeed = ibaud;
    termios->c_ospeed = obaud;

#ifdef BOTHER
    /* If the user asked for a precise weird speed give a precise weird
       answer. If they asked for a Bfoo speed they many have problems
       digesting non-exact replies so fuzz a bit */

    if ((termios->c_cflag & CBAUD) == BOTHER)
        oclose = 0;
    if (((termios->c_cflag >> IBSHIFT) & CBAUD) == BOTHER)
        iclose = 0;
    if ((termios->c_cflag >> IBSHIFT) & CBAUD)
        ibinput = 1;    /* An input speed was specified */
#endif
    termios->c_cflag &= ~CBAUD;

    /*
     *    Our goal is to find a close match to the standard baud rate
     *    returned. Walk the baud rate table and if we get a very close
     *    match then report back the speed as a POSIX Bxxxx value by
     *    preference
     */

    do {
        if (obaud - oclose <= baud_table[i] &&
            obaud + oclose >= baud_table[i]) {
            termios->c_cflag |= baud_bits[i];//在baud_table中找到最接近obaud 的波特率,把其标志设在在c_cflag中
            ofound = i;
        }
        if (ibaud - iclose <= baud_table[i] &&
            ibaud + iclose >= baud_table[i]) {
            /* For the case input == output don't set IBAUD bits
               if the user didn't do so */
            if (ofound == i && !ibinput)
                ifound  = i;
#ifdef IBSHIFT
            else {
                ifound = i;
                termios->c_cflag |= (baud_bits[i] << IBSHIFT);
            }
#endif
        }
    } while (++i < n_baud_table);

    /*
     *    If we found no match then use BOTHER if provided or warn
     *    the user their platform maintainer needs to wake up if not.
     */
#ifdef BOTHER
    if (ofound == -1)
        termios->c_cflag |= BOTHER;
    /* Set exact input bits only if the input and output differ or the
       user already did */
    if (ifound == -1 && (ibaud != obaud || ibinput))
        termios->c_cflag |= (BOTHER << IBSHIFT);
#else
    if (ifound == -1 || ofound == -1) {
        static int warned;
        if (!warned++)
            printk(KERN_WARNING "tty: Unable to return correct "
              "speed data as your architecture needs updating.\n");
    }
#endif
}

static void
serial8250_console_write(struct console *co, const char *s, unsigned int count)
{
    struct uart_8250_port *up = &serial8250_ports[co->index];
    unsigned long flags;
    unsigned int ier;
    int locked = 1;

    touch_nmi_watchdog();

    local_irq_save(flags);
    if (up->port.sysrq) {
        /* serial8250_handle_port() already took the lock */
        locked = 0;
    } else if (oops_in_progress) {
        locked = spin_trylock(&up->port.lock);
    } else
        spin_lock(&up->port.lock);

    /*
     *    First save the IER then disable the interrupts
     */
    ier = serial_in(up, UART_IER);//保存中断标志

    if (up->capabilities & UART_CAP_UUE)
        serial_out(up, UART_IER, UART_IER_UUE);
    else
        serial_out(up, UART_IER, 0);

    uart_console_write(&up->port, s, count, serial8250_console_putchar);//输出控制信息

    /*
     *    Finally, wait for transmitter to become empty
     *    and restore the IER
     */
    wait_for_xmitr(up, BOTH_EMPTY);//等待输出完成
    serial_out(up, UART_IER, ier);//恢复中断标志

    /*
     *    The receive handling will happen properly because the
     *    receive ready bit will still be set; it is not cleared
     *    on read.  However, modem control will not, we must
     *    call it if we have saved something in the saved flags
     *    while processing with interrupts off.
     */
    if (up->msr_saved_flags)
        check_modem_status(up);

    if (locked)
        spin_unlock(&up->port.lock);
    local_irq_restore(flags);
}

static void serial8250_console_putchar(struct uart_port *port, int ch)
{
    struct uart_8250_port *up = (struct uart_8250_port *)port;

    wait_for_xmitr(up, UART_LSR_THRE);
    serial_out(up, UART_TX, ch);
}
阅读(4552) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~