1、串口驱动程序结构分析
对用户来讲,能够正常使用串口肯定是需要实现如下函数的:
1、串口设备文件的打开
2、串口设备文件的初始化
3、串口设备文件的读写
4、串口设备文件的控制
2、串口驱动中重要的数据结构
首先分析一下串口读写的流程
当用户读写串口设备文件的时候,就会调用到usart_write函数(图中没有),在usart_write函数中会读取uart_state数组里的数据,数组中的元素包含2个信息,info和port分别对应不同串口的uart_port和uart_info,在uart_port里面包含uart_ops,这里面的函数就是用来实现硬件操作的。uart_state数组的获取又依赖于uart_driver结构,在执行uart_open时,他的指针会指向uart_state。
• UART驱动程序结构:struct uart_driver,对应一个串口驱动
• UART端口结构: struct uart_port,对应一个串口设备
• UART相关操作函数结构: struct uart_ops,对应串口的硬件操作函数
• UART状态结构: struct uart_state
• UART信息结构: struct uart_info
3、初始化分析
打开Samsung.c文件,里面有这个函数:
/* module initialisation code */
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 0;
}
它使用uart_register_driver这个函数注册了一个串口驱动。跳到这个串口驱动的定义中:
static struct uart_driver s3c24xx_uart_drv = {
.owner = THIS_MODULE,
.dev_name = "s3c2410_serial",
.nr = CONFIG_SERIAL_SAMSUNG_UARTS,
.cons = S3C24XX_SERIAL_CONSOLE,
.driver_name = S3C24XX_SERIAL_NAME,
.major = S3C24XX_SERIAL_MAJOR,
.minor = S3C24XX_SERIAL_MINOR,
};
再打开s3c2440.c文件,找到模块初始化函数:
static int __init s3c2440_serial_init(void)
{
return s3c24xx_serial_init(&s3c2440_serial_driver, &s3c2440_uart_inf);
}
static void __exit s3c2440_serial_exit(void)
{
platform_driver_unregister(&s3c2440_serial_driver);
}
module_init(s3c2440_serial_init);
module_exit(s3c2440_serial_exit);
模块初始化函数优势调用s3c24xx_serial_init实现的,找到它的定义
int s3c24xx_serial_init(struct platform_driver *drv,
struct s3c24xx_uart_info *info)
{
dbg("s3c24xx_serial_init(%p,%p)\n", drv, info);
#ifdef CONFIG_PM
drv->suspend = s3c24xx_serial_suspend;
drv->resume = s3c24xx_serial_resume;
#endif
return platform_driver_register(drv);
}
这里面又是调用平台驱动初始化来完成的。在平台设备注册的时候回将驱动和设备匹配,如果匹配成功,将调用驱动的prob函数,我们看看s3c2440_serial_driver 的prob函数是什么样的:
static struct platform_driver s3c2440_serial_driver = {
.probe = s3c2440_serial_probe,
.remove = __devexit_p(s3c24xx_serial_remove),
.driver = {
.name = "s3c2440-uart",
.owner = THIS_MODULE,
},
};
找到s3c2440_serial_probe最终实现的地方:
int s3c24xx_serial_probe(struct platform_device *dev,
struct s3c24xx_uart_info *info)
{
struct s3c24xx_uart_port *ourport;
int ret;
dbg("s3c24xx_serial_probe(%p, %p) %d\n", dev, info, probe_index);
ourport = &s3c24xx_serial_ports[probe_index];
probe_index++;
dbg("%s: initialising port %p...\n", __func__, ourport);
ret = s3c24xx_serial_init_port(ourport, info, dev);
if (ret < 0)
goto probe_err;
dbg("%s: adding port\n", __func__);
uart_add_one_port(&s3c24xx_uart_drv, &ourport->port);
platform_set_drvdata(dev, &ourport->port);
ret = device_create_file(&dev->dev, &dev_attr_clock_source);
if (ret < 0)
printk(KERN_ERR "%s: failed to add clksrc attr.\n", __func__);
ret = s3c24xx_serial_cpufreq_register(ourport);
if (ret < 0)
dev_err(&dev->dev, "failed to add cpufreq notifier\n");
return 0;
probe_err:
return ret;
}
我们来分析这段代码:
这段代码首先从s3c24xx_serial_ports数组中寻找一个元素,这个数组里保存的是各个串口的信息。
假如说找到了串口0,拿到串口0后调用s3c24xx_serial_init_port完成串口的初始化,看看初始化函数:
/* s3c24xx_serial_init_port
*
* initialise a single serial port from the platform device given
*/
static int s3c24xx_serial_init_port(struct s3c24xx_uart_port *ourport,
struct s3c24xx_uart_info *info,
struct platform_device *platdev)
{
struct uart_port *port = &ourport->port;
struct s3c2410_uartcfg *cfg;
struct resource *res;
int ret;
dbg("s3c24xx_serial_init_port: port=%p, platdev=%p\n", port, platdev);
if (platdev == NULL)
return -ENODEV;
cfg = s3c24xx_dev_to_cfg(&platdev->dev);
if (port->mapbase != 0)
return 0;
if (cfg->hwport > CONFIG_SERIAL_SAMSUNG_UARTS) {
printk(KERN_ERR "%s: port %d bigger than %d\n", __func__,
cfg->hwport, CONFIG_SERIAL_SAMSUNG_UARTS);
return -ERANGE;
}
/* setup info for port */
port->dev = &platdev->dev;
ourport->info = info;
/* copy the info in from provided structure */
ourport->port.fifosize = info->fifosize;
dbg("s3c24xx_serial_init_port: %p (hw %d)...\n", port, cfg->hwport);
port->uartclk = 1;
if (cfg->uart_flags & UPF_CONS_FLOW) {
dbg("s3c24xx_serial_init_port: enabling flow control\n");
port->flags |= UPF_CONS_FLOW;
}
/* sort our the physical and virtual addresses for each UART */
res = platform_get_resource(platdev, IORESOURCE_MEM, 0);
if (res == NULL) {
printk(KERN_ERR "failed to find memory resource for uart\n");
return -EINVAL;
}
dbg("resource %p (%lx..%lx)\n", res, res->start, res->end);
port->mapbase = res->start;
port->membase = S3C_VA_UART + res->start - (S3C_PA_UART & 0xfff00000);
ret = platform_get_irq(platdev, 0);
if (ret < 0)
port->irq = 0;
else {
port->irq = ret;
ourport->rx_irq = ret;
ourport->tx_irq = ret + 1;
}
ret = platform_get_irq(platdev, 1);
if (ret > 0)
ourport->tx_irq = ret;
ourport->clk = clk_get(&platdev->dev, "uart");
dbg("port: map=%08x, mem=%08x, irq=%d (%d,%d), clock=%ld\n",
port->mapbase, port->membase, port->irq,
ourport->rx_irq, ourport->tx_irq, port->uartclk);
/* reset the fifos (and setup the uart) */
s3c24xx_serial_resetport(port, cfg);
return 0;
}
这里面主要完成3项工作:
1、取串口的基地址
2、取串口的中断号
3、复位FIFO
在回到s3c24xx_serial_probe函数,在初始化串口后,接下来完成下面的操作:
2、添加端口uart_add_one_port
3、添加属性文件,这样在sys下面就可以看到串口的信息了
4、初始化动态频率调节s3c24xx_serial_cpufreq_register。
最后总结如下图: