串口驱动分两阶段初始化

第一阶段  串口驱动没有初始化前的准备工作,包括设备树的解析,platform设备注册

asmlinkage void __init start_kernel(void)
    =>setup_arch
        =>find_legacy_serial_ports();
                =>path = of_get_property(of_chosen, "linux,stdout-path", NULL);//解析设备树,标记默认输出串口,uboot可以设置该节点
                if (path != NULL) //path为 /soc4080@F BE00 0000/serial@11C500
                    stdout = of_find_node_by_path(path);//无stdout
                =>for_each_compatible_node(np, "serial", "ns16550")//解析并遍历设备树,填充2个结构体 legacy_serial_infos 和 legacy_serial_ports,第二阶段需要
                    struct device_node *parent = of_get_parent(np);//对于powerpc p4080,串口的父节点是soc, 恰好匹配{.type = "soc",}
                    if (of_match_node(legacy_serial_parents, parent) != NULL) {
                        index = add_legacy_soc_port(np, np);//从设备树解析串口基本配置参数
                            =>of_get_property(np, "clock-frequency", NULL)
                            =>of_get_property(np, "reg-shift", NULL)
                            =>of_get_property(np, "reg-offset", NULL)
                            =>of_get_property(np, "used-by-rtas", NULL)
                            =>addrp = of_get_address(soc_dev, 0, NULL, NULL);
                            =>addr = of_translate_address(soc_dev, addrp);
                            =>add_legacy_port(np, -1, UPIO_MEM, addr, addr, NO_IRQ, flags, 0);
                                =>clk = of_get_property(np, "clock-frequency", NULL);
                                =>spd = of_get_property(np, "current-speed", NULL);
                                =>if (want_index >= 0 && want_index < MAX_LEGACY_SERIAL_PORTS)
                                    index = want_index;
                                else
                                    index = legacy_serial_count;
                                =>if (index >= MAX_LEGACY_SERIAL_PORTS)
                                    return -1;
                                if (index >= legacy_serial_count)
                                    legacy_serial_count = index + 1;
                                =>memset(&legacy_serial_ports[index], 0,  sizeof(struct plat_serial8250_port));
                                legacy_serial_ports[index].mapbase = base;
                                legacy_serial_ports[index].iotype = iotype;
                                legacy_serial_ports[index].uartclk = clock;
                                legacy_serial_ports[index].irq = irq;//这时中断号为0,因为参数是NO_IRQ,后面会解析设备树
                                legacy_serial_ports[index].flags = flags;
                                legacy_serial_infos[index].taddr = taddr;
                                legacy_serial_infos[index].np = of_node_get(np);
                                legacy_serial_infos[index].clock = clock;
                                legacy_serial_infos[index].speed = spd ? *spd : 0;
                                legacy_serial_infos[index].irq_check_parent = irq_check_parent;
                                return index;

if (index >= 0 && np == stdout)
                            legacy_serial_console = index;//标记默认串口,这个分支不会走
                    }
                    of_node_put(parent);
                =>if (legacy_serial_console >= 0)//这个分支不会走
                    setup_legacy_serial_console(legacy_serial_console);
    =>console_init();
        =>tty_ldisc_begin();//设置默认线路规程
            =>tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
                =>tty_ldiscs[disc] = new_ldisc;
                new_ldisc->num = disc;
                new_ldisc->refcount = 0;
        =>call = __con_initcall_start;
        =>while (call < __con_initcall_end) {
            ==>(*call)();
                =>console_initcall(serial8250_console_init);//初始化serial8250_ports,
                    数组大小 #define UART_NR    CONFIG_SERIAL_8250_NR_UARTS
                    unsigned int nr_uarts = CONFIG_SERIAL_8250_RUNTIME_UARTS;
                    =>serial8250_isa_init_ports();
                        =>struct uart_8250_port *up;
                        static int first = 1;//这个函数只会进来一次,在这边进来了,后面再也进不来了
                        int i, irqflag = 0;

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;
                        }
                        =>后面的for循环在powerpc不会执行,因为在powerpc架构里面old_serial_port为空,而在x86则展开
                    =>register_console(&serial8250_console);
                        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,
                        };
                        =>if (newcon->early_setup)
                            newcon->early_setup();//static int serial8250_console_early_setup(void)
                                =>return serial8250_find_port_for_earlycon();
                                    =>line = serial8250_find_port(port);
                                        =>for (line = 0; line < nr_uarts; line++) {
                                            port = &serial8250_ports[line].port;
                                            if (uart_match_port(p, port))
                                                return line;
                                        }
                                    =>ret = update_console_cmdline("uart", 8250, "ttyS", line, device->options);
                        =>if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {//将新的console加到链表里面, 也就是register的本质
                            newcon->next = console_drivers;
                            console_drivers = newcon;
                            if (newcon->next)
                                newcon->next->flags &= ~CON_CONSDEV;
                        } else {
                            newcon->next = console_drivers->next;
                            console_drivers->next = newcon;
                        }
                        =>if (newcon->flags & CON_PRINTBUFFER) {
                            /*
                             * release_console_sem() will print out the buffered messages
                             * for us.
                             */
                            spin_lock_irqsave(&logbuf_lock, flags);
                            con_start = log_start;//打印buf的信息
                            spin_unlock_irqrestore(&logbuf_lock, flags);
                        }
                        
            ==>call++;
        }
    
int __init serial_dev_init(void)//注册设备,根据设备树,legacy_serial_infos 和 legacy_serial_ports 找中断号
    =>/*
     * Before we register the platform serial devices, we need
     * to fixup their interrupts and their IO ports.
     */
     for (i = 0; i < legacy_serial_count; i++) {
        struct plat_serial8250_port *port = &legacy_serial_ports[i];
        struct device_node *np = legacy_serial_infos[i].np;

if (port->irq == NO_IRQ)
            fixup_port_irq(i, np, port);
                =>virq = irq_of_parse_and_map(np, 0);//解析设备书找中断号
                =>port->irq = virq;//找到中断了
        if ((port->iotype == UPIO_MEM) || (port->iotype == UPIO_TSI))
            fixup_port_mmio(i, np, port);
                =>port->membase = ioremap(port->mapbase, 0x100);
    }
    =>return platform_device_register(&serial_device);//注册 serial_device platform设备,第二阶段需要用
        static struct platform_device serial_device = {
        .name    = "serial8250",
        .id    = PLAT8250_DEV_PLATFORM,
        .dev    = {
            .platform_data = legacy_serial_ports,//legacy_serial_ports 在前面已经初始化
        },
    };    
        
第二阶段 正式初始化
int __init serial8250_init(void)
    =>serial8250_reg.nr = UART_NR;
    ret = uart_register_driver(&serial8250_reg);//int uart_register_driver(struct uart_driver *drv)
        =>struct tty_driver *normal;
        =>drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
        =>normal = alloc_tty_driver(drv->nr);
        =>drv->tty_driver = normal;  uart_driver 与 tty_driver 关联
        =>normal->owner        = drv->owner;
        normal->driver_name    = drv->driver_name;
        normal->name        = drv->dev_name;
        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_driver 与 uart_driver 关联
        tty_set_operations(normal, &uart_ops);//void tty_set_operations(struct tty_driver *driver, const struct tty_operations *op)
            =>driver->ops = op;
        =>/*
         * 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->close_delay     = 500;    /* .5 seconds */
            port->closing_wait    = 30000;    /* 30 seconds */
            tasklet_init(&state->tlet, uart_tasklet_action,
                     (unsigned long)state);
        }
        =>retval = tty_register_driver(normal);
            =>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 (p) {
            driver->ttys = (struct tty_struct **)p;
            driver->termios = (struct ktermios **)(p + driver->num);
        }
        =>cdev_init(&driver->cdev, &tty_fops);
        driver->cdev.owner = driver->owner;
        error = cdev_add(&driver->cdev, dev, driver->num);
        =>list_add(&driver->tty_drivers, &tty_drivers);
        =>if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {//这个流程不会进入,因为 TTY_DRIVER_DYNAMIC_DEV 被置
            for (i = 0; i < driver->num; i++)
                tty_register_device(driver, i, NULL);
        }
        =>proc_tty_register_driver(driver);
        =>driver->flags |= TTY_DRIVER_INSTALLED;
    =>serial8250_isa_devs = platform_device_alloc("serial8250", PLAT8250_DEV_LEGACY);
    =>ret = platform_device_add(serial8250_isa_devs);//这个东东对x86有用,对于powerpc没有太大用
    =>serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
    =>ret = platform_driver_register(&serial8250_isa_driver);//注册 serial8250_isa_driver platform驱动, 与serial_device platform设备serial_device发生match, 调用probe函数
        static struct platform_driver serial8250_isa_driver = {
            .probe        = serial8250_probe,
            .remove        = __devexit_p(serial8250_remove),
            .suspend    = serial8250_suspend,
            .resume        = serial8250_resume,
            .driver        = {
                .name    = "serial8250",
                .owner    = THIS_MODULE,
            },
        };

