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.");