1. 程式人生 > >Linux串列埠驅動程式(2)-串列埠驅動程式初始化分析

Linux串列埠驅動程式(2)-串列埠驅動程式初始化分析

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。 最後總結如下圖: