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和物理内存之间进行地址转换,
将地址从逻辑空间映映射到物理地址空间。
posted @ 2017-03-11 23:10  bkycrmn  阅读(245)  评论(0)    收藏  举报