上中下三层
底层部分: uart层
关注UART或者其他底层串行硬件特征的底层驱动程序
中间层部分:即tty驱动层
和底层驱动程序接口的tty驱动程序层,将上层驱动程序和形形色色的硬件进行了隔离
上层部分:ldisc 线路规划层
加工用于和tty驱动程序交换数据的线路规程
关系:
底层UART驱动 与 TTY驱动 负责从硬件是哪个收发数据,而上层线路规程驱动负责处理这些数据,并在内核空间与用户空间之间传递数据。
底层部分: uart驱动层
关注UART或者其他底层串行硬件特征的底层驱动程序
drivers\tty\serial\tty_virtual.c
static struct uart_ops ttyx_ops = {
.tx_empty= ttyx_tx_empty,
.set_mctrl= ttyx_set_mctrl,
.get_mctrl= ttyx_get_mctrl,
.stop_tx= ttyx_stop_tx,
.start_tx= ttyx_start_tx,
.stop_rx= ttyx_stop_rx,
.enable_ms= ttyx_enable_ms,
.break_ctl= ttyx_break_ctl,
.startup= ttyx_startup,
.shutdown= ttyx_shutdown,
.set_termios= ttyx_termios,
.type= ttyx_type,
.release_port= ttyx_release_port,
.request_port= ttyx_request_port,
.config_port= ttyx_config_port,
.verify_port= ttyx_verify_port,
};
static struct uart_driver ttyx_uart_driver = {
owner: THIS_MODULE,
major: SERIAL_TTYX_MAJOR,
driver_name: "ttyx",
dev_name: "ttyx",
minor: MINOR_START,
nr: NR_PORTS,
cons: NULL
};
static int vtty_probe(struct platform_device *pdev)
{
unsigned char i;
int ret=0;
printk(KERN_ALERT "%s!!--in--\n", __func__);
ret=uart_register_driver(&ttyx_uart_driver);
if (ret)
{
printk(KERN_ERR "Couldn't register ttyx uart driver\n");
return ret;
}
for(i =0;i<NR_PORTS;i++)
{
struct ttyvx_port *s = &ttyxs[i];
s->port.line = i;
s->port.ops = &ttyx_ops;
s->port.uartclk = CRASTAL_CLK;
s->port.fifosize = TXFIFO_SIZE;
s->port.iobase = i+1;
s->port.iotype = SERIAL_IO_PORT;
//该属性 需要设置 UPF_FIXED_PORT 位。否则 注册过程中 会将 s->port.type强制设置为 空 !!!
s->port.flags = UPF_FIXED_PORT;// ASYNC_BOOT_AUTOCONF
s->port.type = PORT_HONGDIAN;
ret = uart_add_one_port(&ttyx_uart_driver, &s->port);
if(ret<0)
{
printk(KERN_ALERT "uart_add_one_port failed for line i:= %d with error %d\n",i,ret);
}else{
printk(KERN_INFO "uart_add_one_port success for line i:= %d with right %d\n",i,ret);
}
}
return 0;
}
中间层部分:即tty驱动层
和底层驱动程序接口的tty驱动程序层,将上层驱动程序和形形色色的硬件进行了隔离
serial_core.c
struct uart_port {
...
const struct uart_ops *ops;
}
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,
#endi
}
int uart_register_driver(struct uart_driver *drv)
{
struct tty_driver *normal;
int i, retval;
BUG_ON(drv->state);
/*
* Maybe we should be using a slab cache for this, especially if
* we have a large number of ports to handle.
*/
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
if (!drv->state)
goto out;
normal = alloc_tty_driver(drv->nr);
if (!normal)
goto out_kfree;
drv->tty_driver = normal;
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_set_operations(normal, &uart_ops);
/*
* 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->ops = &uart_port_ops;
}
retval = tty_register_driver(normal);
if (retval >= 0){
return retval;
}
for (i = 0; i < drv->nr; i++)
tty_port_destroy(&drv->state[i].port);
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}
上层部分:ldisc 线路规划层
加工用于和tty驱动程序交换数据的线路规程
drivers\tty\n_tty.c
struct tty_ldisc_ops {
....
int (*open)(struct tty_struct *);
void (*close)(struct tty_struct *);
void (*flush_buffer)(struct tty_struct *tty);
ssize_t (*chars_in_buffer)(struct tty_struct *tty);
ssize_t (*read)(struct tty_struct *tty, struct file *file,
unsigned char __user *buf, size_t nr);
ssize_t (*write)(struct tty_struct *tty, struct file *file,
...
};
struct tty_ldisc {
struct tty_ldisc_ops *ops;
struct tty_struct *tty;
};
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,
.fasync = n_tty_fasync,
.receive_buf2 = n_tty_receive_buf2,
};