头文件
-
Linux中申请GPIO的头文件
include/linux/gpio.h
-
三星平台的GPIO配置函数的包含头文件
arch/arm/plat-samsung/include/plat/gpio-cfg.h
-
EXYNOS平台GPIO配置函数传参定义的包含头文件
arch/arm/mach-exynos/include/mach/gpio.h(在该头文件中已经引用了gpio-exynos4.h)
-
EXYNOS平台4412平台处理器引脚有关的宏
arch/arm/mach-exynos/include/mach/gpio-exynos4.h
函数和宏
-
linux GPIO申请函数和赋值函数
申请:gpio_request
赋值:gpio_set_value
-
三星平台配置GPIO函数和参数
函数:s3c_gpio_cfgpin
输出参数:S3C_GPIO_OUTPUT
编写之前
驱动probe函数中干的事情一般有:注册设备节点(其实任意地方均可注册)、申请资源(如GPIO)
开始
将leds驱动从内核去掉
本节内容按照这篇文章进行操作:这里
也就是取消leds编译进内核(取消宏定义,那么leds内核设备信息、驱动被取消编译),下面会重写一个led驱动和设备信息以模块加载进内核进行实验。
避免因为存在的leds驱动申请了GPIO资源而导致我们新写的led驱动在申请资源时失败。
完成后make zImage
生成内核,进行烧录。
编写led驱动和设备信息
驱动和设备信息匹配的文章参见: 这里(不过这个是设备信息自动注册到内核了),本节介绍的是编写模块注册驱动和设备信息。
编写led设备信息
加载该设备信息模块后,会在/sys/devices/platform/
目录生成DEV_NAME的设备信息。
led_device.c :注册设备信息的文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("jerry");
#define DEV_NAME "platform_led"
static void led_release(struct device *pdv){
printk(KERN_EMERG "led_release ok!\n");
};
static struct platform_device platform_dev_led = {
.name = DEV_NAME,
.id = -1,
.dev = {
.release = led_release,
},
};
static int led_init(void){
/*注册到/sys/devices/platform目录*/
platform_device_register(&platform_dev_led);
return 0;
}
static void led_exit(void){
platform_device_unregister(&platform_dev_led);
}
module_init(led_init);
module_exit(led_exit);
编写led驱动
这里只申请一个led灯的GPIO资源,当驱动DRV_NAME和设备信息中DEV_NAME匹配后就会执行probe函数:申请GPIO,设置输出模式,初始拉低,注册设备节点到dev目录;一旦失去匹配:释放GPIO,取消dev目录的注册。
led_driver.c :注册驱动和设备节点申请资源的文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
/*注册杂项设备设备节点的头文件*/
#include <linux/miscdevice.h>
/*文件操作的头文件*/
#include <linux/fs.h>
/*linux 申请的gpio头文件*/
#include <linux/gpio.h>
/*配置函数和参数配置的宏头文件*/
#include <plat/gpio-cfg.h>
/*配置参数头文件*/
#include <mach/gpio.h>
/*gpio引脚相关头文件,可不要,在上面的文件中已包含*/
//#include <mach/gpio-exynos4.h>
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("jerry");
#define DRV_NAME "platform_led"
#define NODE_NAME "led"
static int led_open(struct inode *inode, struct file *file){
printk(KERN_EMERG "led_open ok!\n");
return 0;
}
static int led_release(struct inode *inode, struct file *file){
printk(KERN_EMERG "led_release ok!\n");
return 0;
}
static long led_ioctl(struct file *file, unsigned int cmd, unsigned long arg){
printk(KERN_EMERG "cmd is %d,arg is %ld;\n",cmd,arg);
if(cmd > 1){
printk(KERN_EMERG "cmd is 0 or 1\n");
return 1;
}
if(arg != 1){
printk(KERN_EMERG "arg is only 1\n");
return 1;
}
gpio_set_value(EXYNOS4_GPL2(0),cmd);
return 0;
}
static struct file_operations led_ops = {
.owner = THIS_MODULE,
.open = led_open, // open节点执行
.release = led_release, // close节点执行
.unlocked_ioctl = led_ioctl,
};
static struct miscdevice misc_led = {
.minor = MISC_DYNAMIC_MINOR, // 自动分配
.name = NODE_NAME,
.fops = &led_ops,
};
static int led_probe(struct platform_device *pdv){
int ret;
ret = gpio_request(EXYNOS4_GPL2(0),"LED"); /*申请GPIO资源*/
if(ret < 0){
printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed!\n");
return ret;
}
s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT); /*设置输出模式*/
gpio_set_value(EXYNOS4_GPL2(0),0); /*置低*/
misc_register(&misc_led); /*注册到dev目录,可任意放*/
printk(KERN_EMERG "led_probe ok!\n");
return 0;
}
static int led_remove(struct platform_device *pdv){
gpio_free(EXYNOS4_GPL2(0)); /*释放GPIO资源*/
misc_deregister(&misc_led);
printk(KERN_EMERG "led_remove ok!\n");
return 0;
}
static void led_shutdown(struct platform_device *pdv){
printk(KERN_EMERG "led_shutdown ok!\n");
}
static int led_suspend(struct platform_device *pdv, pm_message_t state){
printk(KERN_EMERG "led_suspend ok!\n");
return 0;
}
static int led_resume(struct platform_device *pdv){
printk(KERN_EMERG "led_resume ok!\n");
return 0;
}
static struct platform_driver platform_drv_led = {
.probe = led_probe,
.remove = led_remove,
.shutdown = led_shutdown,
.suspend = led_suspend,
.resume = led_resume,
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
};
static int led_init(void){
platform_driver_register(&platform_drv_led);
return 0;
}
static void led_exit(void){
platform_driver_unregister(&platform_drv_led);
}
module_init(led_init);
module_exit(led_exit);
进行编译和测试
-
对编写的驱动文件和设备信息文件进行模块编译,Makefile的编写可参考:Makefile模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#!/bin/bash obj-m += led_device.o obj-m += led_driver.o KDIR := /home/jerry/Projects/iTop4412_Kernel_3.0 PWD ?= $(shell pwd) all: make -C $(KDIR) M=$(PWD) modules clean: rm -rf *.o
-
编写led系统编程测试文件,并并交叉编译为可执行文件led_test
led_test.c :系统编程中调用生成的设备节点的文件,操作led闪烁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#include <stdio.h> // 包含打印函数printf #include <stdlib.h> // 包含exit函数 #include <sys/types.h> // 一下基本数据类型的宏定义 #include <fcntl.h> // 包含open函数 #include <unistd.h> // 包含close函数 #include <sys/ioctl.h> // 包含ioctl函数 int main(){ int fd; char *led_node = "/dev/led"; // 可读可写,非阻塞方式 if((fd = open(led_node,O_RDWR|O_NDELAY)) < 0){ printf("Test: Failed to open %s!\n",led_node); exit(1); } printf("Test: Open %s successfully!\n",led_node); // 控制一个led 亮 灭 亮 ioctl(fd,1,1); // 高 sleep(2); ioctl(fd,0,1); // 低 sleep(2); ioctl(fd,1,1); // 高 close(fd); return 0; }
-
准备好所有文件后,进行测试
编译前:
编译后:
- 拷贝驱动模块、设备信息模块、可执行测试程序到iTop4412(推荐nfs)
- 挂载设备信息模块、驱动模块,然后执行测试程序