cat /proc/tty/drive/ttyAMA 可以看tx与rx包
/proc/tty/driver # cat ttyAMA
serinfo:1.0 driver revision:
0: uart:PL011 rev2 mmio:0x120A0000 irq:21 tx:7455 rx:0 RTS|DTR|DSR|CD|RI
1: uart:PL011 rev2 mmio:0x120A1000 irq:22 tx:0 rx:0 DSR|CD|RI
2: uart:PL011 rev2 mmio:0x120A2000 irq:23 tx:14196 rx:0 RTS|DTR|DSR|CD|RI
3: uart:PL011 rev2 mmio:0x120A3000 irq:24 tx:0 rx:0 DSR|CD|RI
##################################################################################################################
##################################################################################################################
##################################################################################################################
tty:各种类型的终端设备
串口终端:搜索ttyS得到drivers/tty/serial/8250/8250_core.c被使用到了并编译到内核了.
static int __init serial8250_init(void)
{
int ret;
//为每一个串口配置port->ops = &serial8250_pops;串口的操作方法.第1点.
serial8250_isa_init_ports();
...
/*注册用户层的接口,tty_register_driver注册tty,看第3点.*/
ret = uart_register_driver(&serial8250_reg);
...
serial8250_isa_devs = platform_device_alloc("serial8250",
PLAT8250_DEV_LEGACY);
...
ret = platform_device_add(serial8250_isa_devs);
...
serial8250_register_ports(&serial8250_reg, &serial8250_isa_devs->dev);
...
ret = platform_driver_register(&serial8250_isa_driver);
...
}
##################################################################################################################
1.
static struct uart_ops serial8250_pops = {
.tx_empty = serial8250_tx_empty,
.set_mctrl = serial8250_set_mctrl,
.get_mctrl = serial8250_get_mctrl,
.stop_tx = serial8250_stop_tx,
.start_tx = serial8250_start_tx,
.stop_rx = serial8250_stop_rx,
.enable_ms = serial8250_enable_ms,
.break_ctl = serial8250_break_ctl,
.startup = serial8250_startup,//配置启动串口,并配置串口中断输入.在打开设备文件时会调用到.
.shutdown = serial8250_shutdown,
.set_termios = serial8250_set_termios,
.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
};
static int serial8250_startup(struct uart_port *port)
{
//如果配置为中断形式,就调配置中断回调函数:
retval = serial_link_irq_chain(up);
}
static int serial_link_irq_chain(struct uart_8250_port *up)
{
ret = request_irq(up->port.irq, serial8250_interrupt,
irq_flags, "serial", i);
}
static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
{
//中断调用注册的serial8250_default_handle_irq,第2点.
if (port->handle_irq(port)) {
}
}
##################################################################################################################
2.
p->serial_in/serial_out用于读取配置寄存器的.在serial8250_startup和配置serial8250_config_port会调用他.
static void set_io_from_upio(struct uart_port *p)
{
...
p->serial_in = io_serial_in;
p->serial_out = io_serial_out;
p->handle_irq = serial8250_default_handle_irq;
}
##################################################################################################################
3.
int uart_register_driver(struct uart_driver *drv)
{
tty_set_operations(normal, &uart_ops);//配置tty_driver的操作方法.
retval = tty_register_driver(normal);注册tty_driver.
}
static 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,
.get_icount = uart_get_icount,
#ifdef CONFIG_CONSOLE_POLL
.poll_init = uart_poll_init,
.poll_get_char = uart_poll_get_char,
.poll_put_char = uart_poll_put_char,
#endif
};
在后面打开设备文件的时候有:
tty_fops.open-->tty_open-->tty->ops->open-->uart_ops.open-->uart_open-->uart_startup-->uart_port_startup--> uport->ops->startup(uport)-->serial8250_startup
做到开始串口初始化的目的.
int tty_register_driver(struct tty_driver *driver)
{
//这个应该经常看到,注册字符设备,如果有主设备号用register_chrdev_region,没有用alloc_chrdev_region
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 (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
for (i = 0; i < driver->num; i++) {
//注册每一个如ttyS0,ttyS1的.
d = tty_register_device(driver, i, NULL);
if (IS_ERR(d)) {
error = PTR_ERR(d);
goto err_unreg_devs;
}
}
}
proc_tty_register_driver(driver);
}
################
tty_register_device-->tty_register_device_attr
{
...
retval = tty_cdev_add(driver, devt, index, 1);
retval = device_register(dev);
}
static int tty_cdev_add(struct tty_driver *driver, dev_t dev,
unsigned int index, unsigned int count)
{
/* init here, since reused cdevs cause crashes */
cdev_init(&driver->cdevs[index], &tty_fops);//为每个设备配置文件操作
driver->cdevs[index].owner = driver->owner;
return cdev_add(&driver->cdevs[index], dev, count);
}
static const struct file_operations tty_fops = {
.llseek = no_llseek,
.read = tty_read,
.write = tty_write,
.poll = tty_poll,
.unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl,
.open = tty_open,
.release = tty_release,
.fasync = tty_fasync,
};
static int tty_open(struct inode *inode, struct file *filp)
{
if (tty->ops->open)
retval = tty->ops->open(tty, filp);
}
##########
4.中断过程
serial8250_default_handle_irq-->serial8250_handle_irq-->serial8250_rx_chars-->tty_flip_buffer_push-->flush_to_ldisc-->disc->ops->receive_buf-->tty_ldisc_N_TTY.receive_buf-->n_tty_receive_buf-->wake_up_interruptible唤醒阻塞
读取过程
tty_read-->ld->ops->read-->tty_ldisc_N_TTY.n_tty_read-->add_wait_queue等待-->ldata->read_buf读取缓冲区-->tty_put_user发给用户层.
输出过程
tty_write-->do_tty_write-->tty_ldisc_N_TTY.n_tty_write-->tty->ops->write-->uart_ops.uart_write-->uart_start-->__uart_start-->port->ops->start_tx-->serial8250_pops.start_tx-->serial8250_start_tx操作硬件了.
5.配置
tty接口的是:
main.c中: console_init();
对应tty_io.c,接口就过来了.
void __init console_init(void)
{
initcall_t *call;
/* Setup the default TTY line discipline. */
tty_ldisc_begin();
}
void tty_ldisc_begin(void)
{
/* Setup the default TTY line discipline. */
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
}
tty接口都是一样的,不同的终端接口注册不同的接口罢了.