系列文章目录
Linux UART子系统【1】- uart_register_driver驱动
Linux UART子系统【2】- imx6ul uart 入口函数 uart_add_one_port
Linux UART子系统【3】- Linux串口驱动(3) - open详解
Linux UART子系统【4】- Linux串口驱动(2) - 线路规程
Linux UART子系统【5】- UART的RTS和CTS
Linux UART子系统【6】- write详解
Linux UART子系统【7】- read详解
UART BUG-系列文章目录
文章目录
前言
一、uart_driver 注册与注销
同 I2C
、SPI
一样,Linux
也提供了串口驱动框架,我们只需要按照相应的串口框架编写驱
动程序即可。串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也
已经由 NXP
官方已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信
息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成
/dev/ttymxcX(X=0….n)
文件。
uart_driver
结构体表示 UART
驱动,uart_driver
定义在 include/linux/serial_core.h
文件中,内容如下:
1.1 调用流程
基于Linux的tty架构及UART驱动详解相关阅读2.3.1.1注册uart_driver
--->static struct uart_driver imx_reg
--->int uart_register_driver(struct uart_driver *drv)
--->struct tty_driver *normal = alloc_tty_driver(drv->nr)/*分配一个可注册的tty_driver,并告知设备数据量*/
--->struct tty_driver *driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
--->将用户struct uart_driver信息赋值给struct tty_driver *normal
--->tty_port_init(port);/*初始化8个uart通道,指明port的ops*/
--->tty_register_driver(normal);/*注册struct tty_driver,最终要注册的实体*/
--->register_chrdev_region();/*将char_device_struct变量注册到内核*/
1.2 相关结构体
每个串口驱动都需要定义一个 uart_driver
,加载驱动的时候通过 uart_register_driver
函数,向系统注册这个 uart_driver
,此函数原型如下:
#define DRIVER_NAME "IMX-uart"
static struct uart_driver imx_reg = {
.owner = THIS_MODULE,
.driver_name = DRIVER_NAME, /*/proc/tty/driver/IMX-uart*/
.dev_name = DEV_NAME, /*/dev/tty/ttymxcX(0~10)*/
.major = SERIAL_IMX_MAJOR,
.minor = MINOR_START,
.nr = ARRAY_SIZE(imx_ports),
.cons = IMX_CONSOLE,
};
/*
* This is the state information which is persistent across opens.
*/
struct uart_state {
struct tty_port port;
enum uart_pm_state pm_state;
struct circ_buf xmit;
struct uart_port *uart_port;
};
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;
};
struct tty_driver {
int magic; /* magic number for this structure */
struct kref kref; /* Reference management */
struct cdev *cdevs;
struct module *owner;
const char *driver_name;
const char *name;
int name_base; /* offset of printed name */
int major; /* major device number */
int minor_start; /* start of minor device number */
unsigned int num; /* number of devices allocated */
short type; /* type of tty driver */
short subtype; /* subtype of tty driver */
struct ktermios init_termios; /* Initial termios */
unsigned long flags; /* tty driver flags */
struct proc_dir_entry *proc_entry; /* /proc fs entry */
struct tty_driver *other; /* only used for the PTY driver */
/*
* Pointer to the tty data structures
*/
struct tty_struct **ttys;
struct tty_port **ports;
struct ktermios **termios;
void *driver_state;
/*
* Driver methods
*/
const struct tty_operations *ops;
struct list_head tty_drivers;
};
/* tty driver types */
#define TTY_DRIVER_TYPE_SYSTEM 0x0001
#define TTY_DRIVER_TYPE_CONSOLE 0x0002
#define TTY_DRIVER_TYPE_SERIAL 0x0003
#define TTY_DRIVER_TYPE_PTY 0x0004
#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */
#define TTY_DRIVER_TYPE_SYSCONS 0x0006
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE]; // 255
1.3 uart_register_driver
UART
驱动主要涉及的驱动文件是imx.c、serial_core.c
两个文件。首先我们找到驱动的入口函数module_init(imx_serial_init)
,在函数imx_serial_init
中调用uart_register_driver
向内核注册了一个驱动.
/*函数参数和返回值含义如下:drv:要注册的 uart_driver。
返回值:0,成功;负值,失败。*/
drivers/tty/serial/serial_core.c
(
int uart_register_driver(struct uart_driver *drv)
{
drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
struct tty_driver *normal = alloc_tty_driver(drv->nr);
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);/*list_add(&driver->tty_drivers, &tty_drivers);*/
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;
}
)
drivers/tty/tty_io.c
(
struct tty_driver *normal = alloc_tty_driver(drv->nr);
tty_alloc_driver(lines, 0);
struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,
unsigned long flags)
{
struct tty_driver *driver;
unsigned int cdevs = 1;
int err;
if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))
return ERR_PTR(-EINVAL);
driver = kzalloc(sizeof(struct tty_driver), GFP_KERNEL);
if (!driver)
return ERR_PTR(-ENOMEM);
kref_init(&driver->kref);
driver->magic = TTY_DRIVER_MAGIC;
driver->num = lines;
driver->owner = owner;
driver->flags = flags;
if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {
driver->ttys = kcalloc(lines, sizeof(*driver->ttys),
GFP_KERNEL);
driver->termios = kcalloc(lines, sizeof(*driver->termios),
GFP_KERNEL);
if (!driver->ttys || !driver->termios) {
err = -ENOMEM;
goto err_free_all;
}
}
if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {
driver->ports = kcalloc(lines, sizeof(*driver->ports),
GFP_KERNEL);
cdevs = lines;
}
driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);
return driver;
err_free_all:
kfree(driver->ports);
kfree(driver->ttys);
kfree(driver->termios);
kfree(driver);
return ERR_PTR(err);
}
)
先是调用tty_set_operations
将uart_ops
这一个tty设备的操作函数集设置到了tty
驱动中,同时调用tty_register_driver
函数向内核注册了tty
驱动,其中uart_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,
#endif
};
1.3 tty_port_init
tty.h
(
struct tty_buffer {
union {
struct tty_buffer *next;
struct llist_node free;
};
int used;
int size;
int commit;
int read;
int flags;
/* Data points here */
unsigned long data[0];
};
struct tty_bufhead {
struct tty_buffer *head; /* Queue head */
struct work_struct work;
struct mutex lock;
atomic_t priority;
struct tty_buffer sentinel;
struct llist_head free; /* Free queue head */
atomic_t mem_used; /* In-use buffers excluding free list */
int mem_limit;
struct tty_buffer *tail; /* Active buffer */
};
struct tty_port {
struct tty_bufhead buf; /* Locked internally */
struct tty_struct *tty; /* Back pointer */
struct tty_struct *itty; /* internal back ptr */
const struct tty_port_operations *ops; /* Port operations */
spinlock_t lock; /* Lock protecting tty field */
int blocked_open; /* Waiting to open */
int count; /* Usage count */
wait_queue_head_t open_wait; /* Open waiters */
wait_queue_head_t close_wait; /* Close waiters */
wait_queue_head_t delta_msr_wait; /* Modem status change */
unsigned long flags; /* TTY flags ASY_*/
unsigned char console:1, /* port is a console */
low_latency:1; /* optional: tune for latency */
struct mutex mutex; /* Locking */
struct mutex buf_mutex; /* Buffer alloc lock */
unsigned char *xmit_buf; /* Optional buffer */
unsigned int close_delay; /* Close port delay */
unsigned int closing_wait; /* Delay for output */
int drain_delay; /* Set to zero if no pure time
based drain is needed else
set to size of fifo */
struct kref kref; /* Ref counter */
};
)
tty_port.c
(
void tty_port_init(struct tty_port *port)
{
memset(port, 0, sizeof(*port));
tty_buffer_init(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);
}
)
tty_buffer.c
(
/*tty_buffer_init - prepare a tty buffer structure*/
void tty_buffer_init(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
mutex_init(&buf->lock);
tty_buffer_reset(&buf->sentinel, 0);
buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
init_llist_head(&buf->free);
atomic_set(&buf->mem_used, 0);
atomic_set(&buf->priority, 0);
INIT_WORK(&buf->work, flush_to_ldisc);
buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT;
}
static void tty_buffer_reset(struct tty_buffer *p, size_t size)
{
p->used = 0;
p->size = size;
p->next = NULL;
p->commit = 0;
p->read = 0;
p->flags = 0;
}
)
1.4 tty_register_driver
tty_io.c
(
/*
* Called by a tty driver to register itself.
*/
int tty_register_driver(struct tty_driver *driver)
{
int error;
int i;
dev_t dev;
struct device *d;
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);/*Linux 设备模型【4】- register_chrdev_region解析*/
}
if (error < 0)
goto err;
/*normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;*/
if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {
error = tty_cdev_add(driver, dev, 0, driver->num);
if (error)
goto err_unreg_char;
}
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_unreg_devs;
}
}
}
proc_tty_register_driver(driver);
driver->flags |= TTY_DRIVER_INSTALLED;
return 0;
err_unreg_devs:
for (i--; i >= 0; i--)
tty_unregister_device(driver, i);
mutex_lock(&tty_mutex);
list_del(&driver->tty_drivers);
mutex_unlock(&tty_mutex);
err_unreg_char:
unregister_chrdev_region(dev, driver->num);
err:
return error;
}
)
proc_tty.c
(
/*
* This function is called by tty_register_driver() to handle
* registering the driver's /proc handler into /proc/tty/driver/<foo>
*/
void proc_tty_register_driver(struct tty_driver *driver)
{
struct proc_dir_entry *ent;
if (!driver->driver_name || driver->proc_entry ||
!driver->ops->proc_fops)
return;
/*proc_create_data函数在proc下创建节点*/
/*driver->driver_name =DRIVER_NAME "IMX-uart"*//*/proc/tty/driver/IMX-uart*/
ent = proc_create_data(driver->driver_name, 0, proc_tty_driver,
driver->ops->proc_fops, driver);
driver->proc_entry = ent;
}
)
1.5 tty_port_destroy
drivers/tty/tty_post.c
(
/**
* tty_port_destroy -- destroy inited port
* @port: tty port to be doestroyed
*
* When a port was initialized using tty_port_init, one has to destroy the
* port by this function. Either indirectly by using tty_port refcounting
* (tty_port_put) or directly if refcounting is not used.
*/
void tty_port_destroy(struct tty_port *port)
{
cancel_work_sync(&port->buf.work);
tty_buffer_free_all(port);
}
)
drivers/tty/tty_buffer.c
{
/**
* tty_buffer_free_all - free buffers used by a tty
* @tty: tty to free from
*
* Remove all the buffers pending on a tty whether queued with data
* or in the free ring. Must be called when the tty is no longer in use
*/
void tty_buffer_free_all(struct tty_port *port)
{
struct tty_bufhead *buf = &port->buf;
struct tty_buffer *p, *next;
struct llist_node *llist;
while ((p = buf->head) != NULL) {
buf->head = p->next;
if (p->size > 0)
kfree(p);
}
llist = llist_del_all(&buf->free);
llist_for_each_entry_safe(p, next, llist, free)
kfree(p);
tty_buffer_reset(&buf->sentinel, 0);
buf->head = &buf->sentinel;
buf->tail = &buf->sentinel;
atomic_set(&buf->mem_used, 0);
}
}
1.6 uart_unregister_driver
drivers/tty/serial/serial_core.c
(
/**
* uart_unregister_driver - remove a driver from the uart core layer
* @drv: low level driver structure
*
* Remove all references to a driver from the core driver. The low
* level driver must have removed all its ports via the
* uart_remove_one_port() if it registered them with uart_add_one_port().
* (ie, drv->port == NULL)
*/
void uart_unregister_driver(struct uart_driver *drv)
{
struct tty_driver *p = drv->tty_driver;
unsigned int i;
tty_unregister_driver(p);
put_tty_driver(p);
for (i = 0; i < drv->nr; i++)
tty_port_destroy(&drv->state[i].port);
kfree(drv->state);
drv->state = NULL;
drv->tty_driver = NULL;
}
)
drivers/tty/tty_io.c
(
/*
* Called by a tty driver to unregister itself.
*/
int tty_unregister_driver(struct tty_driver *driver)
{
#if 0
/* FIXME */
if (driver->refcount)
return -EBUSY;
#endif
unregister_chrdev_region(MKDEV(driver->major, driver->minor_start),
driver->num);
(
/*static struct char_device_struct *__unregister_chrdev_region(unsigned major, unsigned baseminor, int minorct)*/
kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));
)
mutex_lock(&tty_mutex);
list_del(&driver->tty_drivers);
mutex_unlock(&tty_mutex);
return 0;
}
)