int __devinit serial8250_probe(struct platform_device *dev)
    =>struct plat_serial8250_port *p = dev->dev.platform_data;
    =>memset(&port, 0, sizeof(struct uart_port));
    =>for (i = 0; p && p->flags != 0; p++, i++) {
        port.iobase        = p->iobase;
        port.membase        = p->membase;
        port.irq        = p->irq;
        port.irqflags        = p->irqflags;
        port.uartclk        = p->uartclk;
        port.regshift        = p->regshift;
        port.iotype        = p->iotype;
        port.flags        = p->flags;
        port.mapbase        = p->mapbase;
        port.hub6        = p->hub6;
        port.private_data    = p->private_data;
        port.type        = p->type;
        port.serial_in        = p->serial_in;
        port.serial_out        = p->serial_out;
        port.dev        = &dev->dev;
        port.irqflags        |= irqflag;
        ret = serial8250_register_port(&port);
            =>uart = serial8250_find_match_or_unused(port);
            =>if (uart) {
                uart_remove_one_port(&serial8250_reg, &uart->port);

uart->port.iobase       = port->iobase;
                uart->port.membase      = port->membase;
                uart->port.irq          = port->irq;
                uart->port.irqflags     = port->irqflags;
                uart->port.uartclk      = port->uartclk;
                uart->port.fifosize     = port->fifosize;
                uart->port.regshift     = port->regshift;
                uart->port.iotype       = port->iotype;
                uart->port.flags        = port->flags | UPF_BOOT_AUTOCONF;
                uart->port.mapbase      = port->mapbase;
                uart->port.private_data = port->private_data;
                if (port->dev)
                    uart->port.dev = port->dev;

if (port->flags & UPF_FIXED_TYPE)
                    serial8250_init_fixed_type_port(uart, port->type);

set_io_from_upio(&uart->port);
                /* Possibly override default I/O functions.  */
                if (port->serial_in)
                    uart->port.serial_in = port->serial_in;
                if (port->serial_out)
                    uart->port.serial_out = port->serial_out;

ret = uart_add_one_port(&serial8250_reg, &uart->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;
                    =>state = drv->state + uport->line;line 从0开始
                    port = &state->port;
                    state->uart_port = uport; uart_port 与 uart_driver 关联  ( drv->state + uport->line)->uart_port = uport
                    state->pm_state = -1;

uport->cons = drv->cons;
                    uport->state = state; uart_driver 与 uart_port 关联 uport->state = drv->state + uport->line
                    =>uart_configure_port(drv, state, uport);
                        =>port->ops->config_port(port, flags);
                        /\
                        ||
                        V
                        .config_port    = serial8250_config_port//硬件寄存器配置都在这里面
                            =>autoconfig(up, probeflags);
                                =>scratch = serial_in(up, UART_IIR) >> 6;
                                =>switch (scratch)
                                        case 3:
                                        autoconfig_16550a(up);
                                        break;
                        =>uart_report_port(drv, port);
                        =>uart_change_pm(state, 0);/* Power up port for set_mctrl() */
                        =>port->ops->set_mctrl(port, port->mctrl & TIOCM_DTR);
                        =>register_console(port->cons);
                    =>tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev);
                    =>if (likely(!IS_ERR(tty_dev))) {
                        device_init_wakeup(tty_dev, 1);
                        device_set_wakeup_enable(tty_dev, 0);
                    }
                if (ret == 0)
                    ret = uart->port.line;
            }
        }
    }
    return 0;

