串口驱动的整体框架实际上和显示驱动类似
驱动程序是一个字符设备,驱动的实质内容都是在一个平台总线设备驱动程序里
1. 串口驱动的分析,从"drivers/tty/serial/8250/8250.c"开始
serial8250_init为入口函数,从这个函数一路分析下去,下面列出主要代码
serial8250_reg.nr = UART_NR; UART_NR为8,查看datasheet可知A20支持8个uart
ret = uart_register_driver(&serial8250_reg);
uart_register_driver在”drivers/tty/serial/serial_core.c“中可以找到
normal->type = TTY_DRIVER_TYPE_SERIAL;
normal->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
tty_register_driver
tty_register_driver 在"drivers/tty/tty_io.c"这个文件中找到如下函数
alloc_chrdev_region
register_chrdev_region
cdev_init
cdev_add
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;
}
}
}
前四个函数是创建一个字符设备驱动程序的典型函数,后面那段if语句之所以拿出来说一下,是因为
tty_register_device这个函数是用来创建设备文件的,但是这个if语句是不会执行,因为从上面的分析可
知道这个驱动的flags里有TTY_DRIVER_DYNAMIC_DEV。那么什么时候创建了设备文件呢。实际上,上面几个文件只是一个框架性的东西。
2. 接下来分析另一路主线
”drivers/tty/serial/8250/8250_sunxi.c"
sw_serial_init
这里注册了一个平台总线驱动和平台总线设备
script_get_item(uart_para, "uart_used", &val);
从配置文件中得到某一个串口是否可用,如果可用的话,在调用如下函数
platform_device_register
platform_driver_register
来完成平台总线驱动和平台总线设备的匹配
分析device和driver 如下
static struct platform_driver sw_serial_driver = {
.probe = sw_serial_probe,
.remove = sw_serial_remove,
.suspend = sw_serial_suspend,
.resume = sw_serial_resume,
.driver = {
.name = "sunxi-uart",
.owner = THIS_MODULE,
},
};
struct platform_device sw_uart_dev[] = {
[0] = {.name = "sunxi-uart", .id = 0, .num_resources = ARRAY_SIZE(sw_uart_res[0]), .resource = &sw_uart_res[0][0], .dev.release = sunxi_serial_release},
[1] = {.name = "sunxi-uart", .id = 1, .num_resources = ARRAY_SIZE(sw_uart_res[1]), .resource = &sw_uart_res[1][0], .dev.release = sunxi_serial_release},
[2] = {.name = "sunxi-uart", .id = 2, .num_resources = ARRAY_SIZE(sw_uart_res[2]), .resource = &sw_uart_res[2][0], .dev.release = sunxi_serial_release},
[3] = {.name = "sunxi-uart", .id = 3, .num_resources = ARRAY_SIZE(sw_uart_res[3]), .resource = &sw_uart_res[3][0], .dev.release = sunxi_serial_release},
[4] = {.name = "sunxi-uart", .id = 4, .num_resources = ARRAY_SIZE(sw_uart_res[4]), .resource = &sw_uart_res[4][0], .dev.release = sunxi_serial_release},
[5] = {.name = "sunxi-uart", .id = 5, .num_resources = ARRAY_SIZE(sw_uart_res[5]), .resource = &sw_uart_res[5][0], .dev.release = sunxi_serial_release},
[6] = {.name = "sunxi-uart", .id = 6, .num_resources = ARRAY_SIZE(sw_uart_res[6]), .resource = &sw_uart_res[6][0], .dev.release = sunxi_serial_release},
[7] = {.name = "sunxi-uart", .id = 7, .num_resources = ARRAY_SIZE(sw_uart_res[7]), .resource = &sw_uart_res[7][0], .dev.release = sunxi_serial_release},
};
从上面两个结构体可以看出,8个平台总线设备匹配了1个平台总线驱动。
那么接下来我们看看平台总线驱动的probe函数实现了什么。
3. sw_serial_probe函数分析
sw_serial_get_config这个函数从配置文件中得到type和port两个参数,type应该是管脚数目,比如基本的串口使用了tx,rx,那个type就是2,加两个流控,type就是4,全串口type就是8.
sw_serial_get_resource这个函数,从配置文件中得到管脚的定义,并从平台设备的resources中得到,内存和中断资源。
serial8250_register_port serial8250_register_port定义8250.c中
uart_add_one_port 定义在serial_core.c中
tty_register_device 定义在tty_io.c中
下面主要分析这个函数
4. struct device *tty_register_device(struct tty_driver *driver, unsigned index,
struct device *device)
{
char name[64];
这句构造主次设备号
dev_t dev = MKDEV(driver->major, driver->minor_start) + index;
if (index >= driver->num) {
printk(KERN_ERR "Attempt to register invalid tty line number "
" (%d).\n", index);
return ERR_PTR(-EINVAL);
}
if (driver->type == TTY_DRIVER_TYPE_PTY)
pty_line_name(driver, index, name);
else
这句构造串口设备文件
tty_line_name(driver, index, name);
这句用name来创建一个设备文件
return device_create(tty_class, device, dev, NULL, name);
}
最终在这里创建了串口的设备文件,实际这个串口真正硬件资源相关的东西都是在这个平台总线设备和平台总线驱动里来管理的。
最上面的那个串口字符设备驱动只是一个框架性的东西。实际和显示驱动类似,2.6内核以后的很多驱动程序都是这种分层的方式,用平台总线驱动程序来实现。