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