内核态打印        
int printk(const char *fmt, ...)
    =>r = vprintk(fmt, args);
        =>preempt_disable();//关闭抢占和关中断
        raw_local_irq_save(flags);
        =>/* Emit the output into the temporary buffer *///准备好缓冲区
        printed_len += vscnprintf(printk_buf + printed_len,
                      sizeof(printk_buf) - printed_len, fmt, args);

p = printk_buf;
        =>for ( ; *p; p++)//Copy the output into log_buf.
                emit_log_char('<');
                emit_log_char(current_log_level + '0');
                emit_log_char('>');
                if (!*p)
                    break;
                emit_log_char(*p);
                if (*p == '\n')
                    new_text_line = 1;
        =>if (acquire_console_semaphore_for_printk(this_cpu))
            release_console_sem();
                =>for ( ; ; ) {//串口打印
                    spin_lock_irqsave(&logbuf_lock, flags);
                    wake_klogd |= log_start - log_end;
                    if (con_start == log_end)
                        break;            /* Nothing to print */
                    _con_start = con_start;
                    _log_end = log_end;
                    con_start = log_end;        /* Flush */
                    spin_unlock(&logbuf_lock);
                    stop_critical_timings();    /* don't trace print latency */
                    call_console_drivers(_con_start, _log_end);
                        =>_call_console_drivers(start_print, cur_index, msg_level);
                            =>if ((msg_log_level < console_loglevel || ignore_loglevel) &&
                                    console_drivers && start != end) { //根据打印级别console_loglevel决定什么信息打印到串口上
                                if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {
                                    /* wrapped write */
                                    __call_console_drivers(start & LOG_BUF_MASK, log_buf_len);
                                        =>con->write(con, &LOG_BUF(start), end - start);//对于第一阶段来说,是 serial8250_console_write
                                            =>struct uart_8250_port *up = &serial8250_ports[co->index];
                                            =>ier = serial_in(up, UART_IER);
                                            =>uart_console_write(&up->port, s, count, serial8250_console_putchar);
                                                =>putchar(port, *s);//serial8250_console_putchar
                                                    =>struct uart_8250_port *up = (struct uart_8250_port *)port;
                                                    =>wait_for_xmitr(up, UART_LSR_THRE);
                                                    =>serial_out(up, UART_TX, ch);
                                            =>wait_for_xmitr(up, BOTH_EMPTY);
                                            =>serial_out(up, UART_IER, ier);
                                    __call_console_drivers(0, end & LOG_BUF_MASK);
                                } else {
                                    __call_console_drivers(start, end);
                                }
                            }
                    start_critical_timings();
                    local_irq_restore(flags);
                }
                =>if (wake_klogd)//唤醒klogd,后续可以提交给syslogd打印到syslog或者messages里面
                    wake_up_klogd();
        =>raw_local_irq_restore(flags);
        preempt_enable();
            
第二阶段串口初始化完毕后
打开串口的代码调用关系如下
tty_open
chrdev_open
__dentry_open
do_last
do_filp_open
do_sys_open
ret_from_syscall

