Linux串列埠驅動程式(2)-串列埠驅動程式初始化分析
阿新 • • 發佈:2019-01-09
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檔案,裡面有這個函式:它使用uart_register_driver這個函式註冊了一個串列埠驅動。跳到這個串列埠驅動的定義中:/* 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; }
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。 最後總結如下圖: