慢慢欣赏linux之串口驱动代码分析

串口驱动分两阶段初始化

第一阶段  串口驱动没有初始化前的准备工作,包括设备树的解析,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,
            },
        };

扫描二维码关注公众号,回复: 3814850 查看本文章

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

猜你喜欢

转载自blog.csdn.net/shipinsky/article/details/82177143