int tty_open(struct inode *inode, struct file *filp)
    =>dev_t device = inode->i_rdev;
    =>driver = get_tty_driver(device, &index);
    =>if (!tty)
        tty = tty_driver_lookup_tty(driver, inode, index);
    =>if (tty) {
        retval = tty_reopen(tty);
        if (retval)
            tty = ERR_PTR(retval);
    } else
        tty = tty_init_dev(driver, index, 0);//struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, int first_ok)
            =>struct tty_struct *tty
            =>tty = alloc_tty_struct();
            =>initialize_tty_struct(tty, driver, idx);
                =>tty->magic = TTY_MAGIC;
                tty_ldisc_init(tty);
                tty->buf.head = tty->buf.tail = NULL;
                tty_buffer_init(tty);
                tty->driver = driver;
                tty->ops = driver->ops;
                tty->index = idx;
                tty_line_name(driver, idx, tty->name);
            =>retval = tty_driver_install_tty(driver, tty);
            =>retval = tty_ldisc_setup(tty, tty->link);
                =>struct tty_ldisc *ld = tty->ldisc;
                =>retval = tty_ldisc_open(tty, ld);
                =>if (o_tty) 
                    retval = tty_ldisc_open(o_tty, o_tty->ldisc);
                    tty_ldisc_enable(o_tty);
                =>tty_ldisc_enable(tty);
    =>filp->private_data = tty;
    =>if (!retval) {
        if (tty->ops->open)
            retval = tty->ops->open(tty, filp);//根据inode找到主从设备号,根据主从设备号从tty_drivers链表找到tty_driver
            ^
            ||
            v
            uart_open
                =>struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
                struct uart_state *state;
                struct tty_port *port;
                int retval, line = tty->index;
                =>state = uart_get(drv, line);
                    =>state = drv->state + line;
                    =>port = &state->port;
                    =>port->count++;
                    =>return state;
                =>port = &state->port;
                =>tty->driver_data = state;
                state->uart_port->state = state;
                tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
                tty->alt_speed = 0;
                tty_port_tty_set(port, tty);
                =>if (port->count == 1)
                    uart_change_pm(state, 0);
                =>retval = uart_startup(state, 0);
                    =>if (!state->xmit.buf) {
                        /* This is protected by the per port mutex */
                        page = get_zeroed_page(GFP_KERNEL);
                        if (!page)
                            return -ENOMEM;

state->xmit.buf = (unsigned char *) page;
                        uart_circ_clear(&state->xmit);
                    }
                    =>retval = uport->ops->startup(uport);
                    ^
                    ||
                    v
                    serial8250_startup
                        =>struct uart_port *uport = state->uart_port;
                        struct tty_port *port = &state->port;
                        unsigned long page;
                        =>if (!state->xmit.buf) {
                            /* This is protected by the per port mutex */
                            page = get_zeroed_page(GFP_KERNEL);
                            if (!page)
                                return -ENOMEM;

state->xmit.buf = (unsigned char *) page;
                            uart_circ_clear(&state->xmit);
                        }
                        =>retval = uport->ops->startup(uport);
                        ^
                        ||
                        v
                        static int serial8250_startup(struct uart_port *port)
                            =>struct uart_8250_port *up = (struct uart_8250_port *)port;
                            =>(void) serial_inp(up, UART_LSR);//先关闭硬件中断源
                            (void) serial_inp(up, UART_RX);
                            (void) serial_inp(up, UART_IIR);
                            (void) serial_inp(up, UART_MSR);
                            =>spin_lock_irqsave(&up->port.lock, flags);//使能中断控制器
                            if (up->port.irqflags & IRQF_SHARED)
                                    enable_irq(up->port.irq);
                            spin_unlock_irqrestore(&up->port.lock, flags);
                            =>retval = serial_link_irq_chain(up);
                                =>ret = request_irq(up->port.irq, serial8250_interrupt, irq_flags, "serial", i);//挂接中断处理例程
                            =>spin_lock_irqsave(&up->port.lock, flags);//重新使能中断源
                            /*
                             * 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);
                            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);
                            }

写操作串口调用栈如下
uart_start
uart_write
n_tty_write
tty_write
vfs_write
sys_write
ret_from_syscall

输出的代码调用关系如下
ssize_t tty_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
    =>struct tty_struct *tty;
    struct inode *inode = file->f_path.dentry->d_inode;
    struct tty_ldisc *ld;
    =>tty = (struct tty_struct *)file->private_data;
    ld = tty_ldisc_ref_wait(tty);
    =>do_tty_write(ld->ops->write, tty, file, buf, count);
        for (;;)
            copy_from_user(tty->write_buf, buf, size)
            ret = write(tty, file, tty->write_buf, size)
            ^
            ||
            v
            ld->ops->write
            ^
            ||
            v
            n_tty_write
                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
                };
                =>const unsigned char *b = buf;
                DECLARE_WAITQUEUE(wait, current);
                int c;
                =>process_echoes(tty);
                =>add_wait_queue(&tty->write_wait, &wait);
                =>while (1) 
                    ==>set_current_state(TASK_INTERRUPTIBLE);
                    if (signal_pending(current)) {
                        retval = -ERESTARTSYS;
                        break;
                    }
                    ==>while (nr > 0) {
                        ===>c = tty->ops->write(tty, b, nr);
                        ^
                        ||
                        v
                        uart_write
                            const struct tty_operations uart_ops = {
                                .open        = uart_open,
                                .close        = uart_close,
                                .write        = uart_write,
                                .put_char    = uart_put_char,
                                .flush_chars    = uart_flush_chars,
                                .write_room    = uart_write_room,
                                .chars_in_buffer= uart_chars_in_buffer,
                                .flush_buffer    = uart_flush_buffer,
                                .ioctl        = uart_ioctl,
                                .throttle    = uart_throttle,
                                .unthrottle    = uart_unthrottle,
                                .send_xchar    = uart_send_xchar,
                                .set_termios    = uart_set_termios,
                                .set_ldisc    = uart_set_ldisc,
                                .stop        = uart_stop,
                                .start        = uart_start,
                                .hangup        = uart_hangup,
                                .break_ctl    = uart_break_ctl,
                                .wait_until_sent= uart_wait_until_sent,
                            #ifdef CONFIG_PROC_FS
                                .proc_fops    = &uart_proc_fops,
                            #endif
                                .tiocmget    = uart_tiocmget,
                                .tiocmset    = uart_tiocmset,
                            #ifdef CONFIG_CONSOLE_POLL
                                .poll_init    = uart_poll_init,
                                .poll_get_char    = uart_poll_get_char,
                                .poll_put_char    = uart_poll_put_char,
                            #endif
                            };
                            =>struct uart_state *state = tty->driver_data;
                            struct uart_port *port;
                            struct circ_buf *circ;
                            unsigned long flags;
                            =>port = state->uart_port;
                            circ = &state->xmit;
                            =>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;
                            }
                            =>uart_start(tty);
                                =>struct uart_state *state = tty->driver_data;
                                struct uart_port *port = state->uart_port;
                                =>__uart_start(tty);
                                    =>struct uart_state *state = tty->driver_data;
                                    struct uart_port *port = state->uart_port;
                                    =>if (!uart_circ_empty(&state->xmit) && state->xmit.buf && !tty->stopped && !tty->hw_stopped)
                                        port->ops->start_tx(port);
                                        ^
                                        ||
                                        v
                                        serial8250_start_tx
                                            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,
                                                .set_ldisc    = serial8250_set_ldisc,
                                                .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
                                            };
                                            =>struct uart_8250_port *up = (struct uart_8250_port *)port;
                                            =>serial_out
                        ===>if (c < 0) {
                            retval = c;
                            goto break_out;
                        }
                        if (!c)
                            break;
                        b += c;
                        nr -= c;
                    }
                    ==>if (!nr)
                        break;
                    if (file->f_flags & O_NONBLOCK) {
                        retval = -EAGAIN;
                        break;
                    }
                    schedule();
                =>__set_current_state(TASK_RUNNING);
                remove_wait_queue(&tty->write_wait, &wait);
                if (b - buf != nr && tty->fasync)
                    set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
                return (b - buf) ? b - buf : retval;
    =>tty_ldisc_deref(ld);

irqreturn_t serial8250_interrupt(int irq, void *dev_id)
    =>struct irq_info *i = dev_id;
    struct list_head *l, *end = NULL;
    =>l = i->head;
    =>do {
        struct uart_8250_port *up = list_entry(l, struct uart_8250_port, list);
        unsigned intiir = serial_in(up, UART_IIR);
        if (!(iir & UART_IIR_NO_INT)) {
            serial8250_handle_port(up);
                =>status = serial_inp(up, UART_LSR);
                =>if (status & (UART_LSR_DR | UART_LSR_BI))
                    receive_chars(up, &status);
                        =>struct tty_struct *tty = up->port.state->port.tty;
                        unsigned char ch, lsr = *status;
                        int max_count = 256;
                        =>do {
                            if (likely(lsr & UART_LSR_DR))
                                ch = serial_inp(up, UART_RX);
                            uart_insert_char(&up->port, lsr, UART_LSR_OE, ch, flag);
                                =>struct tty_struct *tty = port->state->port.tty;
                                =>if ((status & port->ignore_status_mask & ~overrun) == 0)
                                    tty_insert_flip_char(tty, ch, 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);
                        } while ((lsr & (UART_LSR_DR | UART_LSR_BI)) && (max_count-- > 0));
                        =>tty_flip_buffer_push(tty);
                        =>*status = lsr;
                =>check_modem_status(up);
                =>if (status & UART_LSR_THRE)
                    transmit_chars(up);
            handled = 1;

end = NULL;
        }
        } while (l != end);
    =>return IRQ_RETVAL(handled);
        
    
    
    
linux下串口(serial)和串口驱动
https://blog.csdn.net/wangzhen209/article/details/76685756

linux 串口驱动详细分析
https://blog.csdn.net/dai_xiangjun/article/details/41241881

tty初探—uart驱动框架分析
https://blog.csdn.net/lizuobin2/article/details/51773305

serival(串口驱动)分析
https://wenku.baidu.com/view/49bc74c3b7360b4c2f3f6420.html

LINUX 日志级别(LOGLEVEL)详解
http://smilejay.com/2011/12/linux_loglevel/

Linux中tty框架与uart框架之间的调用关系剖析
http://blog.chinaunix.net/uid-29025972-id-4738659.html

linux关于串口的配置和多线程收发
http://www.360doc.com/content/17/0831/23/44391309_683701497.shtml

Linux下的串口编程及非阻塞模式
https://www.cnblogs.com/ynxf/p/6105072.html

Linux open系统调用流程浅析
https://www.jianshu.com/p/f3f5a33f2c59

浅析linux中open系统调用
http://www.360doc.com/content/12/0507/15/9171956_209262761.shtml

ttyUSB串口设备节点生成过程
https://blog.csdn.net/mingtianwoyueni/article/details/63709861

Linux串口驱动分析read
https://blog.csdn.net/longwang155069/article/details/42776059

tty初探—uart驱动框架分析
https://blog.csdn.net/lizuobin2/article/details/51773305

串口驱动分析
https://blog.csdn.net/weed_hz/article/details/8946391

linux设备驱动之8250串口驱动
https://blog.csdn.net/zjy900507/article/details/78678783

慢慢欣赏linux之串口驱动代码分析 - 基于powerpc 2.6.x版本相关推荐

  1. linux pl320 mbox控制器驱动分析-(3) pl320驱动代码分析

    linux pl320 mbox控制器驱动分析-(3)pl320驱动代码分析 1 pl320 mbox控制器宏定义 2 初始化接口 3 ipc_handler mbox中断处理函数 4 数据的收发 4 ...

  2. 三星framebuffer驱动代码分析

    一.驱动总体概述 本次的驱动代码是Samsung公司为s5pv210这款SoC编写的framebuffer驱动,对应于s5pv210中的内部外设Display Controller (FIMD)模块. ...

  3. linux下IIC驱动开发分析

    1.  IIC规范 IIC(Inter-Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备.IIC总线产生于在80年代,最初为音频和 ...

  4. 【鸿蒙OS开发入门】18 - HDF驱动子系统:加速度计传感器 Driver层驱动代码分析

    [鸿蒙OS开发入门]18 - HDF驱动子系统:加速度计传感器 Driver层代码分析 一.如何添加速度计传感器驱动代码(代码.编译.配置) 1.驱动代码实现 2.驱动编译配置 2.1 linux 编 ...

  5. Xilinx XDMA 数据传输sgdma 驱动代码分析

    Xilinx XDMA 数据传输sgdma 驱动代码分析 我的之前两篇文章有介绍到上位机软件的逻辑该如何控制,驱动代码的框架是怎样的,驱动的整体逻辑在linux系统中是如何实现的,感兴趣的小伙伴可以去 ...

  6. linux内核中链表代码分析---list.h头文件分析(一)

    linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17:13:14 在学习数据结构时,有一个重要的知识点就是链表.对于链表的一些基本操作,它的最好学习资料就是内核中的li ...

  7. DRM驱动代码分析:开机过程中显示驱动做了什么

    前言: 有些信息是在网上查资料后总结的,没有去追代码验证.如果有说得不对的地方,欢迎提出指正.感谢! 手机启动的大致流程 1.长按开机键 2.执行存储在ROM里(应该是某一个固定地址或是预定义的地址) ...

  8. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

  9. QSPI Flash驱动代码分析 (QSPI控制器初始化)

    QSPI Flash驱动代码分析 (QSPI控制器初始化) 1. 函数cqspi_controller_enable() 该函数主要使能和去能QSPI控制器.QSPI配置寄存器(偏移量0x00)的bi ...

最新文章

  1. swift_045(Swift @IBDesignable和@IBInspectable使用)
  2. 乌班图linux分辨率不能调,ubuntu不能设置高分辨率问题的解决方法,
  3. php指定长度 分割整形,php指定长度分割字符串str_split函数用法示例
  4. 如果你对java的异常处理头皮发麻,那就进来吧
  5. python get post区别_大话Python, webpy教程之GET和POST区别
  6. 第一冲刺阶段工作总结02
  7. iDow Brand——关于一个商标的构思。
  8. 用计算机弹假面骑士build,假面骑士build使用的武器汇总,你知道几个?
  9. 21世纪需要的七种人才—李开复
  10. vs2005安装使用指南
  11. Python 使jupyter notebook 从指定浏览器启动 以及编程中途更换浏览器
  12. 使用计算机进行会计核算的 只要,用电子计算机生成的会计资料?
  13. 三色旗问题中的快排应用
  14. linux 格式化工具 mkfs 简介
  15. 腾讯视频转换mp4格式用什么转换器?电脑怎么把腾讯视频转换成mp4?
  16. C语言有趣的代码大全,分享一段有趣的小代码
  17. Java Class
  18. Pytorch深度学习笔记(四)梯度向下算法
  19. 4月全球“.网址”域名总量排行榜:ZDNS份额仍超99%
  20. 蒜头君爬楼梯(1) - 计蒜客

热门文章

  1. 协同过滤推荐算法简介
  2. 我在拼多多的三年......
  3. 计算机AMD方案不超过4000元,4000元左右AMD R5-1400配RX570全新芯片显卡电脑配置推荐...
  4. for语句计算1到10的阶乘的和
  5. clearfix清除浮动进化史
  6. 画画用电容笔还是触控笔?电容笔10大品牌排行榜
  7. 批量新建文件夹/文本文档
  8. Matlab中的RandStream
  9. Mysql外键约束怎么删除
  10. js 页面输出html,javascript中如何输出?