第七章 驅動程序開發-LED驅動-7.6.platform驅動模型-代碼分析

7.6.2.代碼分析
7.6.2.1. ledtest.c

    沒變。
7.6.2.2.leddrv.c

    用代碼對比看了下分層-分離的leddrv.c和platform的leddrv.c,主要區別新的leddrv.c中不再在led_init()中create deivces,而是獨立成函數。

30: void led_class_create_device(int minor)
31: {
32: 		device_create(led_class, NULL, MKDEV(major, minor), NULL, "100ask_led%d", minor);
32: /* /dev/100ask_led0,1,... */
33: }
34: void led_class_destroy_device(int minor)
35: {
36: 		device_destroy(led_class, MKDEV(major, minor));
37: }
38: void register_led_operations(struct led_operations *opr)
39: {
40: 		p_led_opr = opr;
41: }
42:
43: EXPORT_SYMBOL(led_class_create_device); //export給驅動層使用
44: EXPORT_SYMBOL(led_class_destroy_device);
45: EXPORT_SYMBOL(register_led_operations); 

   其中,“EXPORT_SYMBOL標籤內定義的函數或者符號對全部內核代碼公開,不用修改內核代碼就可以在內核模塊中直接調用,即使用EXPORT_SYMBOL可以將一個函數以符號的方式導出給其他模塊使用。”視頻:leddrv.c會編譯成獨立的.ko文件,其他文件如果調用該.ko中的函數,需要使用EXPORT_SYMBOL()將其導出。

    底層文件chip_demo_gpio.c中定義led_opreation結構體board_demo_led_opr,定義了board_demo_led_init()和board_demo_led_ctl(),還定義了獲取該結構體的獲取函數get_board_led_opr()。而驅動層leddrv.c想使用該結構體對led操作就必須先獲取到_init()和_ctl()函數,前提是先獲取到結構體board_demo_led_opr,就得通過獲取函數get_board_led_opr()獲得,所以在leddrv.c中led_init()中先獲取了p_led_opr = get_board_led_opr(),然後再給open()和write()使用。

    而現在是把p_led_opr的獲取單獨定義成了一個函數,register_led_operations(),並沒有去使用它,export它了,意思就是給別的函數使用了,使用之前必須把leddrv.ko先註冊到內核纔行。

7.6.2.3.leddrv.h

    以前是沒有這個.h文件的,因爲leddrv.c使用的函數都是內核的函數,直接include就ok,而現在自己定義了3個函數,需要聲明下,只有驅動層文件leddrv.c調用了它們。

2: #ifndef _LEDDRV_H
3: #define _LEDDRV_H
4:
5: #include "led_opr.h"
6:
7: void led_class_create_device(int minor);
8: void led_class_destroy_device(int minor);
9: void register_led_operations(struct led_operations *opr);
10:
11: #endif /* _LEDDRV_H */

7.6.2.4.led_opr.h

    沒變,定義了led的操作函數,用來給硬件層chip_demo_gpio.c使用。

7.6.2.5.led_resource.h

   去掉了struct led_resource .pin,應該要定義到platform_device中去。硬件層chip_demo_gpio.c和board_A_led.c都調用了該文件。

1: #ifndef _LED_RESOURCE_H
2: #define _LED_RESOURCE_H
3:
4: /* GPIO3_0 */
5: /* bit[31:16] = group */
6: /* bit[15:0] = which pin */
7: #define GROUP(x) (x>>16)
8: #define PIN(x) (x&0xFFFF)
9: #define GROUP_PIN(g,p) ((g<<16) | (p))
10:
12: #endif

7.6.2.6.board_A_led.c

    board_A_led.c把board_A的led抽象成platform_device註冊到內中,成員按照內核格式來定義,爲其分配resource,即pins。

18: #include <linux/platform_device.h>
20: #include "led_resource.h"
21:
22: /* 定義led_dev釋放函數 */
23: static void led_dev_release(struct device *dev)
24: {
25: }
26:
27:
28: /* 定義led引腳資源 */
29: static struct resource resources[] = {
30: {
31: 		.start = GROUP_PIN(3,1),
32: 		.flags = IORESOURCE_IRQ,
33: 		.name = "100ask_led_pin",
34: },
35: {
36: 		.start = GROUP_PIN(5,8),
37: 		.flags = IORESOURCE_IRQ,
38: 		.name = "100ask_led_pin",
39: },
40: };
41:
42: /* 定義名爲100ask_led的platform_device */
43: static struct platform_device board_A_led_dev = {
44: 		.name = "100ask_led",
45: 		.num_resources = ARRAY_SIZE(resources),
46: 		.resource = resources,
47: 		.dev = {
48: 			.release = led_dev_release,
49: 		},
50: };
51:
52: /* 定義board_A_led_dev註冊函數 */
53: static int __init led_dev_init(void)
54: {
55: 		int err;
57: 		err = platform_device_register(&board_A_led_dev);
59:		return 0;
60: }
61:
62: /* 定義board_A_led_dev卸載函數 */
63: static void __exit led_dev_exit(void)
64: {
65: 		platform_device_unregister(&board_A_led_dev);
66: }
68: module_init(led_dev_init);
69: module_exit(led_dev_exit);
71: MODULE_LICENSE("GPL"); 

