Note06_02_LED驅動_GPIO子系統控制
Note06_02_LED驅動_GPIO子系統控制
接上節,使用另一種方式來初始化LED的暫存器:採用GPIO子系統函式介面,對LED的引腳對應的SFR進行設定和初始化,然後使用 ioctl命令來 讀取和設定LED燈的狀態;
-
Led硬體電路圖
-
Led GPIO引腳控制暫存器
參見上節《Note06_01_LED驅動_ioctl傳輸命令》內容。
-
GPIO子系統:
linux核心封裝了一套專門用於操作 GPIO 的介面函式,這些介面函式是通過一個GPIO號來訪問控制的,現將常用的GPIO 介面函式羅列如下:
To test if such number from such a structure could reference a GPIO, you may use this predicate: int gpio_is_valid(int number);
Using GPIOs
-----------
The first thing a system should do with a GPIO is allocate it, using the gpio_request() call; see later.
One of the next things to do with a GPIO, often in board setup code when setting up a platform_device using the GPIO, is mark its direction:
/* set as input or output, returning 0 or negative errno */
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
The return value is zero for success, else a negative errno. It should be checked, since the get/set calls don't have error returns and since misconfiguration is possible.
Spinlock-Safe GPIO access
-------------------------
Most GPIO controllers can be accessed with memory read/write instructions.
Those don't need to sleep, and can safely be done from inside hard (nonthreaded) IRQ handlers and similar contexts.
Use the following calls to access such GPIOs, for which gpio_cansleep() will always return false (see below):
/* GPIO INPUT: return zero or nonzero */
int gpio_get_value(unsigned gpio);
/* GPIO OUTPUT */
void gpio_set_value(unsigned gpio, int value);
The values are boolean, zero for low, nonzero for high. When reading the value of an output pin, the value returned should be what's seen on the pin ... that won't always match the specified output value, because of issues including open-drain signaling and output latencies.
linux GPIO 子系統的相關介紹,詳見之前寫過的剛查閱的一篇文章《linux GPIO子系統核心原始碼追蹤》
-
Led驅動程式框架及驅動實現流程
1)驅動程式入口實現
通過gpio 子系統介面函式,實現資源申請
設定LED pin 引腳模式,及初始電平
註冊 misc 雜項裝置驅動
122 /*
123 ** init miscdecvice's source
124 */
125 static struct miscdevice misc = {
126 .minor = MISC_DYNAMIC_MINOR,
127 .name = DEVNAME,
128 .fops = &fops,
129 };
// 驅動入口函式
132 static int __init demo_init(void)
133 {
134 int ret;
135 int i;
136 // request GPIO_sys source: arg (pin, name)
137 for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
138 ret = gpio_request(ledgpios[i], "led");
139 if (ret < 0) {
140 goto error0;
141 }
142 // set mode of LED's pins
143 ret = gpio_direction_output(ledgpios[i], 1);
144 if (ret < 0) {
145 gpio_free(ledgpios[i]);
146 goto error0;
147 }
148 }
149 // register misc's devive
150 ret = misc_register(&misc);
151 if (ret < 0) {
152 goto error0;
153 }
154
155 return 0;
156
157 error0:
158 while (i--) {
159 gpio_free(ledgpios[i]);
160 }
161
162 return ret;
163 }
164
165 module_init(demo_init);
2)驅動程式出口實現
復位LED pin引腳的控制電平:非使能態,1
釋放申請的gpio 子系統資源
登出 misc 雜項裝置驅動
167 static void __exit demo_exit(void)
168 {
169 int i;
170
171 for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
172 // reset led pins' state is unenable
173 gpio_set_value(ledgpios[i], 1);
174 // free request gpio resource
175 gpio_free(ledgpios[i]);
176 }
177
178 misc_deregister(&misc);
179 }
180
181 module_exit(demo_exit);
3)驅動程式fops操作介面實現
open() 函式實現
ioctl() 函式實現
release() 函式實現
115 static struct file_operations fops = {
116 .owner = THIS_MODULE,
117 .open = mill_open,
118 .unlocked_ioctl = mill_unlocked_ioctl,
119 .release = mill_release,
120 };
-
Led驅動實現流程
通過執行測試程式,傳入引數直接控制 LED 燈,比如 ./test onall,./test on 1
1)首先開啟驅動註冊的LED 驅動裝置檔案;
2)根據傳入的引數個數,以及控制引數和LED 的編號(是控制第一個還是最後1個),構造 ioctl 的命令碼
3)通過 ioctl() 系統呼叫函式,直接進行控制 LED 燈;
21 int main(int argc, char **argv)
22 {
23 int fd;
24 int num;
25 int request;
26 int ret;
27 char buf[4] = {0};
28 int i;
29
30 if ((argc != 2)&&(argc != 3)) {
31 usage(argv[0]);
32 }
33 // 開啟leds 雜項裝置驅動,試想,若不註冊雜項裝置驅動,則操作led燈
34 // 時,每個led燈都要有可操作的裝置節點;
35 fd = open("/dev/leds0-4", O_RDWR | O_NDELAY);
36 assert(fd > 0);
37
38 if (argc == 2)
39 { // 全部開啟
40 if (!strncmp("onall", argv[1], 5))
41 {
42 ret = ioctl(fd, LED_ON_ALL);
43 assert(ret == 0);
44 }// 全部關閉
45 else if (!strncmp("offall", argv[1], 5))
46 {
47 ret = ioctl(fd, LED_OFF_ALL);
48 assert(ret == 0);
49 }// 獲取當前狀態
50 else if (!strncmp("stat", argv[1], 4))
51 {
52 ret = ioctl(fd, LED_GET_STAT, buf);
53 for (i = 0; i < 4; i++) {
54 if (buf[i] == '1')
55 {
56 printf("led %d is on\n", i+1);
57 } else if (buf[i] == '0')
58 {
59 printf("led %d is off\n", i+1);
60 } else {
61 exit(1);
62 }
63 }
64 } else {
65 usage(argv[0]);
66 }
67 }
68 else
69 { // 開啟指定的 led燈
70 if (!strncmp("on", argv[1], 2))
71 {
72 request = LED_ON;
73 }// 關閉指定的 led燈
74 else if (!strncmp("off", argv[1], 3))
75 {
76 request = LED_OFF;
77 }
78 else
79 {
80 usage(argv[0]);
81 }
82 // 通過可執行檔案傳入的'第幾個led燈'引數,是字元型,需轉換為整形
83 num = atoi(argv[2]);
84 // 通過ioctl 系統呼叫,下發控制命令
85 ret = ioctl(fd, request, num);
86 assert(ret == 0);
87 }
88
89 return 0;
90 }
assert巨集的原型定義在<assert.h>中,其作用是如果它的條件返回錯誤,則終止程式執行,原型定義:
#include <assert.h>
void assert( int expression );
assert的作用是現計算表示式 expression ,如果其值為假(即為0),那麼它先向stderr列印一條出錯資訊,然後通過呼叫 abort 來終止程式執行。
缺點: 頻繁的呼叫會極大的影響程式的效能,增加額外的開銷。
4)ioclt 命令碼:應用層 和 驅動層使用統一套命令碼命名規則;
1 #ifndef MILLET_LED_H_
2 #define MILLET_LED_H_
3
4 #include <linux/ioctl.h>
5
6 #define LEDTYPE 'L' // 幻數
7
8 #define LED_ON _IOW(LEDTYPE, 0, int) // 控制命令
9 #define LED_OFF _IOW(LEDTYPE, 1, int)
10 #define LED_ON_ALL _IO(LEDTYPE, 2)
11 #define LED_OFF_ALL _IO(LEDTYPE, 3)
12 #define LED_GET_STAT _IOR(LEDTYPE, 4, int)
13
14 #endif
-
測試結果
1)插入LED 裝置驅動
2)檢視註冊的裝置驅動名稱:次裝置號為 50,裝置名:leds0-4
因是雜項裝置驅動,故在 /proc/misc 目錄下檢視
3)整個系統中搜索 裝置名
/dev/leds0-4 // LED 裝置檔案節點,包含4個LED
/sys/devices/virtual/misc/leds0-4 // 暫時未知
/sys/class/misc/leds0-4 // 因驅動裝置檔案是通過 misc 雜項裝置機制註冊的,故自動建立了裝置節點
備註: class_create() 自動建立裝置節點形式,晚上進行總結;
4)點亮所有 LED,並檢視狀態
12 // LED 控制狀態,或當前狀態
13 enum led{
14 OFF, // 0, led unlight
15 ON // 1, led light
16 };
5)熄滅LED 3,並檢視狀態
-
ioctl.h 檔案
應用層和驅動,共用 ioctl() 命令碼:
1 #ifndef MILLET_LED_H_
2 #define MILLET_LED_H_
3
4 #include <linux/ioctl.h>
5
6 #define LEDTYPE 'L'
7
8 #define LED_ON _IOW(LEDTYPE, 0, int)
9 #define LED_OFF _IOW(LEDTYPE, 1, int)
10 #define LED_ON_ALL _IO(LEDTYPE, 2)
11 #define LED_OFF_ALL _IO(LEDTYPE, 3)
12 #define LED_GET_STAT _IOR(LEDTYPE, 4, int)
13
14 #endif
-
Led 測試程式實現
1 #include <stdio.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <assert.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "ioctl.h"
11
12 void usage(const char *str)
13 {
14 fprintf(stderr, "Usage:\n");
15 fprintf(stderr, " %s on/off num(1~4)\n", str);
16 fprintf(stderr, " %s onall/offall\n", str);
17 fprintf(stderr, " %s stat\n", str);
18 exit(1);
19 }
20
21 int main(int argc, char **argv)
22 {
23 int fd;
24 int num;
25 int request;
26 int ret;
27 char buf[4] = {0};
28 int i;
29
30 if ((argc != 2)&&(argc != 3)) {
31 usage(argv[0]);
32 }
33 // 開啟leds 雜項裝置驅動,試想,若不註冊雜項裝置驅動,則操作led燈
34 // 時,每個led燈都要有可操作的裝置節點;
35 fd = open("/dev/leds0-4", O_RDWR | O_NDELAY);
36 assert(fd > 0);
37
38 if (argc == 2)
39 { // 全部開啟
40 if (!strncmp("onall", argv[1], 5))
41 {
42 ret = ioctl(fd, LED_ON_ALL);
43 assert(ret == 0);
44 }// 全部關閉
45 else if (!strncmp("offall", argv[1], 5))
46 {
47 ret = ioctl(fd, LED_OFF_ALL);
48 assert(ret == 0);
49 }// 獲取當前狀態
50 else if (!strncmp("stat", argv[1], 4))
51 {
52 ret = ioctl(fd, LED_GET_STAT, buf);
53 for (i = 0; i < 4; i++) {
54 if (buf[i] == '1')
55 {
56 printf("led %d is on\n", i+1);
57 } else if (buf[i] == '0')
58 {
59 printf("led %d is off\n", i+1);
60 } else {
61 exit(1);
62 }
63 }
64 } else {
65 usage(argv[0]);
66 }
67 }
68 else
69 { // 開啟指定的 led燈
70 if (!strncmp("on", argv[1], 2))
71 {
72 request = LED_ON;
73 }// 關閉指定的 led燈
74 else if (!strncmp("off", argv[1], 3))
75 {
76 request = LED_OFF;
77 }
78 else
79 {
80 usage(argv[0]);
81 }
82 // 通過可執行檔案傳入的'第幾個led燈'引數,是字元型,需轉換為整形
83 num = atoi(argv[2]);
84 // 通過ioctl 系統呼叫,下發控制命令
85 ret = ioctl(fd, request, num);
86 assert(ret == 0);
87 }
88
89 return 0;
90 }
-
Led驅動程式實現
1 #include <linux/init.h>
2 #include <linux/module.h>
3 #include <linux/fs.h>
4 #include <linux/io.h>
5 #include <linux/gpio.h>
6 #include <linux/miscdevice.h>
7 #include "ioctl.h"
8
9 // misc 裝置驅動名稱
10 #define DEVNAME "leds0-4"
11
12 // LED 控制狀態,或當前狀態
13 enum led{
14 OFF, // 0, led unlight
15 ON // 1, led light
16 };
17
18 // LED gpio 編號,使用該變化,可向系統申請資源
19 // EXYNOS4X12_GPM4(x),這個巨集解析後,為對應引腳暫存器的首地址
20 static int ledgpios[] = {
21 EXYNOS4X12_GPM4(0),
22 EXYNOS4X12_GPM4(1),
23 EXYNOS4X12_GPM4(2),
24 EXYNOS4X12_GPM4(3)
25 };
26
27 static int
28 mill_open (struct inode *inodp, struct file *filp)
29 {
30 printk("KER-[%s]\n", __func__);
31 return 0;
32 }
33
34 void led_ctl(enum led cmd, int arg)
35 {
36 printk("KER-[%s], control LED%d's state is %d\n", __func__, arg, cmd);
37 // LED 低電平為有效電平,亮
38 if (cmd == ON) {
39 gpio_set_value(ledgpios[arg-1], 0);
40 } else if (cmd == OFF) {
41 gpio_set_value(ledgpios[arg-1], 1);
42 }
43 }
44
45 /*
46 ** set leds all ON or OFF
47 */
48 void led_ctl_all(enum led cmd)
49 {
50 int i;
51
52 for (i = 0; i < 4; i++) {
53 if (cmd == ON) {
54 led_ctl(ON, i+1);
55 } else {
56 led_ctl(OFF, i+1);
57 }
58 }
59 }
60 /*
61 ** get leds status
62 */
63 static void led_get_stat(char *buf)
64 {
65 int i;
66
67 for (i = 0; i < 4; i++) {
68 buf[i] = gpio_get_value(ledgpios[i])?'0':'1';
69 printk("KER-[%s], get LED%d's state is %c\n", __func__, (i+1), buf[i]);
70 }
71 }
72
73 /*
74 ** get user space's ioctl cmd to contral leds
75 ** request: ioctl CMD from user space
76 */
77 static long
78 mill_unlocked_ioctl (struct file *filp, unsigned int request, unsigned long arg)
79 {
80 printk("KER-[%s], TYPE: %c, NR: %d\n", __func__, _IOC_TYPE(request), _IOC_NR(reque st));
81 if (_IOC_TYPE(request) == LEDTYPE) {// get ioctl cmd TYPE's data
82 switch (_IOC_NR(request)) { // get ioctl cmd's data
83 case 0:
84 if (arg < 1 || arg > 4) {
85 return -EINVAL;
86 }
87 led_ctl(ON, arg);
88 break;
89 case 1:
90 if (arg < 1 || arg > 4) {
91 return -EINVAL;
92 }
93 led_ctl(OFF, arg);
94 break;
95 case 2:
96 led_ctl_all(ON);
97 break;
98 case 3:
99 led_ctl_all(OFF);
100 break;
101 case 4:
102 led_get_stat((char *)arg);
103 default:
104 return -EINVAL;
105 }
106 }
107
108 return 0;
109 }
110
111 static int
112 mill_release (struct inode *inodp, struct file *filp)
113 {
114
115 return 0;
116 }
117
118 static struct file_operations fops = {
119 .owner = THIS_MODULE,
120 .open = mill_open,
121 .unlocked_ioctl = mill_unlocked_ioctl,
122 .release = mill_release,
123 };
124
125 /*
126 ** init miscdecvice's source
127 */
128 static struct miscdevice misc = {
129 .minor = MISC_DYNAMIC_MINOR,
130 .name = DEVNAME,
131 .fops = &fops,
132 };
133
134 /*ioread32/ioread16/ioared8 iowrite32/iowrite16/iowrite8*/
135 static int __init demo_init(void)
136 {
137 int ret;
138 int i;
139 // request GPIO_sys source: arg (pin, name)
140 for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
141 ret = gpio_request(ledgpios[i], "led");
142 if (ret < 0) {
143 goto error0;
144 }
145 // set mode of LED's pins
146 ret = gpio_direction_output(ledgpios[i], 1);
147 if (ret < 0) {
148 gpio_free(ledgpios[i]);
149 goto error0;
150 }
151 }
152 // register misc's devive
153 ret = misc_register(&misc);
154 if (ret < 0) {
155 goto error0;
156 }
157
158 printk("KER-[%s] leave.\n", __func__);
159 return 0;
160
161 error0:
162 while (i--) {
163 gpio_free(ledgpios[i]);
164 }
165
166 return ret;
167 }
168
169 module_init(demo_init);
170
171 static void __exit demo_exit(void)
172 {
173 int i;
174
175 for (i = 0; i < ARRAY_SIZE(ledgpios); ++i) {
176 // reset led pins' state is unenable
177 gpio_set_value(ledgpios[i], 1);
178 // free request gpio resource
179 gpio_free(ledgpios[i]);
180 }
181
182 misc_deregister(&misc);
183 }
184
185 module_exit(demo_exit);
186
187 MODULE_LICENSE("GPL");
188
189 MODULE_AUTHOR("zhang li lin");
190 MODULE_VERSION("zhang leds control2");
191 MODULE_DESCRIPTION("It is a simple example for module.");