代码示例_中断下半部

 

 


 

 

  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------------\n",__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------------\n",__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!\n",p->name);
122         button_dev->event.code = p->code;
123         button_dev->event.value = 0;
124     }else{
125         //按下
126         printk("kenel:%s pressed!\n",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------------\n",__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------------\n",__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!\n");
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------------\n",__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------------\n",__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!\n");
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------------\n",__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\n",__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------------\n",__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------------\n",__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------------\n",__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!\n");
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!\n");
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!\n");
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!\n");
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!\n");
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!\n");
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!\n");
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!\n");
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------------\n",__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\n",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("按下了上键!\n");
            else
                printf("松开了上键!\n");
            break;
            case KEY_DOWN:
            if(event.value)
                printf("按下了下键!\n");
            else
                printf("松开了下键!\n");
            break;
            case KEY_LEFT:
            if(event.value)
                printf("按下了左键!\n");
            else
                printf("松开了左键!\n");
            break;
            case KEY_RIGHT:
            if(event.value)
                printf("按下了右键!\n");
            else
                printf("松开了右键!\n");
            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

 

posted @ 2019-06-09 13:11  panda_w  阅读(385)  评论(0编辑  收藏  举报