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中增加一個成員就行,然後重新編譯。