目录
串口驱动,内核启动的时候,便初始化完成,后期i应用层用到的时候,访问设备节点便,获取串口设备的 fd 句柄,然后设置相关的参数(波特率,停止位,校验位等等)便可以使用串口进行数据收发。(比如,Bluetooth 芯片通过 UART 挂到 CPU 上,应用程序通过 HCI 命令,用串口给 Bluetooth 芯片进行交互)。
Linux Kernel 的 UART 串口分为几层,tty 核心层,tty 线路规程,串行驱动核心层,以及下面对接具体芯片的 ops:
整个流程走下来,有太多的数据结构和 ops,要分析清楚整个流程,有两种方式:
1、至上而下,即,从用户空间调用串口配置以及串口读写的角度来分析
2、从底层注册开始分析
本文先从底层注册开始分析,根据内核已知提供的接口,逐步打开魔盒,力求扩散到每一个常用的地方;读者也可以按照这个脉络,自行查看 Source code,毕竟某大神说过,最快的深入理解一个模块的方法便是 ”Read The F**king Source Code“,这一点我坚信不疑;
1、底层对接
Kernel 对 UART 设备提供了一套底层对接的接口,接口中涉及到一些结构体和函数,首先让我们来分析一下相关的结构体和函数,这样咱们便知道如何去实现一款芯片的 UART 并对接到 Kernel Driver 中去;
1.1、uart_driver 结构
首先映入眼帘的是这个 uart_driver 结构,每一款芯片的 UART 都需要去实现并定义个属于他自己的 uart_driver 结构(注意,这里的对接的芯片,所以相关的东西,都是代表着最底层的芯片信息),他定义在:< include/linux/serial_core.h >文件中,我们来看看他的定义:
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;
};
其他的成员不多说了,注意到这里有一个 uart_state 的成员和 tty_driver 的成员,你看他的注释,说的够明确了把,让你别动他们俩!这里,我们暂且知道他们两个的存在,后面马上来讲。那么换句话来说,除了这两个成员,其他成员都是需要被初始化赋值的。打个比方来说:
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.driver_name = "s3c2410_serial",
.nr = CONFIG_SERIAL_SAMSUNG_UARTS,
.cons = S3C24XX_SERIAL_CONSOLE,
.dev_name = S3C24XX_SERIAL_NAME,
.major = S3C24XX_SERIAL_MAJOR,
.minor = S3C24XX_SERIAL_MINOR,
};
1.2、注册 uart_driver
我们准备好了一个 uart_drvier 呢,我们需要将它注册到 Kernel 中去,这个是内核开出来让芯片厂家使用的接口,叫做 uart_register_driver ,这个接口的实现在 < drivers/tty/serial/serial_core.c >:
int uart_register_driver(struct uart_driver *drv)
他的具体实现是:
int uart_register_driver(struct uart_driver *drv)
{
---------------- Part 1 Start ----------------
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 = kcalloc(drv->nr, sizeof(struct uart_state), GFP_KERNEL);
if (!drv->state)
goto out;
---------------- Part 1 End ----------------
---------------- Part 2 Start ----------------
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);
---------------- Part 2 End ----------------
---------------- Part 3 Start ----------------
/*
* 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;
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 (retval >= 0)
return retval;
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
---------------- Part 3 End ----------------
}
代码不算多,根据芯片厂家自己定义的 uart_driver 传入结构体,做了几件事情:
Part 1、根据传入的 uart_driver 结构体中的 nr 成员,分配对应个数的 uart_state 成员
uart_driver 中的 nr 是什么含义呢?它其实代表了你一个 uart_driver 对应的实体硬件 UART 的个数!比如,某某芯片的介绍:”多达 3 个 UART 接口“,那么这个 nr 指的就是这个 3;那么这样看来,这个 uart_state 的含义就是对应了芯片的每一个 UART (比如 uart_state[0] 对应了芯片的 UART0 以此类推)。那么我们来看看这个 uart_state 结构具体长啥样:
1.2.1、uart_state 结构
一个 uart_state 定义在 < include/linux/serial_core.h > 文件中:
struct uart_state {
struct tty_port port;
int pm_state;
struct circ_buf xmit;
struct tasklet_struct tlet;
struct uart_port *uart_port;
};
他定义了一个 tty_port,pm 操作,环形 buffer,tasklet,和一个 uart_port;
注意,这里只有 uart_port 是指针类型,这意味着后面会给他分配空间
好了,回到 uart_register_driver 继续分析;
Part2、根据传入的 uart_driver 结构体,分配一个 tty_driver 成员,并对其进行赋值初始化
这部分,调用了 alloc_tty_driver 函数,分配一个 tty_driver,在 < drivers/tty/tty_io.c >
struct tty_driver *alloc_tty_driver(int lines)
{
struct tty_driver *driver;
driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (driver) {
kref_init(&driver->kref);
driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
/* later we'll move allocation of tables here */
}
return driver;
}
EXPORT_SYMBOL(alloc_tty_driver);
对传入的 uart_drvier 下的 tty_drvier 分配了空间,并做了一下 tty 层的初始化;
在 Part 2 中有一个关键的操作:
tty_set_operations(normal, &uart_ops);
这里的 normal 是我们上面分配的 tty 层的 tty_driver,在这里设置了 tty_driver 的 ops 为 serial_core.c 中的 uart_ops,也就是 tty_driver 的 ops 调用,会走到这里来,他是一个 tty_operations 结构:
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_show = uart_proc_show,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
.set_serial = uart_set_info_user,
.get_serial = uart_get_info_user,
.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
};
我们接着看 uart_register_driver;
Part 3、根据传入的 uart_driver 结构体中的 state (前面已经分配了内存),初始化他的 tty_port ,并注册 tty_driver
可以看到,调用了 tty_port_init 来初始化了相关的 tty_port 结构:
void tty_port_init(struct tty_port *port)
{
memset(port, 0, sizeof(*port));
init_waitqueue_head(&port->open_wait);
init_waitqueue_head(&port->close_wait);
init_waitqueue_head(&port->delta_msr_wait);
mutex_init(&port->mutex);
mutex_init(&port->buf_mutex);
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);
然后调用 tasklet_init 注册了 tasklet;
并最终调用 tty_register_driver 注册到了 tty 层,这 tty_register_driver 的操作,其实就是注册了字符设备,因为串口 UART 本质上来说就是一个字符设备:
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
void **p = NULL;
struct device *d;
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++) {
d = tty_register_device(driver, i, NULL);
if (IS_ERR(d)) {
error = PTR_ERR(d);
goto err;
}
}
}
proc_tty_register_driver(driver);
driver->flags |= TTY_DRIVER_INSTALLED;
return 0;
err:
for (i--; i >= 0; i--)
tty_unregister_device(driver, i);
mutex_lock(&tty_mutex);
list_del(&driver->tty_drivers);
mutex_unlock(&tty_mutex);
unregister_chrdev_region(dev, driver->num);
driver->ttys = NULL;
driver->termios = NULL;
kfree(p);
return error;
}
EXPORT_SYMBOL(tty_register_driver);
通过调用 cdev_init(&driver->cdev, &tty_fops) 和 cdev_add 注册一个字符设备(字符设备的名字由我们驱动层的 uart_driver->name 指定),而这个字符设备的操作集为:tty_fops 请记住这个 ops,因为用户空间的 open ,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.3、增加 uart_port
在 uart_register_driver 调用成功后,可以说,我们已经对 uart_drvier 进行了注册,并且实现了这个结构体中的绝大多数成员了,此刻,按照流程,需要调用 uart_add_one_port 结构来向已经注册进内核的 xxx uart_drvier 赋予其生命;
int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
他有两个入参:
1、drv:为哪个 uart_driver 赋予一个 uart_port
2、uport:具体的 uart port
一个 uart_port 代表了一个硬件 UART,有多个 UART 的话,就要调用多次这个接口!
还记得之前那个 uart_state 结构么,他里面不就有一个 uart_port 么,没错,这里就是把这个 uport 赋值给对应的这个 state 下面的这个 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;
BUG_ON(in_interrupt());
if (uport->line >= drv->nr)
return -EINVAL;
state = drv->state + uport->line;
port = &state->port;
mutex_lock(&port_mutex);
mutex_lock(&port->mutex);
if (state->uart_port) {
ret = -EINVAL;
goto out;
}
state->uart_port = uport;
state->pm_state = -1;
uport->cons = drv->cons;
uport->state = state;
/*
* If this port is a console, then the spinlock is already
* initialised.
*/
if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
spin_lock_init(&uport->lock);
lockdep_set_class(&uport->lock, &port_lock_key);
}
uart_configure_port(drv, state, uport);
/*
* Register the port whether it's detected or not. This allows
* setserial to be used to alter this ports parameters.
*/
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);
} else
printk(KERN_ERR "Cannot register tty device on line %d\n",
uport->line);
/*
* Ensure UPF_DEAD is not set.
*/
uport->flags &= ~UPF_DEAD;
out:
mutex_unlock(&port->mutex);
mutex_unlock(&port_mutex);
return ret;
}
可以看到,这个 add one port 将一个 uart_port 添加到了传入的这个 uart_driver->state->uart_port 下面
那么这个 uart_port 又是从何而来的呢?既然是和芯片直接相关的,那么肯定是芯片厂家定义的。我们先看 uart_port 的定义:
1.3.1、uart_port 结构
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);
void (*set_termios)(struct uart_port *,
struct ktermios *new,
struct ktermios *old);
void (*set_ldisc)(struct uart_port *,
struct ktermios *);
unsigned int (*get_mctrl)(struct uart_port *);
void (*set_mctrl)(struct uart_port *, unsigned int);
unsigned int (*get_divisor)(struct uart_port *,
unsigned int baud,
unsigned int *frac);
void (*set_divisor)(struct uart_port *,
unsigned int baud,
unsigned int quot,
.................
const struct uart_ops *ops;
.................
}
他包含了这个 UART 的基地址,lock,等信息,当然,这个既然是和底层相关的,所以呢,芯片厂商需要定义自己的 uart_port 结构并填充他,比如:
struct s3c24xx_uart_port {
unsigned char rx_claimed;
unsigned char tx_claimed;
unsigned int pm_level;
unsigned long baudclk_rate;
unsigned int min_dma_size;
unsigned int rx_irq;
unsigned int tx_irq;
unsigned int tx_in_progress;
unsigned int tx_mode;
unsigned int rx_mode;
struct s3c24xx_uart_info *info;
struct clk *clk;
struct clk *baudclk;
struct uart_port port;
struct s3c24xx_serial_drv_data *drv_data;
/* reference to platform data */
struct s3c2410_uartcfg *cfg;
struct s3c24xx_uart_dma *dma;
#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
struct notifier_block freq_transition;
#endif
};
static struct s3c24xx_uart_port
s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
.port = {
.lock = __PORT_LOCK_UNLOCKED(0),
.iotype = UPIO_MEM,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 0,
}
},
[1] = {
.port = {
.lock = __PORT_LOCK_UNLOCKED(1),
.iotype = UPIO_MEM,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
}
},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
[2] = {
.port = {
.lock = __PORT_LOCK_UNLOCKED(2),
.iotype = UPIO_MEM,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
},
#endif
#if CONFIG_SERIAL_SAMSUNG_UARTS > 3
[3] = {
.port = {
.lock = __PORT_LOCK_UNLOCKED(3),
.iotype = UPIO_MEM,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 3,
}
}
#endif
};
这里我们主要关心后面的那个 uart_ops *ops 结构,在这里被赋值成为了 s3c24xx_serial_ops;
1.3.2、uart_ops 结构
uart_ops,定义了一组 UART 相关的底层的操作集,芯片厂家需要进行硬件寄存器级的适配其中各个操作,
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 (*throttle)(struct uart_port *);
void (*unthrottle)(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 *, struct ktermios *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int oldstate);
/*
* 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
int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
};
一个芯片,需要实现 uart_ops 中的接口,比如:
static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
.get_mctrl = s3c24xx_serial_get_mctrl,
.set_mctrl = s3c24xx_serial_set_mctrl,
.stop_tx = s3c24xx_serial_stop_tx,
.start_tx = s3c24xx_serial_start_tx,
.stop_rx = s3c24xx_serial_stop_rx,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = s3c24xx_serial_startup,
.shutdown = s3c24xx_serial_shutdown,
.set_termios = s3c24xx_serial_set_termios,
.type = s3c24xx_serial_type,
.release_port = s3c24xx_serial_release_port,
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
#if defined(CONFIG_SERIAL_SAMSUNG_CONSOLE) && defined(CONFIG_CONSOLE_POLL)
.poll_get_char = s3c24xx_serial_get_poll_char,
.poll_put_char = s3c24xx_serial_put_poll_char,
#endif
};
挂接上去的函数都是寄存器级的硬件描述,到这里,对接底层的工作基本完成!
1.4、小结
对接底层的部分,Kernel 主要是提供了两个接口:
1、uart_register_driver (一次调用)
2、uart_add_one_port (多次调用)
通过这两个接口,实现了芯片将自己的 UART 对接到 Linux Kernel UART Driver 中。
芯片厂商需要自行设计并实现的部分有:
1、uart_drvier 结构(一个)
2、uart_port 结构(多个)
3、uart_ops 对串口的操作集(可能一个,可能多个)
所以从结构上来看,整个对接过程为:
从数据结构以及相互之间的关系来看:
1.5、注意
这里有一点需要特别注意,在对接底层的部分中,Kernel 定义了一个结构体叫:
struct uart_ops
在 tty 层,对 tty_driver 初始化的时候(serial_core.c),调用到:
tty_set_operations(normal, &uart_ops);
而他的实现是:
void tty_set_operations(struct tty_driver *driver,
const struct tty_operations *op)
{
driver->ops = op;
};
EXPORT_SYMBOL(tty_set_operations);
看到了么,传进去的是 tty_operations *op,所以,在 tty_driver 挂接的 uart_ops 并非那个 struct uart_ops,而是这个 serial_core.c 文件内定义的:
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_show = uart_proc_show,
#endif
.tiocmget = uart_tiocmget,
.tiocmset = uart_tiocmset,
.set_serial = uart_set_info_user,
.get_serial = uart_get_info_user,
.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
};
名字一样,但是不是同一个结构,容易让人眼花~~