原文转自:http://m.blog.csdn.net/blog/lushengchu2003/9368031
最近闲来无事情做,想到以前项目中遇到串口硬件流控制的问题,蓝牙串口控制返回错误,上层读写串口buffer溢出的问题等,也折腾了一阵子,虽然 最终证明与串口驱动无关,但是排查问题时候毫无疑问会查看串口驱动的相关代码,所以把串口驱动的流程过了一遍,方便以后再用到时拿来用。分析的是全志代码 A20。直接开始代码分析吧。
串口驱动代码在linux-3.3/drivers/tty/serial目录下,全志把自己平台相关的代码集中到了一个文件中,叫sw_uart.c,那就从它的__init开始了:
static int __init sw_uart_init(void)
{
int ret;
u32 i;
struct sw_uart_pdata *pdata;
SERIAL_MSG("driver initializied\n");
ret = sw_uart_get_devinfo();
if (unlikely(ret))
return ret;
ret = uart_register_driver(&sw_uart_driver);
if (unlikely(ret)) {
SERIAL_MSG("driver initializied\n");
return ret;
}
for (i=0; i
pdata = &sw_uport_pdata[i];
if (pdata->used)
platform_device_register(&sw_uport_device[i]);
}
return platform_driver_register(&sw_uport_platform_driver);
}
sw_uart_get_devinfo是解析全志的sys配置脚本中的串口配置,一共有八个串口,要用到那个直接在sys脚本中设置1就行了,这个用过全志平台的都知道
接着sw_uart_driver结构体定义如下:
static struct uart_driver sw_uart_driver = {
.owner = THIS_MODULE,
.driver_name = "sw_serial",
.dev_name = "ttyS",
.nr = SW_UART_NR,
.cons = SW_CONSOLE,
};
这里SW_UART_NR为8,ttyS就是将要显示在/dev/目录下的名字了,从0~7
接着看uart注册函数,顾名思义是把全志自己平台的串口注册到串口核心serial_core中去:
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->owner = drv->owner;
normal->driver_name = drv->driver_name;
normal->name = drv->dev_name;//名字为ttyS
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;
}
先列出来吧,以用到时候再回来看,这里先创建了NR个state, 并为每个state做一些初始化,但是这些state还没有和端口(uart_port)对应起来;初始化完port口后,调用tty_register_driver:
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;
这里alloc_chrdev_region是动态分配了主从设备号,接着cdev_init,它file_operations结构提和它关联了起来,以后我们open /dev/ttyS节点时候会调用他的open函数,先看看这个结构体:
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,
};
接着cdev_add就把file_operations和设备号关联起来了,我们现在还没有创建设备节点,不过看到有为
driver->major,driver->minor_start赋值的,后面创建节点就是用这两个主从设备号。接着list_add把
这个tty_driver添加到链表中来,方便后续查找。
接着if语句判断TTY_DRIVER_DYNAMIC_DEV标志,我们前面有赋值:
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
所以这里的if条件不成立的,最后创建proc下的节点,就返回了。按我的理解,tty_register_driver是注册了一个tty的驱
动,这个驱动有了逻辑能力,但是这个时候这个驱动还没有对应任何设