4 Led 驱动编写
vim Makefile
1 obj-m := demo.o
2
3 KERNEL := /linux-3.5
4
5 all:
6 make -C $(KERNEL) M=`pwd`
7 clean:
8 make -C $(KERNEL) M=`pwd` clean
9
ioctl.h
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_CTL_ON _IOW(LEDTYPE, 1, int)
9 #define LED_CTL_OFF _IOW(LEDTYPE, 2, int)
10 #define LED_CTL_STAT _IOR(LEDTYPE, 3, int)
11
12 #endif
demo.c
1 /* head file */
2 #include <linux/init.h>
3 #include <linux/module.h>
4 #include <linux/fs.h>
5 #include <linux/cdev.h>
6 #include <linux/uaccess.h>
7 #include <linux/slab.h>
8 #include <linux/wait.h>
9 #include <linux/poll.h>
10 #include <linux/miscdevice.h>
11 #include <linux/sched.h>
12 #include <linux/io.h>
13 #include "ioctl.h"
14
15 #define DEVNAME "leds"//具体要注册的杂项设备的设备名
16
17 #define GPM4BASE 0x110002e0 //leds的具体物理地址
18 #define GPM4CON 0x0
19 #define GPM4DAT 0x4
20
21 static void *gpm4base;//定义指向映射后对应虚拟地址的基地址
22
23 enum {ON, OFF};//0 1
24
25 void mill_ctl_led(int cmd, int num)//控制哪个灯?亮/灭?
26 {
27 u8 tmp;
28
29 tmp = ioread8(gpm4base+GPM4DAT);//读取GPM4DAT寄存器上对应虚拟地址的值
30 if(!cmd) {
31 tmp &= ~(1 << (num - 1));//0开 则 tmp 为0 ==》LED 对应硬件引脚低电平:亮
32 } else {
33 tmp |= 1 << (num - 1);//1关 则 tmp 为1 ==》LED 对应硬件引脚高电平:灭
34 }
35
36 iowrite8(tmp, gpm4base+GPM4DAT);//将tmp值重新写入(写8个字节到tmp) 指向寄存器对应的虚拟地址中==》对应驱动硬件设备的相应寄存器
37 }
38
39 int mill_ctl_stat (unsigned long arg)//控制灯的状态
40 {
41 char kbuf[4];//定义4个灯亮灭状态的数组
42 int i;//定义灯的个数
43 u8 tmp;
44
45 tmp = ioread8(gpm4base+GPM4DAT);//读取GPM4DAT寄存器对应虚拟地址上的低4位的值去判断。。。
46
47 for (i = 0; i < 4; i++) {
48 if (tmp & (1 << i)) { //遍历判断(对应寄存器低4位的)哪个灯亮灭?
49 kbuf[i] = 0;//哪个位为0则相应灯亮
50 } else {
51 kbuf[i] = 1;//
52 }
53 }
54
55 if (copy_to_user((void *)arg, kbuf, 4)) {// 把亮/灭状态值传递给应用层/(void *)表示指向char类型
56 return -EINVAL;
57 }
58
59 return 0;
60 }
61
62 static long
63 mill_unlocked_ioctl (struct file *filp, unsigned int request, unsigned long arg)
64 {
65 if (_IOC_TYPE(request) != LEDTYPE) { //解析类型type,判断是否类型为我们定义的l
66 return -EINVAL;//否则返回错误负值
67 }
68
69 switch (_IOC_NR(request)) {//解析ioctl命令的序号nr,通过switch语句判断对应的命令
70 default:
71 return -EINVAL;
72 case 1:
73 if (arg < 1 || arg > 4) {
74 return -EINVAL;
75 }
76
77 mill_ctl_led(ON, arg);//序号为1的执行
78 break;
79 case 2:
80 if (arg < 1 || arg > 4) {
81 return -EINVAL;
82 }
83 mill_ctl_led(OFF, arg);
84 break;
85 case 3:
86 if (mill_ctl_stat(arg) < 0) {
87 return -EINVAL;
88 }
89 break;
90 }
91
92 return 0;
93 }
94
95 static struct file_operations fops = {
96 .owner = THIS_MODULE,
97 .unlocked_ioctl = mill_unlocked_ioctl,
98 };
99
100
101 /*一个杂项设备对应如下一个结构体对象*/
102 static struct miscdevice misc = {
103 .minor = MISC_DYNAMIC_MINOR,//为自己动态申请次设备号
104 .name = DEVNAME,
105 .fops = &fops,
106 };
107
108 static int __init demo_init(void)
109 {
110 u32 tmp;
111
112 gpm4base = ioremap(GPM4BASE, SZ_8);//将物理地址映射为CPU核可见的对应虚拟地址/偏移0~4位,共5位;字节对齐,所以偏移8bits
113
114 /*set GPM4_0,GPM4_1,GPM4_2,GPM4_3 pin as output*/
115 tmp = ioread32(gpm4base+GPM4CON);//读取GPM4CON寄存器映射对应的虚拟地址上的值赋给tmp
116 tmp &= ~0xffff;
117 tmp |= 0x1111;
118 /*writel*/
119 iowrite32(tmp, gpm4base+GPM4CON);//回写将leds引脚设置为输出功能
120
121 tmp = ioread8(gpm4base+GPM4DAT);//读取GPM4DAT寄存器映射对应的虚拟地址上的值赋给tmp
122 tmp |= 0xf;
123 iowrite8(tmp, gpm4base+GPM4DAT);//回写给leds引脚输出高电平/默认关闭灯
124
125 return misc_register(&misc);//注册杂项设备
126 }
127
128 module_init(demo_init);
129
130 /* driver module exit */
131 static void __exit demo_exit(void)
132 {
133 u8 tmp;
134
135 tmp = ioread8(gpm4base+GPM4DAT);//同上,移除驱动时默认为关灯状态
136 tmp |= 0xf;
137 iowrite8(tmp, gpm4base+GPM4DAT);
138
139 iounmap(gpm4base);//注意解除映射关系
140
141 misc_deregister(&misc);//移除杂项设备
142 }
143 module_exit(demo_exit);
144
145 /* driver module description */
146 MODULE_LICENSE("GPL");
147
148 MODULE_AUTHOR("millet9527");
149 MODULE_VERSION("millet plus 18");
150 MODULE_DESCRIPTION("example for driver module arch");
//app
vim Makefile
1 CC=arm-linux-gcc
ledapp.c
1 #include <fcntl.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <sys/stat.h>
6 #include <assert.h>
7
8 #include "ioctl.h"
9
10 void usage(char *str)
11 {
12 printf("Usage:\n");
13 printf(" %s on/off 1/2/3/4\n", str);
14 printf(" %s state\n", str);
15
16 exit(1);
17 }
18
19 int main(int argc, char **argv)
20 {
21 int i;
22 int fd;
23 int ret;
24 int request, arg;
25 char buf[4] = { 0 };
26
27 if((argc !=2) && (argc != 3)) {
28 usage(argv[0]);
29 }
30
31 fd = open("/dev/leds", O_RDWR | O_NDELAY);
32 assert(fd > 0);
33
34 if (argc == 3) {
35 if (!strncmp("on", argv[1], 2)) {
36 request = LED_CTL_ON;
37 } else if (!strncmp("off", argv[1], 3)) {
38 request = LED_CTL_OFF;
39 } else {
40 usage(argv[0]);
41 }
42
43 arg = atoi(argv[2]);
44
45 if (arg < 1 || arg > 4) {
46 usage(argv[0]);
47 }
48
49 ret = ioctl(fd, request, arg);
50 assert(ret == 0);
51 } else if (argc == 2) {
52 if (!strncmp("state", argv[1], 5)) {
53 request = LED_CTL_STAT;
54 }
55 ret = ioctl(fd, request, buf);
56 assert(ret == 0);
57
58 for (i = 0; i < 4; i++) {
59 printf("led %d is %s!\n", i+1, \
60 buf[i]?"on":"off");
61 }
62 }
63
64
65 return 0;
66 }
ioctl.h
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_CTL_ON _IOW(LEDTYPE, 1, int)
9 #define LED_CTL_OFF _IOW(LEDTYPE, 2, int)
10 #define LED_CTL_STAT _IOR(LEDTYPE, 3, int)
11
12 #endif





cpu 为什么要使用虚拟地址空间与物理地址空间映射?
存储管理单元 MMU(Memory Manage Unit, 存储管理单元)和物理内存之间进行地址转换 在CPU和物理内存之间进行地址转换,
将地址从逻辑空间映映射到物理地址空间。
浙公网安备 33010602011771号