led_class_create_device次設備爲什麼不能在這個文件中定義呢?需要在leddrv.c定義?想想。

7.6.2.7.chip_demo_gpio.c

   按照之前的代碼就是包含init和ctl函數,到目前platform_driver還沒有註冊,那肯定要在這個文件中註冊了。

106: /* 定義名爲100ask_led的platform_driver */
157: static struct platform_driver chip_demo_gpio_driver = {
158: 	.probe = chip_demo_gpio_probe,
159: 	.remove = chip_demo_gpio_remove,
160: 	.driver = {
161: 		.name = "100ask_led",
162: 	},
163: };
164:
166: /* 定義函數向內核註冊GPIO的platform_driver以及獲取led_operation結構體 */
167: static int __init chip_demo_gpio_drv_init(void)
168: {
169: 	int err;
170:
171: 	/* 註冊chip_demo_gpio_driver */
172: 	err = platform_driver_register(&chip_demo_gpio_driver);
173:
174: 	/* 獲取board_demo_led_opr,這裏獲取了怎麼共享給leddrv.c???*/
175: 	register_led_operations(&board_demo_led_opr);
176:
177: 	return 0;
178: } 

105   /*
107: * 定義gpio_probe函數,當platform_driver和platform_device配對之後就執行該函數;
110: */
111: static int chip_demo_gpio_probe(struct platform_device *pdev)
112: {
113: 	struct resource *res;
114: 	int i = 0;
115:
116: 	while (1)
117: 	{
118: 	/* 獲取platform_device資源 */
119: 	res = platform_get_resource(pdev, IORESOURCE_IRQ, i++);
120:
121: 	if (!res)
122: 		break;
123: 	printk("platform_device %s and platform_driver matching sucessed!\n", pdev->name);
124:
125: 	/* 獲取g_ledpins[g_ledcnt] */
126: 	g_ledpins[g_ledcnt] = res->start;
127: 	printk("g_ledcnt : %d,\n g_ledpins[g_ledcnt] : %x \n\n", g_ledcnt, g_ledpins[g_ledcnt]);
128:
129: 	/* 註冊次設備 */
130: 	led_class_create_device(g_ledcnt);
132: 	g_ledcnt++;
134: }
135: return 0;
136: } « end chip_demo_gpio_probe » 

   明確一下3個.h文件的作用,一個是led聲明led操作的,一個是聲明led資源的,也就是引腳的;一個是聲明leddrv中函數的。可不可以把led操作和資源放在一個.h中呢。

    總結一下整個驅動程序的架構:

分層:驅動層依然是leddrv.c,變化的是不再在該層獲取led_operations結構體,也不在該層註冊次設備,只是定義獲取和註冊函數,供硬件層調用;

分離:芯片和單板配置分離,也就是platform_device和platform_driver分離。在board_A_led.c定義platform_device註冊到內核中去,並定義所用資源(Pins)。在chip_demo_gpio.c中定義了platform_driver,將其註冊到內中去,獲取led_operations結構體,定義了probe用來獲取資源註冊次設備。同時定義了led_operations結構體及其init/ctl函數(突然感覺爲啥不把platform_driver單獨做一個.c呢,和init/ctl分開)。

 7.6.2.8.編譯執行

修改Makefile,編譯成單個.ko,而不是編譯成一個.ko。

obj-m += leddrv.o chip_demo_gpio.o board_A_led.o

    連接開發板,掛載,3個.o都生成.ko先後insmod的順序是什麼?

    猜測:leddrv.o應該是第一個,先有100ask_led這個chedev存在和幾個函數,後面兩個順序可以不要求,然後註冊platform_device/platform_driver,通過name:100ask_led,來匹配。

    做了6組不同順序的insmod,發現必須先insmod leddrv.ko再insmod chip_demo_gpio.ko,否則會報錯,從報錯信息看出,chip_demo_gpio.ko需要leddrv.o中的幾個函數。

     調出打印信息:echo 7       4       1      7 > /proc/sys/kernel/printk

    如果想增加LEDs呢,只需要在struct resource中增加一個成員就行,然後重新編譯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章