1 //头文件
2 #include <linux/init.h>
3 #include <linux/module.h>
4 #include <linux/fs.h>
5 #include <linux/device.h>
6 #include <linux/slab.h>
7 #include <linux/gpio.h>
8 #include <linux/cdev.h>
9 #include <linux/interrupt.h>
10 #include <linux/input.h>
11 #include <linux/sched.h>
12 #include <linux/poll.h>
13 #include <linux/mm.h>
14
15
16 #include <asm/io.h>
17 #include <asm/page.h>
18 #include <asm/string.h>
19 #include <asm/uaccess.h>
20 #include <asm-generic/ioctl.h>
21
22 #define BUTTON_iOC_GET_DATA 0x4321
23 struct mem_data{
24 char buf[128];
25 };
26
27 //定义一个按键的数据包
28 struct button_event{
29 int code; //按键的名称---键值:KEY_DOWN
30 int value; //按键的状态---按下:1,松开:0
31 };
32
33 //设计一个描述按键的结构体类型
34 struct buttons{
35 char *name; //名称
36 unsigned int irqno; //中断号
37 int gpio; //按键对应的gpio口
38 int code; //键值
39 unsigned long flags; //触发方式
40 };
41
42 //定义一个数组来保存多个按键的数据
43 struct buttons buttons_set[] = {
44 [0] = {
45 .name = "key1_up",
46 .irqno = IRQ_EINT(0),
47 .gpio = S5PV210_GPH0(0),
48 .code = KEY_UP,
49 .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
50 },
51 [1] = {
52 .name = "key2_down",
53 .irqno = IRQ_EINT(1),
54 .gpio = S5PV210_GPH0(1),
55 .code = KEY_DOWN,
56 .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
57 },
58 [2] = {
59 .name = "key3_left",
60 .irqno = IRQ_EINT(2),
61 .gpio = S5PV210_GPH0(2),
62 .code = KEY_LEFT,
63 .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
64 },
65 [3] = {
66 .name = "key4_right",
67 .irqno = IRQ_EINT(3),
68 .gpio = S5PV210_GPH0(3),
69 .code = KEY_RIGHT,
70 .flags = IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
71 },
72 };
73
74 //面向对象编程----设计设备的类型
75 struct s5pv210_button{
76 //unsigned int major;
77 dev_t devno;
78 struct class * cls;
79 struct device * dev;
80 struct cdev *cdev;
81 unsigned int irqno;
82 struct button_event event;
83 wait_queue_head_t wq_head;
84
85 int have_data; //表示当前是否有数据可读,可读--1,不可读--0
86
87 void * virt_mem;
88 struct tasklet_struct tasklet;
89 };
90 struct s5pv210_button *button_dev;
91
92 //中断下半部的处理函数-----该函数会被内核线程执行
93 void button_irq_tasklet(unsigned long data)
94 {
95 printk("--------^_^ %s------------
",__FUNCTION__);
96
97 //此时有数据可读
98 button_dev->have_data = 1;
99
100 //从等待队列中唤醒阻塞的进程
101 wake_up_interruptible(&button_dev->wq_head);
102
103 }
104
105
106 //实现中断处理函数--------当触发中断时会被执行
107 irqreturn_t button_irq_svc(int irqno, void *dev)
108 {
109 int value;
110 struct buttons *p;
111 printk("--------^_^ %s------------
",__FUNCTION__);
112
113 //获取当前触发中断的按键信息
114 p = (struct buttons *)dev;
115
116 //获取产生中断的gpio口的值
117 value = gpio_get_value(p->gpio);
118 //判断是按下还是松开
119 if(value){
120 //松开
121 printk("kernel:%s up!
",p->name);
122 button_dev->event.code = p->code;
123 button_dev->event.value = 0;
124 }else{
125 //按下
126 printk("kenel:%s pressed!
",p->name);
127 button_dev->event.code = p->code;
128 button_dev->event.value = 1;
129 }
130
131 //将tasklet对象加入到内核线程中
132 tasklet_schedule(&button_dev->tasklet);
133
134 return IRQ_HANDLED;
135 }
136
137 //实现设备操作接口
138 int button_open(struct inode *inode, struct file *filp)
139 {
140
141 printk("--------^_^ %s------------
",__FUNCTION__);
142
143 return 0;
144 }
145 ssize_t button_read(struct file *filp , char __user *buf , size_t size, loff_t *flags)
146 {
147 int ret;
148 printk("--------^_^ %s------------
",__FUNCTION__);
149 //判读open时,有没有设置flags为NONBLOCK
150 if(filp->f_flags & O_NONBLOCK && !button_dev->have_data)
151 return -EAGAIN;
152
153 //判断此时是否有数据可读
154 wait_event_interruptible(button_dev->wq_head,button_dev->have_data);
155
156 //将内核数据转换为用户空间数据
157 ret = copy_to_user(buf,&button_dev->event,size);
158 if(ret > 0){
159 printk("copy_to_user error!
");
160 return -EFAULT;
161 }
162
163 //将数据返回给应用空间后,清空数据包,同时将hava_data置零
164 memset(&button_dev->event,0,sizeof(button_dev->event));
165 button_dev->have_data = 0;
166 return size;
167 }
168
169 ssize_t button_write(struct file *filp, const char __user *buf, size_t size, loff_t *flags)
170 {
171
172 printk("--------^_^ %s------------
",__FUNCTION__);
173
174 return size;
175 }
176
177 long button_ioctl(struct file *filp, unsigned int cmd , unsigned long args)
178 {
179 void __user *argp;
180 struct mem_data data;
181 int ret;
182 printk("--------^_^ %s------------
",__FUNCTION__);
183 argp = (void __user *)args;
184
185 switch(cmd){
186 case BUTTON_iOC_GET_DATA:
187 memset(data.buf,0,sizeof(data.buf));
188 memcpy(data.buf, button_dev->virt_mem,sizeof(data.buf));
189 ret = copy_to_user(argp,&data,sizeof(data));
190 if(ret > 0){
191 return -EFAULT;
192 }
193 break;
194 default:
195 printk("unkown cmd!
");
196 }
197
198 return 0;
199 }
200
201 int button_mmap(struct file *filp, struct vm_area_struct *vma)
202 {
203 unsigned int addr;
204 printk("--------^_^ %s------------
",__FUNCTION__);
205
206 //1,获得一块物理内存空间-----将申请的虚拟空间转换为对应的物理空间
207 addr = virt_to_phys(button_dev->virt_mem);
208
209 //2,将物理内存映射到虚拟空间---应用空间
210
211 vma->vm_flags |= VM_IO;
212 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
213
214 if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
215 PAGE_SIZE, vma->vm_page_prot)) {
216 printk(KERN_ERR "%s: io_remap_pfn_range failed
",__func__);
217 return -EAGAIN;
218 }
219
220 return 0;
221 }
222
223 unsigned int button_poll(struct file *filp, struct poll_table_struct *pts)
224 {
225 unsigned int mask = 0;
226 printk("--------^_^ %s------------
",__FUNCTION__);
227
228 //1,将等待队列头注册到系统中(VFS)
229 poll_wait(filp,&button_dev->wq_head,pts);
230
231 //2,如果产生按键中断-有数据可读,此时返回POLLIN,如果没有数据返回0
232 if(button_dev->have_data)
233 mask |= POLLIN;
234
235 return mask;
236
237 }
238
239 int button_close(struct inode *inode, struct file *filp)
240 {
241 printk("--------^_^ %s------------
",__FUNCTION__);
242
243 return 0;
244 }
245
246
247 static struct file_operations fops = {
248 .open = button_open,
249 .read = button_read,
250 .write = button_write,
251 .poll = button_poll,
252 .mmap = button_mmap,
253 .unlocked_ioctl = button_ioctl,
254 .release = button_close,
255 };
256
257
258 //加载函数和卸载函数
259 static int __init button_init(void) //加载函数-----在驱动被加载时执行
260 {
261 int ret,i;
262 printk("--------^_^ %s------------
",__FUNCTION__);
263 //0,实例化设备对象
264 //参数1 ---- 要申请的空间的大小
265 //参数2 ---- 申请的空间的标识
266 button_dev = kzalloc(sizeof(struct s5pv210_button),GFP_KERNEL);
267 if(IS_ERR(button_dev)){
268 printk("kzalloc error!
");
269 ret = PTR_ERR(button_dev);
270 return -ENOMEM;
271 }
272
273 //1,申请设备号-----新方法
274 #if 0
275 //静态申请设备号
276 button_dev->major = 256;
277 ret = register_chrdev_region(MKDEV(button_dev->major,0),1,"button_drv");
278 if(ret < 0){
279 printk("register_chrdev_region error!
");
280 ret = -EINVAL;
281 goto err_kfree;
282 }
283 #else
284 //动态申请设备号
285 ret = alloc_chrdev_region(&button_dev->devno,0,1,"button_drv");
286 if(ret < 0){
287 printk("register_chrdev_region error!
");
288 ret = -EINVAL;
289 goto err_kfree;
290 }
291 #endif
292
293 //创建cdev
294
295 //申请cdev的空间
296 button_dev->cdev = cdev_alloc();
297 if(IS_ERR(button_dev->cdev)){
298 printk("button_dev->cdev error!
");
299 ret = PTR_ERR(button_dev->cdev);
300 goto err_unregister;
301 }
302
303 //初始化cdev的成员
304 cdev_init(button_dev->cdev,&fops);
305
306 //将cdev加入到内核中----链表
307 ret = cdev_add(button_dev->cdev,button_dev->devno,1);
308
309
310
311 //2,创建设备文件-----/dev/button
312 button_dev->cls = class_create(THIS_MODULE,"button_cls");
313 if(IS_ERR(button_dev->cls)){
314 printk("class_create error!
");
315 ret = PTR_ERR(button_dev->cls);
316 goto err_cdev_del;
317 }
318
319 button_dev->dev = device_create(button_dev->cls,NULL,button_dev->devno,NULL,"button");
320 if(IS_ERR(button_dev->dev)){
321 printk("device_create error!
");
322 ret = PTR_ERR(button_dev->dev);
323 goto err_class;
324 }
325
326
327 //3,硬件初始化---申请中断
328 for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
329 ret = request_irq(buttons_set[i].irqno,button_irq_svc,buttons_set[i].flags,buttons_set[i].name,&buttons_set[i]);
330 if(ret != 0){
331 printk("request_irq error!
");
332 ret = -EBUSY;
333 goto err_device;
334 }
335 }
336 //初始化等待队列头
337 init_waitqueue_head(&button_dev->wq_head);
338
339 //获取一块虚拟的内存空间---内核中
340 button_dev->virt_mem = kzalloc(PAGE_SIZE,GFP_KERNEL);
341 if(IS_ERR(button_dev->virt_mem)){
342 printk("kzalloc error!
");
343 ret = -EBUSY;
344 goto err_free_irq;
345 }
346
347 //中断下半部---初始化: struct tasklet_struct
348 tasklet_init(&button_dev->tasklet,button_irq_tasklet,123);
349
350 return 0;
351 err_free_irq:
352 for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
353 free_irq(buttons_set[i].irqno,&buttons_set[i]);
354 }
355 err_device:
356 device_destroy(button_dev->cls,button_dev->devno);
357 err_class:
358 class_destroy(button_dev->cls);
359
360 err_cdev_del:
361 cdev_del(button_dev->cdev);
362
363 err_unregister:
364 unregister_chrdev_region(button_dev->devno,1);
365
366 err_kfree:
367 kfree(button_dev);
368 return ret;
369
370
371 }
372
373 static void __exit button_exit(void) //卸载函数-----在驱动被卸载时执行
374 {
375 int i;
376 printk("--------^_^ %s------------
",__FUNCTION__);
377 kfree(button_dev->virt_mem);
378 for(i = 0; i < ARRAY_SIZE(buttons_set);i++){
379 free_irq(buttons_set[i].irqno,&buttons_set[i]);
380 }
381 device_destroy(button_dev->cls,button_dev->devno);
382 class_destroy(button_dev->cls);
383 cdev_del(button_dev->cdev);
384 unregister_chrdev_region(button_dev->devno,1);
385 kfree(button_dev);
386 }
387
388 //声明和认证
389 module_init(button_init);
390 module_exit(button_exit);
391 MODULE_LICENSE("GPL");
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <sys/mman.h>
#include <linux/input.h>
//定义一个按键的数据包
struct button_event{
int code; //按键的名称---键值:KEY_DOWN
int value; //按键的状态---按下:1,松开:0
};
#define BUTTON_iOC_GET_DATA 0x4321
#define PAGE_SIZE 1UL<<12
struct mem_data{
char buf[128];
};
int main(void)
{
int fd;
int ret;
struct button_event event;
struct pollfd pfds[2];
char buf[128];
char *str = "hello kernel";
struct mem_data data;
fd = open("/dev/button",O_RDWR);
if(fd < 0){
perror("open");
exit(1);
}
//测试mmap的功能
char *addr = mmap(NULL,PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(addr == NULL){
perror("mmap");
exit(1);
}
//向映射的物理空间中写数据
strcpy(addr,str);
sleep(1);
//验证数据是否写入到映射的物理空间----通过ioctl读取数据
ret = ioctl(fd,BUTTON_iOC_GET_DATA,&data);
if(ret < 0){
perror("ioctl");
exit(1);
}
printf("data.buf = %s
",data.buf); //将读到的数据打印出来
sleep(1);
pfds[0].fd = 0; //标准输入文件描述符
pfds[0].events = POLLIN; //是否可读
pfds[1].fd = fd; //开发板中的键盘
pfds[1].events = POLLIN; //按键是否触发中断
while(1){
ret = poll(pfds,2,-1);
if(ret < 0){
perror("poll");
exit(1);
}
if(ret > 0){
//标准输入可读
if(pfds[0].revents & POLLIN){
fgets(buf,sizeof(buf),stdin);
printf("%s",buf);
}
//开发板中的按键触发了中断
if(pfds[1].revents & POLLIN){
bzero(&event,sizeof(event));
ret = read(fd,&event,sizeof(event));
if(ret < 0){
perror("read");
exit(1);
}
switch(event.code){
case KEY_UP:
if(event.value)
printf("按下了上键!
");
else
printf("松开了上键!
");
break;
case KEY_DOWN:
if(event.value)
printf("按下了下键!
");
else
printf("松开了下键!
");
break;
case KEY_LEFT:
if(event.value)
printf("按下了左键!
");
else
printf("松开了左键!
");
break;
case KEY_RIGHT:
if(event.value)
printf("按下了右键!
");
else
printf("松开了右键!
");
break;
}
}
}
}
close(fd);
return 0;
}
1 #指定内核源码路径
2 KERNEL_DIR = /home/farsight/s5pv210/kernel/linux-3.0.8
3 CUR_DIR = $(shell pwd)
4 MYAPP = test
5
6 all:
7 #让make进入内核源码编译,同时将当前目录中的c程序作为内核模块一起编译
8 make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
9 arm-none-linux-gnueabi-gcc -o $(MYAPP) $(MYAPP).c
10
11 clean:
12 #删除上面编译生成的文件
13 make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
14 rm -rf $(MYAPP)
15
16 install:
17 cp *.ko $(MYAPP) /opt/rootfs/drv_module
18
19 #指定当前目录下哪个文件作为内核模块编
20 obj-m = button_drv.o