1. 在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备。
(1)串口终端(/dev/ttyS*):串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看作是一个字符设备。这些串行端口所对应的设备名称是/dev/ttySAC0;/dev/ttySAC1……
(2)控制台终端(/dev/console):在Linux系统中,计算机的输出设备通常被称为控制台终端(Console),这里特指printk信息输出到的设备。/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数” console=ttySAC0”就把console映射到了串口0
(3)虚拟终端(/dev/tty*):
2. Linux tty子系统包含:tty核心,tty线路规程和tty驱动。
(1)tty核心是对整个tty设备的抽象,对用户提供统一的接口。
(2)tty线路规程是对传输数据的格式化。
(3)tty驱动则是面向tty设备的硬件驱动。
3. Linux中Uart框架:
整个 uart 框架大概的样子如上图所示。简单来分的话可以说成两层,一层是下层我们的串口驱动层,它直接与硬件相接触,我们需要填充一个 struct uart_ops 的结构体,另一层是上层 tty 层,包括 tty 核心以及线路规程,它们各自都有一个 Ops 结构,用户空通过间是 tty 注册的字符设备节点来访问,这么说来如上图所示涉及到了4个 ops 结构了,层层跳转。
4. 在 s3c2440平台,注册串口驱动的步骤是,分配一个struct uart_driver 简单填充,并调用uart_register_driver 注册到内核中去。uart_driver源码
struct uart_driver { struct module *owner; const char *driver_name; const char *dev_name; int major; int minor; int nr; struct console *cons; /* * these are private; the low level driver should not * touch these; they should be initialised to NULL */ struct uart_state *state; struct tty_driver *tty_driver; };
(1) 分配uart_driver结构并简单填充(state和tty_driver将会在uart_register_driver时候赋值)
static struct uart_driver s3c24xx_uart_drv = { .owner = THIS_MODULE, .dev_name = "s3c2410_serial", .nr = CONFIG_SERIAL_SAMSUNG_UARTS, .cons = S3C24XX_SERIAL_CONSOLE, .driver_name = S3C24XX_SERIAL_NAME, .major = S3C24XX_SERIAL_MAJOR, .minor = S3C24XX_SERIAL_MINOR, };
(2) 调用uart_register_driver主要填充uart_driver的state和tty_deriver域
int uart_register_driver(struct uart_driver *drv) { struct tty_driver *normal = NULL; 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); retval = -ENOMEM; if (!drv->state) goto out; normal = alloc_tty_driver(drv->nr); if (!normal) goto out; drv->tty_driver = normal; 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_set_operations(normal, &uart_ops); /* * Initialise the UART state(s). */ for (i = 0; i < drv->nr; i++) { struct uart_state *state = drv->state + i; state->close_delay = 500; /* .5 seconds */ state->closing_wait = 30000; /* 30 seconds */ mutex_init(&state->mutex); tty_port_init(&state->info.port); init_waitqueue_head(&state->info.delta_msr_wait); tasklet_init(&state->info.tlet, uart_tasklet_action, (unsigned long)state); } retval = tty_register_driver(normal); out: if (retval < 0) { put_tty_driver(normal); kfree(drv->state); } return retval; }
5. uart_driver的state域(下层,主要是对uart口的统一封装与描述)
(1)state的类型为struct uart_state,定义如下
struct uart_state { unsigned int close_delay; /* msec */ unsigned int closing_wait; /* msec */ #define USF_CLOSING_WAIT_INF (0) #define USF_CLOSING_WAIT_NONE (~0U) int count; int pm_state; struct uart_info info; struct uart_port *port; struct mutex mutex; };
(2)在注册driver时,即在uart_register_driver()函数内,会根据 uart_driver->nr 来申请 nr 个 uart_state 空间,用来存放驱动所支持的串口(端口)的物理信息。在Linux内核中,每一个串口的信息由struct uart_port结构来描述,
struct uart_port { spinlock_t lock; /* port lock */ unsigned long iobase; /* in/out[bwl] */ unsigned char __iomem *membase; /* read/write[bwl] */ unsigned int (*serial_in)(struct uart_port *, int); void (*serial_out)(struct uart_port *, int, int); unsigned int irq; /* irq number */ unsigned int uartclk; /* base uart clock */ unsigned int fifosize; /* tx fifo size */ unsigned char x_char; /* xon/xoff char */ unsigned char regshift; /* reg offset shift */ unsigned char iotype; /* io access style */ unsigned char unused1; #define UPIO_PORT (0) #define UPIO_HUB6 (1) #define UPIO_MEM (2) #define UPIO_MEM32 (3) #define UPIO_AU (4) /* Au1x00 type IO */ #define UPIO_TSI (5) /* Tsi108/109 type IO */ #define UPIO_DWAPB (6) /* DesignWare APB UART */ #define UPIO_RM9000 (7) /* RM9000 type IO */ unsigned int read_status_mask; /* driver specific */ unsigned int ignore_status_mask; /* driver specific */ struct uart_info *info; /* pointer to parent info */ struct uart_icount icount; /* statistics */ struct console *cons; /* struct console, if any */ #ifdef CONFIG_SERIAL_CORE_CONSOLE unsigned long sysrq; /* sysrq timeout */ #endif upf_t flags; #define UPF_FOURPORT ((__force upf_t) (1 << 1)) #define UPF_SAK ((__force upf_t) (1 << 2)) #define UPF_SPD_MASK ((__force upf_t) (0x1030)) #define UPF_SPD_HI ((__force upf_t) (0x0010)) #define UPF_SPD_VHI ((__force upf_t) (0x0020)) #define UPF_SPD_CUST ((__force upf_t) (0x0030)) #define UPF_SPD_SHI ((__force upf_t) (0x1000)) #define UPF_SPD_WARP ((__force upf_t) (0x1010)) #define UPF_SKIP_TEST ((__force upf_t) (1 << 6)) #define UPF_AUTO_IRQ ((__force upf_t) (1 << 7)) #define UPF_HARDPPS_CD ((__force upf_t) (1 << 11)) #define UPF_LOW_LATENCY ((__force upf_t) (1 << 13)) #define UPF_BUGGY_UART ((__force upf_t) (1 << 14)) #define UPF_NO_TXEN_TEST ((__force upf_t) (1 << 15)) #define UPF_MAGIC_MULTIPLIER ((__force upf_t) (1 << 16)) #define UPF_CONS_FLOW ((__force upf_t) (1 << 23)) #define UPF_SHARE_IRQ ((__force upf_t) (1 << 24)) /* The exact UART type is known and should not be probed. */ #define UPF_FIXED_TYPE ((__force upf_t) (1 << 27)) #define UPF_BOOT_AUTOCONF ((__force upf_t) (1 << 28)) #define UPF_FIXED_PORT ((__force upf_t) (1 << 29)) #define UPF_DEAD ((__force upf_t) (1 << 30)) #define UPF_IOREMAP ((__force upf_t) (1 << 31)) #define UPF_CHANGE_MASK ((__force upf_t) (0x17fff)) #define UPF_USR_MASK ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY)) unsigned int mctrl; /* current modem ctrl settings */ unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ const struct uart_ops *ops; unsigned int custom_divisor; unsigned int line; /* port index */ resource_size_t mapbase; /* for ioremap */ struct device *dev; /* parent device */ unsigned char hub6; /* this should be in the 8250 driver */ unsigned char suspended; unsigned char unused[2]; void *private_data; /* generic platform data pointer */ };
注:这个结构体,是需要我们自己来填充的,比如我们 s3c2440 有3个串口,那么就需要填充3个 uart_port ,并且通过 uart_add_one_port 添加到 uart_driver->uart_state->uart_port 中去。当然 uart_driver 有多个 uart_state ,每个 uart_state 有一个 uart_port
(3)在 uart_port 里还有一个非常重要的成员 struct uart_ops *ops ,这个也是需要我们自己来实现的。该结构体描述了针对某一串口的具体操作方法
struct uart_ops { unsigned int (*tx_empty)(struct uart_port *); void (*set_mctrl)(struct uart_port *, unsigned int mctrl); unsigned int (*get_mctrl)(struct uart_port *); void (*stop_tx)(struct uart_port *); void (*start_tx)(struct uart_port *); void (*send_xchar)(struct uart_port *, char ch); void (*stop_rx)(struct uart_port *); void (*enable_ms)(struct uart_port *); void (*break_ctl)(struct uart_port *, int ctl); int (*startup)(struct uart_port *); void (*shutdown)(struct uart_port *); void (*flush_buffer)(struct uart_port *); void (*set_termios)(struct uart_port *, struct ktermios *new, struct ktermios *old); void (*set_ldisc)(struct uart_port *); void (*pm)(struct uart_port *, unsigned int state, unsigned int oldstate); int (*set_wake)(struct uart_port *, unsigned int state); /* * Return a string describing the type of the port */ const char *(*type)(struct uart_port *); /* * Release IO and memory resources used by the port. * This includes iounmap if necessary. */ void (*release_port)(struct uart_port *); /* * Request IO and memory resources used by the port. * This includes iomapping the port if necessary. */ int (*request_port)(struct uart_port *); void (*config_port)(struct uart_port *, int); int (*verify_port)(struct uart_port *, struct serial_struct *); int (*ioctl)(struct uart_port *, unsigned int, unsigned long); #ifdef CONFIG_CONSOLE_POLL void (*poll_put_char)(struct uart_port *, unsigned char); int (*poll_get_char)(struct uart_port *); #endif };
6. uart_driver的tty_driver域(上层)
(1)tty_driver 是在注册过程中构建的,即在uart_register_driver()函数中构建起来的。
(2)注册过程即uart_register_driver()所做的工作
① 根据driver支持的最大设备数,申请n个 uart_state 空间,每一个 uart_state 都有一个 uart_port 。
② 分配一个 tty_driver 结构,并将drv->tty_driver 指向它
③ 对 tty_driver 进行设置,其中包括默认波特率、校验方式等
④ 初始化每一个 uart_state 的 tasklet
⑤ 注册tty_driver
注: 注册 uart_driver 实际上是注册 tty_driver,因此与用户空间打交道的工作完全交给了 tty_driver ,而且这一部分都是内核实现好的,我们不需要修改,了解一下工作原理即可。
(3)tty_dr的注册tty_register_driver()
/* * Called by a tty driver to register itself. */ int tty_register_driver(struct tty_driver *driver) { int error; int i; dev_t dev; void **p = NULL; 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 (error < 0) { kfree(p); return error; } if (p) { driver->ttys = (struct tty_struct **)p; driver->termios = (struct ktermios **)(p + driver->num); } else { driver->ttys = NULL; driver->termios = NULL; } cdev_init(&driver->cdev, &tty_fops); driver->cdev.owner = driver->owner; error = cdev_add(&driver->cdev, dev, driver->num); if (error) { unregister_chrdev_region(dev, driver->num); driver->ttys = NULL; driver->termios = NULL; kfree(p); return error; } mutex_lock(&tty_mutex); list_add(&driver->tty_drivers, &tty_drivers); mutex_unlock(&tty_mutex); if (!(driver->flags & 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; return 0; }
tty_driver注册过程
① 为线路规程和termios分配空间,并使 tty_driver 相应的成员指向它们。
② 注册字符设备,名字是 uart_driver->name 我们这里是“ttySAC”,文件操作函数集是 tty_fops。
③ 将该 uart_driver->tty_drivers 添加到全局链表 tty_drivers
④ 向 proc 文件系统添加 driver
7. 调用过程分析:tty_driver 注册了一个字符设备,我们以它的 tty_fops 入手,以 open、read、write 为例,分析用户空间如何访问到最底层的硬件操作函数
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, };
(1)tty_open分析
static int tty_open(struct inode *inode, struct file *filp) { int ret; lock_kernel(); ret = __tty_open(inode, filp); unlock_kernel(); return ret; }
① tty_open的核心代码是调用函数__tty_open(),__tty_open()的简略代码如下
static int __tty_open(struct inode *inode, struct file *filp) { struct tty_struct *tty = NULL; int noctty, retval; struct tty_driver *driver; int index; dev_t device = inode->i_rdev; unsigned saved_flags = filp->f_flags; ... //在全局tty_drivers链表中获取Core注册的tty_driver driver = get_tty_driver(device, &index); tty = tty_init_dev(driver, index, 0); // tty->ops = driver->ops; filp->private_data = tty; if (tty->ops->open) /* 调用tty_driver->tty_foperation->open */ retval = tty->ops->open(tty, filp); return 0; }
从 tty_drivers 全局链表获取到前边我们注册进去的 tty_driver ,然后分配设置一个 struct tty_struct 的结构,最后调用 tty_struct->ops->open 函数,其实 tty_struct->ops == tty_driver->ops 。
② tty_init_dev源代码为
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, int first_ok) { struct tty_struct *tty; int retval; /* Check if pty master is being opened multiple times */ if (driver->subtype == PTY_TYPE_MASTER && (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) return ERR_PTR(-EIO); /* * First time open is complex, especially for PTY devices. * This code guarantees that either everything succeeds and the * TTY is ready for operation, or else the table slots are vacated * and the allocated memory released. (Except that the termios * and locked termios may be retained.) */ if (!try_module_get(driver->owner)) return ERR_PTR(-ENODEV); tty = alloc_tty_struct(); if (!tty) goto fail_no_mem; initialize_tty_struct(tty, driver, idx); retval = tty_driver_install_tty(driver, tty); if (retval < 0) { free_tty_struct(tty); module_put(driver->owner); return ERR_PTR(retval); } /* * Structures all installed ... call the ldisc open routines. * If we fail here just call release_tty to clean up. No need * to decrement the use counts, as release_tty doesn't care. */ retval = tty_ldisc_setup(tty, tty->link); if (retval) goto release_mem_out; return tty; fail_no_mem: module_put(driver->owner); return ERR_PTR(-ENOMEM); /* call the tty release_tty routine to clean out this slot */ release_mem_out: if (printk_ratelimit()) printk(KERN_INFO "tty_init_dev: ldisc open failed, " "clearing slot %d ", idx); release_tty(tty, idx); return ERR_PTR(retval); }
initialize_tty_struct():用tty_driver来初始化tty_struct结构
tty_ldisc_setup():调用线路规程中的open函数
(2)tty_open总结
① 获取tty_driver
② 根据tty_driver初始化一个tty_struct结构
* 设置 tty_struct 的线路规程为 N_TTY (不同类型的线路规程有不同的 ops)
* 初始化一个延时工作队列,唤醒时调用flush_to_ldisc ,读函数时我们需要分析它
* 初始化 tty_struct 里的两个等待队列头
* 设置 tty_struct->ops == tty_driver->ops
③ 在 tty_ldisc_setup 函数中调用到线路规程的open函数
④ 如果 tty_struct->ops 也就是 tty_driver->ops 定义了 open 函数则调用,显然是有的 uart_open