uart串口驱动分为两部分
(1)下层我们的串口驱动层,它直接与硬件接触
(2)另一层是上层 tty 层,包括 tty 核心以及线路规程
在 s3c2440平台注册驱动过程:分配一个struct uart_driver 简单填充,并调用uart_register_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,
};
static int __init s3c24xx_serial_modinit(void)
{
int ret;
ret = uart_register_driver(&s3c24xx_uart_drv);
if (ret < 0) {
printk(KERN_ERR "failed to register UART driver\n");
return -1;
}
return platform_driver_register(&samsung_serial_driver);
}
static void __exit s3c24xx_serial_modexit(void)
{
uart_unregister_driver(&s3c24xx_uart_drv);
}
module_init(s3c24xx_serial_modinit);
module_exit(s3c24xx_serial_modexit);
串口的驱动结构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; /* 其对应的console.若该uart_driver支持serial console,否则为NULL */
/*
* 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;
};
在我们上边填充的结构体中,有两个成员未被赋值,对于tty_driver 代表的是上层,它会在 uart_register_driver中的过程中赋值,而uart_state 则代表下层,uart_state 也会在uart_register_driver的过程中分配空间,但是它里面真正设置硬件相关的东西是 uart_state->uart_port ,这个uart_port 是需要我们从其它地方调用 uart_add_one_port 来添加的。
/*
* This is the state information which is persistent across opens.
*/
struct uart_state {
struct tty_port port;
int pm_state;
struct circ_buf xmit;
struct uart_port *uart_port; //串口设备
};
在注册 driver 时,会根据 uart_driver->nr 来申请 nr 个 uart_state 空间,用来存放驱动所支持的串口(端口)的物理信息。
struct uart_port {
spinlock_t lock; /* port lock */
unsigned long iobase; /* io端口基地址(物理)*/
unsigned char __iomem *membase; /* io内存基地址(虚拟) */
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);
int (*handle_irq)(struct uart_port *);
void (*pm)(struct uart_port *, unsigned int state,
unsigned int old);
unsigned int irq; /* irq number */
unsigned long irqflags; /* irq flags */
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_RM9000 (6) /* RM9000 type IO */
unsigned int read_status_mask; /* driver specific */
unsigned int ignore_status_mask; /* driver specific */
struct uart_state *state; /* pointer to parent state */
struct uart_icount icount; /* statistics */
struct console *cons; /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
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))
#define UPF_EXAR_EFR ((__force upf_t) (1 << 25))
#define UPF_BUG_THRE ((__force upf_t) (1 << 26))
/* 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 irq_wake;
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 。
在 uart_port 里还有一个非常重要的成员 struct uart_ops *ops ,这个也是需要我们自己来实现的,一般芯片厂家都写好了或者只需要稍作修改。
tty核心层
/**
* uart_register_driver - register a driver with the uart core layer
* @drv: low level driver structure
*
* Register a uart driver with the core driver. We in turn register
* with the tty layer, and initialise the core driver per-port state.
*
* We have a proc file in /proc/tty/driver which is named after the
* normal driver.
*
* drv->port should be NULL, and the per-port structures should be
* registered using uart_add_one_port after this call has succeeded.
*/
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;
port->close_delay = HZ / 2; /* .5 seconds */
port->closing_wait = 30 * HZ;/* 30 seconds */
}
retval = tty_register_driver(normal);
if (retval >= 0)
return retval;
put_tty_driver(normal);
out_kfree:
kfree(drv->state);
out:
return -ENOMEM;
}
注册过程
- 根据driver支持的最大设备数,申请n个uart_state空间,每个uart_state都有一个uart_port.
- 分配tty_driver,并将drv->tty_driver指向他。
-
对 tty_driver 进行设置,其中包括默认波特率、校验方式等,还有一个重要的 Ops ,uart_ops ,它是tty核心与我们串口驱动通信的接口。
-
注册 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 };
下面分析注册过程:
/*
* 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;
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);
/* 将该 driver->tty_drivers 添加到全局链表 tty_drivers */
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 文件系统注册driver */
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;
}
tty_driver 注册过程干了哪些事:
1、为线路规程和termios分配空间,并使 tty_driver 相应的成员指向它们。
2、注册字符设备,名字是 uart_driver->name 我们这里是“ttySAC”,文件操作函数集是 tty_fops。
3、将该 uart_driver->tty_drivers 添加到全局链表 tty_drivers 。
4、向 proc 文件系统添加 driver.
调用关系
tty_driver注册了一个字符驱动,从tty_fops 入手,以 open、read、write 为例,看看用户空间是如何访问到最底层的硬件操作函
数的。
- tty_open
static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty;
int noctty, retval;
struct tty_driver *driver = NULL;
int index;
dev_t device = inode->i_rdev;
unsigned saved_flags = filp->f_flags;
nonseekable_open(inode, filp);
retry_open:
retval = tty_alloc_file(filp);
if (retval)
return -ENOMEM;
noctty = filp->f_flags & O_NOCTTY;
index = -1;
retval = 0;
mutex_lock(&tty_mutex);
tty_lock();
tty = tty_open_current_tty(device, filp);
if (IS_ERR(tty)) {
retval = PTR_ERR(tty);
goto err_unlock;
} else if (!tty) {
driver = tty_lookup_driver(device, filp, &noctty, &index);
if (IS_ERR(driver)) {
retval = PTR_ERR(driver);
goto err_unlock;
}
/* check whether we're reopening an existing tty */
tty = tty_driver_lookup_tty(driver, inode, index);
if (IS_ERR(tty)) {
retval = PTR_ERR(tty);
goto err_unlock;
}
}
if (tty) {
retval = tty_reopen(tty);
if (retval)
tty = ERR_PTR(retval);
} else
tty = tty_init_dev(driver, index);
mutex_unlock(&tty_mutex);
if (driver)
tty_driver_kref_put(driver);
if (IS_ERR(tty)) {
tty_unlock();
retval = PTR_ERR(tty);
goto err_file;
}
tty_add_file(tty, filp);
check_tty_count(tty, __func__);
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
noctty = 1;
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name);
#endif
if (tty->ops->open)
/* 调用tty_driver->tty_foperation->open */
retval = tty->ops->open(tty, filp);
else
retval = -ENODEV;
filp->f_flags = saved_flags;
if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
!capable(CAP_SYS_ADMIN))
retval = -EBUSY;
if (retval) {
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__,
retval, tty->name);
#endif
tty_unlock(); /* need to call tty_release without BTM */
tty_release(inode, filp);
if (retval != -ERESTARTSYS)
return retval;
if (signal_pending(current))
return retval;
schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
tty_lock();
if (filp->f_op == &hung_up_tty_fops)
filp->f_op = &tty_fops;
tty_unlock();
goto retry_open;
}
tty_unlock();
mutex_lock(&tty_mutex);
tty_lock();
spin_lock_irq(¤t->sighand->siglock);
if (!noctty &&
current->signal->leader &&
!current->signal->tty &&
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(¤t->sighand->siglock);
tty_unlock();
mutex_unlock(&tty_mutex);
return 0;
err_unlock:
tty_unlock();
mutex_unlock(&tty_mutex);
/* after locks to avoid deadlock */
if (!IS_ERR_OR_NULL(driver))
tty_driver_kref_put(driver);
err_file:
tty_free_file(filp);
return retval;
}
从 tty_drivers 全局链表获取到前边我们注册进去的 tty_driver ,最后调用 tty_struct->ops->open 函数,其实 tty_struct->ops == tty_driver->ops=uart_open 。
整个 tty_open 的工作:
1、获取 tty_driver
2、根据 tty_driver 初始化一个 tty_struct
2.1 设置 tty_struct 的线路规程为 N_TTY (不同类型的线路规程有不同的 ops)
2.2 初始化一个延时工作队列,唤醒时调用flush_to_ldisc ,读函数时我们需要分析它。
2.3 初始化 tty_struct 里的两个等待队列头。
2.4 设置 tty_struct->ops == tty_driver->ops 。
3、在 tty_ldisc_setup 函数中调用到线路规程的open函数,对于 N_TTY 来说是 n_tty_open 。
4、如果 tty_struct->ops 也就是 tty_driver->ops 定义了 open 函数则调用,显然是有的 uart_open 。