71.linux驱动--中断(中)

上一节我们只是能够正常的触发中断,但是程序并不知道我们是按下还是抬起,并且作为字符驱动也没有最基本的open read等函数,应用层是无法操作我们的按键驱动的,那么我们在上节的基础上进行完善。

一. 实现字符设备驱动的框架

      1,设定一个全局的设备对象(struct key_desc)

       key_dev = kzalloc(sizeof(struct key_desc),  GFP_KERNEL);

      2,申请主设备号

       key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);

       3,创建设备节点文件

       key_dev->cls = class_create(THIS_MODULE, "key_cls");

       key_dev->dev = device_create(key_dev->cls, NULL,  MKDEV(key_dev->dev_major,0), NULL, "key0");

       4.硬件的初始化

二. 驱动中将硬件所产生的数据传递给用户

       a,硬件如何获取数据

              key: 按下和抬起: 1/0

              读取key对应的gpio的状态,可以判断按下还是抬起

 

              读取key对应gpio的寄存器--数据寄存器

              int value = readl(key_dev->reg_base + 4) & (1<<2);

       b,驱动如何传递给用户

              在中断处理中填充数据:

                     key_dev->event.code = KEY_ENTER;

                     key_dev->event.value = 0;

 

              在xxx_read中数据传递给用户

                     ret = copy_to_user(buf, &key_dev->event,  count);

 

       c,用户如何拿到--编写应用程序       

  while(1)
 {
     read(fd, &event, sizeof(struct key_event));
     if(event.code == KEY_ENTER)
     {
        if(event.value)
        {

           printf("APP__ key enter pressed\n");

         }else{

           printf("APP__ key enter up\n");
        }
     }
  }

三.程序编写

驱动

#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/device.h>

#include <asm/io.h>
#include <asm/uaccess.h>

#define GPXCON_REG  0x11000C20
#define KEY_ENTER		28

// 设计一个描述按键的数据的对象
struct key_event{
	int code; // 表示按键的类型
	int value; // 表示按下还是抬起 1 / 0
};

//设计一个全局设备对象--描述按键信息
struct key_desc{
	unsigned int dev_major;
	struct class *cls;
	struct device *dev;
	int irqno;
	void *reg_base;
	struct key_event event;
};
struct key_desc *key_dev;

int get_irqno_from_node(void)
{
	// 获取到设备树中到节点
	struct device_node *np = of_find_node_by_path("/key_int_node");
	if(np){
		printk("find node ok\n");
	}else{
		printk("find node failed\n");
	}

	// 通过节点去获取到中断号码
	int irqno = irq_of_parse_and_map(np, 0);
	printk("irqno = %d\n", irqno);
	
	return irqno;
}
irqreturn_t key_irq_handler(int irqno, void *devid)
{
	printk("-------%s-------------\n", __FUNCTION__);
	
	//读取数据寄存器
	int value = readl(key_dev->reg_base + 4) & (1<<2);

	if(value){ // 抬起
		printk("key3 up\n");
		key_dev->event.code = KEY_ENTER;
		key_dev->event.value = 0;

	}else{//按下
		printk("key3 pressed\n");
		key_dev->event.code = KEY_ENTER;
		key_dev->event.value = 1;
	}
	
	return IRQ_HANDLED;
}
int key_drv_open(struct inode *inode, struct file *filp)
{

	printk("-------%s-------------\n", __FUNCTION__);
	return 0;
}

ssize_t key_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *fpos)
{
	//printk("-------%s-------------\n", __FUNCTION__);
	int ret;

	ret = copy_to_user(buf, &key_dev->event,  count);
	if(ret > 0)
	{
		printk("copy_to_user error\n");
		return -EFAULT;
	}

	// 传递给用户数据之后,将数据清除掉
	memset(&key_dev->event, 0,  sizeof(key_dev->event));
	
	return count;
	
}
ssize_t key_drv_write(struct file *filp, const char __user *buf, size_t count, loff_t *fpos)
{
	printk("-------%s-------------\n", __FUNCTION__);
	return 0;

}

int key_drv_close (struct inode *inode, struct file *filp)
{
	printk("-------%s-------------\n", __FUNCTION__);
	return 0;

}

const struct file_operations key_fops = {
	.open = key_drv_open,
	.read = key_drv_read,
	.write = key_drv_write,
	.release = key_drv_close,

};
static int __init key_drv_init(void)
{
	int ret;

	// 1,设定一个全局的设备对象
	key_dev = kzalloc(sizeof(struct key_desc),  GFP_KERNEL);
	
	// 2,申请主设备号
	key_dev->dev_major = register_chrdev(0, "key_drv", &key_fops);

	// 3,创建设备节点文件
	key_dev->cls = class_create(THIS_MODULE, "key_cls");
	key_dev->dev = device_create(key_dev->cls, NULL, 
									MKDEV(key_dev->dev_major,0), NULL, "key0");


	// 4,硬件的初始化--地址映射或者中断申请
	key_dev->irqno = get_irqno_from_node();

	ret = request_irq(key_dev->irqno, key_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, 
					"key3_eint10", NULL);
	if(ret != 0)
	{
		printk("request_irq error\n");
		return ret;
	}

	// a,硬件如何获取数据--gpx1
	key_dev->reg_base  = ioremap(GPXCON_REG, 8);

	return 0;
}

static void __exit key_drv_exit(void)
{
	iounmap(key_dev->reg_base);
	free_irq(key_dev->irqno, NULL);
	device_destroy(key_dev->cls, MKDEV(key_dev->dev_major,0));
	class_destroy(key_dev->cls);
	unregister_chrdev(key_dev->dev_major, "key_drv");
	kfree(key_dev);
}

module_init(key_drv_init);
module_exit(key_drv_exit);
MODULE_LICENSE("GPL");

应用程序

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>


struct key_event{
	int code; // 表示按键的类型:  home, esc, Q,W,E,R,T, ENTER
	int value; // 表示按下还是抬起 1 / 0
};

#define KEY_ENTER		28

int main(int argc, char *argv[])
{
	struct key_event event;

	int fd = open("/dev/key0", O_RDWR);
	if(fd < 0)
	{
		perror("open");
		exit(1);
	}

	while(1)
	{
		read(fd, &event, sizeof(struct key_event));

		if(event.code == KEY_ENTER)
		{
			if(event.value)
			{
				printf("APP__ key enter pressed\n");
			}else{
				printf("APP__ key enter up\n");
			}
		}
	}

	close(fd);
	return 0;

}


进行编译,然后在终端下,装载驱动insmod key_dev.ko,并执行应用程序./key_test,按下按键,这里功能已经实现了

但是有个问题,通过ps,然后top命令查看一下资源占用情况,发现key的这个程序的资源占用非常的高,程序一直在read,当然占用高。

很容易想到,没数据了就休眠去把,就别老费劲的循环读状态了。

 

之后再对程序进行优化。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值