关于linux中断驱动的学习


前言

记录linux内核与驱动相关学习内容


一、linux驱动编写

        首先将相应gpio引脚号转为中断号,并调用request_irq函数来申请一个中断。

	//获得中断号
	button_device->irqno = gpio_to_irq(button_device->gpiono);
	printk("<kernel> irqno=%d\n", button_device->irqno);

	//申请中断
	ret = request_irq(button_device->irqno, button_handler, 
	IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, "xyd_pwr_key_int", NULL);
	if (ret) {
		printk("request_irq fail\n");
		ret = -EINVAL;
		goto err_request_irq;
	}

        关于所用到的变量对其进行封装,并初始化一个结构体对象实例(static的作用是让外部不可见,防止污染内核)。

struct rk3399_button {
	unsigned int major ; //主设备号
	dev_t devno; //设备号
	struct class *cls;
	struct device *dev;
	int gpiono;
	int irqno;
	struct cdev *cdev;
	struct button_event event; //按键的事件对象
	wait_queue_head_t wq_head; //等待队列头对象
	int have_data; // 表示设备是否有数据, 1--有  0--无
	struct tasklet_struct tasklet; //下半部对象
};

static struct rk3399_button  *button_device; //声明一个对象

        接下来就是中断服务函数的编写,本代码是以板子上的gpio0_A5引脚为例(是一个按键)

irqreturn_t button_handler(int irq, void *dev)
{
	

	int value;
	printk("call %s  name=%s pid=%d\n",__func__, current->comm, current->pid);
	//设置event对象
	button_device->event.code = KEY_A;
	value = gpio_get_value(button_device->gpiono);
	if (value) 
		button_device->event.value = 0;
	else
		button_device->event.value = 1;
	
	//调度下半部
	tasklet_schedule(&button_device->tasklet);

	
	return IRQ_HANDLED;

}

             其中current是一个全局结构体变量,我打印了其中的一些成员以便观察对比。当然还有read驱动的编写如下。这个驱动的目的在于将中断发生时内核空间发生改变的结构体event通过copy_to_user传入用户空间,那边定义好相应结构体接收即可,然后判断并打印相应信息。

static ssize_t button_read (struct file *filp, char __user *buf, size_t size, loff_t *flag)
{
	int ret;
	printk("call %s()  name=%s pid=%d\n", __func__, current->comm, current->pid);
	//判断open的打开方式是否非阻塞,是否有数据可读
	if (filp->f_flags & O_NONBLOCK && !button_device->have_data)
		return -EAGAIN;

	//2)判断是否有数据可读,不可读阻塞调用进程, 这个点调用进程休眠,到等待队列休眠
	wait_event_interruptible(button_device->wq_head, button_device->have_data);
		
	//拷贝event到用户空间
	
	ret = copy_to_user(buf, &button_device->event, size);
	if (ret > 0) {
		printk("copy_to_user fail\n");
		return -EFAULT;
	}
	memset(&button_device->event, 0, sizeof(button_device->event));
	button_device->have_data = 0; //读了数据就没有了

	printk("call %s()\n", __func__); //内核用的printf
	return size;
	
}

        如下为应用层代码与内核中驱动层相匹配使用。

ret = read(fd, &event, sizeof(event));//阻塞
			printf("read ret=%d\n", ret);
			if (ret < 0) {
				perror("read");
				printf("errno=%d\n", errno);
			}	
			if (event.code == KEY_A) {
				if (event.value) {
					printf("power key pressed\n");
				} else {
					printf("power key release\n");
				}
			}

   

        装载完驱动后(目前用手动装载的方式),当按下按键,结果如下

        

二、各函数使用方法说明

函数传入参数说明

int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char*name, void *dev_id);

 

参数说明:

  • irq:要请求的硬件中断号。
  • handler:当中断发生时调用的中断处理函数。
  • flags:指定中断处理的一些属性,如 IRQF_SHARED 表示中断可以被共享,IRQF_DISABLED 表示中断处理函数禁用本地中断等。
  • name:设备名称,通常用于 /proc/interrupts 文件中的描述。
  • dev_id:一个指向设备数据的指针,当中断处理程序被调用时,这个指针会被传递给它。

request_irq 函数会尝试注册一个中断处理程序。如果成功,该函数返回 0;如果失败,它返回一个错误代码。

一旦中断处理程序被注册,当与 irq 参数指定的中断号相对应的硬件中断发生时,内核就会调用相应的处理程序

static ssize_t button_read (struct file *filp, char __user *buf, size_t size, loff_t *flag)
  • struct file *filp: 这是一个指向file结构的指针,它代表了打开的文件。通过这个结构,你可以访问到与文件相关联的各种信息,比如文件的偏移量、文件的所有者等。

  • char __user *buf: 这是一个指向用户空间缓冲区的指针,用于存储从设备读取的数据。在内核空间与用户空间之间进行数据传输时,需要使用特殊的指针类型(如__user)来确保内核不会错误地访问用户空间的内存。

  • size_t size: 这是一个表示用户缓冲区大小的变量。它告诉内核用户为读取操作分配了多少空间。

  • loff_t *flag: 这是一个指向文件偏移量的指针。读取操作可能会改变文件的当前读取位置,这个偏移量会更新以反映新的位置

irqreturn_t button_handler(int irq, void *dev)
  • int irq: 这是中断号,它唯一标识了发生中断的硬件设备。

  • void *dev: 这是一个指向设备特定数据的指针。当注册中断处理程序时,你可以传递一个指针给 request_irq 函数,这个指针会在中断处理程序被调用时传递给它。这通常用于让中断处理程序能够访问设备的私有数据。另外补充struct file_operations fops = { .owner = THIS_MODULE, //该对象和当前模块对象绑定,避免卸载模块 .open = button_open, .release = button_close, .write = button_write, .unlocked_ioctl = button_ioctl, .read = button_read, .poll = button_poll, };

  • 另外补充注册设备号以及file_operations成员的初始化(需要用到的初始化即可)
  • struct file_operations fops = {
    	.owner = THIS_MODULE, //该对象和当前模块对象绑定,避免卸载模块
    	.open = button_open,
    	.release = button_close,
    	.write = button_write,
    	.unlocked_ioctl = button_ioctl,
    	.read = button_read,
    	.poll = button_poll,
    	
    };
    
    register_chrdev(0, "button_drv", &fops);


总结

        留此文以便日后开发时重拾

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值