驱动之字符设备按键中断

写驱动流程:

1.原理图-à输出高电平 led亮

2.对应核心板的GPIO口

3.查看寄存器地址

4.了解管脚的功能

5.开始写驱动

①许可证

②加载函数

  1. 申请设备号

 MKDEV

Register_chrdev_region

Alloc_chrdev_region

  1. 注册设备

Cdev

File_oparetions---àopen/release

Cdev_init

Cdev_add

  1. 寄存器映射

Ioremap(PA,size)

  1. 配置寄存器(读改写的方式

Readll()

Write()

 ③卸载函数

  1. 取消映射
  2. 注销设备
  3. 释放

 

传参时: 将字符串转换成整数;

  按键 驱动:

1.原理图

2.外部中断,下降沿触发

Request_irq的第3个参数设置

3.GPH0  

开始写驱动:

  1. 许可证
  2. 加载模块

①     申请设备号

②     注册设备

③     IO的初始化为外部中断模式

  1. 卸载

①     注销设备

②     释放设备号

 

open函数中:注册中断   request_irq(irq, )

release 函数:释放中断  free_irq(unsigned  int  irq,  void  *dev_id);

中断处理函数(中断号, , ,)。。。。。。(第一步,打印键值----à将键值保存在一个变量中,传递给驱动???如何通过缓存,将快速的按键都保存下来)

 

 

按键驱动:(呵呵,虽然挺简单的,但是初学的我,还是搞了一天半,查了不少书。。。。。)

①     设备驱动模块的加载:(MKDEV生成主设备号、注册设备号、ioremap虚拟地址的映射、读改写、)

 1 /*设备驱动模块的加载*/
2 static int __init my_key_init(void)
3 {
4 int error;
5 dev = MKDEV(key_major,key_minor);
6 #if 1
7 if (key_major)
8 {
9 register_chrdev_region(dev,1,"mykey");
10 }
11 else
12 {
13 alloc_chrdev_region(dev,0,1,"my_key");
14 }
15 #endif
16 error = register_chrdev_region(dev,1,"mykey");
17 if (error < 0)
18 {
19 printk(KERN_WARNING "can't get major %d\n",key_major);
20 return error;
21 }
22 char_reg_setup_cdev();
23 gph0con_va = ioremap(GPH0CON,4);
24 if (gph0con_va == NULL)
25 {
26 printk(KERN_WARNING "ioremap error......\n");
27 return -ENOMEM;
28 }
29 writel((readl(gph0con_va)& ~0xff0ffff0) | 0x22022220, gph0con_va); //GPH0CON外部中断模式
30 printk(KERN_INFO "mykey driver is ok.....\n");
31 return 0;
32
33 }
34
35


②     设备驱动的卸载模块:(包括取消映射、注销设备、释放申请的设备号)
View Code
1 static void __exit my_key_exit(void)
2 {
3 iounmap(gph0con_va);
4 cdev_del(&cdev);//注销设备
5 unregister_chrdev_region(dev,1);//释放之前申请的设备号
6 printk(KERN_INFO "myket clean up\n");

7 return ;
8 }

 

③     设备注册 (cdev_init建立cdev和file_operations之间的联系、cdev_add向系统添加一个设备)

 
 1 /*设备的注册*/
2 static void char_reg_setup_cdev(void)
3 {
4 int error;
5 cdev_init(&cdev,&key_fops);//设备初始化,建立cdev和file_operations之间的联系
6      error = cdev_add(&cdev,dev,1);//向系统添加一个设备
7 if (error)
8 {
9 printk(KERN_NOTICE "char_reg_setup_cdev errro.,....\n");
10 }
11 return ;
12 }

④     file_operations结构体,成员函数实现字符设备和内核的接口

1 struct file_operations key_fops = {
2 .owner = THIS_MODULE,
3 .open = my_key_open,
4 .release = my_key_release,
5 };
6
7


 ⑤     open函数中:注册中断   request_irq(irq,hander,irqflags,name, dev_id )其中添加了一种驱动程序的出错处理。。。goto 函数实现
 1 static int my_key_open(struct inode *inodep, struct file *filep)
2 {
3 if (request_irq(IRQ_EINT(1),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key1",NULL)) //红色的flags为下降沿触发中断
4 {
5 printk("key 1 request_irq error...\n");
6 goto key1;
7 }
8 if (request_irq(IRQ_EINT(2),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key2",NULL))
9 {
10 printk("key 2 request_irq error...\n");
11 goto key2;
12 }
13 printk(KERN_INFO "device opend success..\n");
14 return 0;
15
16 key2: free_irq(IRQ_EINT(1),NULL);
17 key1: return 0;
18
19 }

 

⑥     release函数:(释放中断)

View Code
1 static int my_key_release(struct inode *inodep,struct file *filep)
2 {
3 free_irq(IRQ_EINT(2),NULL);
4 free_irq(IRQ_EINT(1),NULL);
5 printk(KERN_INFO "devices closed.....\n");
6 return 0;
7 }


 

⑦     最后的中断处理函数(irqreturn_t 可能有两种返回值,IRQ_NONEIRQ_HANDLED;当中断处理函数程序检测到一个中断时,但该中断对应的设备并不是在注册处理函数期间指定的产生源时,返回IRQ_NONE;当中断处理程序被正确调用,且确实是他所对应设备产生了中断时,返回IRQ_HANDLED;)

 1 irqreturn_t hander(unsigned int irq, void *dev_id)
2 {
3 switch (irq)
4 {
5 case IRQ_EINT(1): printk(KERN_INFO "key 1 is pressed......\n");
6 break;
7 case IRQ_EINT(2): printk(KERN_INFO "key 2 is pressed......\n");
8 break;
9 default :
10 break;
11 }
12 return IRQ_HANDLED;//当中断处理程序被正确调用,且确实是他所对应设备产生了中断时,返回IRQ_HANDLED
13 }
14
15


 

驱动程序的出错处理  goto

if  (a)

         goto  a;

if   (b)

         goto  b;

if   (c)

         goto  c;

 

c:   free_irq(b);

b:   free_irq(a);

a:   return 0;

 

首先注册一个中断处理程序:request_irq(irq , hander, irqflags,devname, dev_id );

有注册就有释放: free_irq(irqno, dev_id);

中断处理程序:static  irqreturn_t   hander (int  irqno,void *  dev_id, struct  pt_regs *regs);

 注意:函数的返回值是一个特殊的类型,irqreturn_t 可能有两种返回值,IRQ_NONE和IRQ_HANDLED;当中断处理函数程序检测到一个中断时,但该中断对应的设备并不是在注册处理函数期间指定的产生源时,返回IRQ_NONE;当中断处理程序被正确调用,且确实是他所对应设备产生了中断时,返回IRQ_HANDLED;利用这两个返回值,内核可以知道设备发出的是否是一种虚假的(为请求)的中断;

 

 

static :

          中断处理程序通常会标记位static,因为它从来都不会被别的文件中的代码直接调用。另外,中断处理程序是无需重入的,当一个给定的中断处理程序正在执行时,相应的中断线在所有的处理器上都会被屏蔽掉,以防止在同一个中断上接收另外一个新的中断处理。

 

 驱动程序:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <asm/io.h>
#include <mach/irqs.h>
#include <linux/cdev.h>

#define GPH0CON 0xE0300100

MODULE_LICENSE ("GPL");

int key_major = 250;
int key_minor = 0;
dev_t dev = 0;
static struct cdev cdev;
unsigned long *gph0con_va;

irqreturn_t hander(unsigned int irq, void *dev_id)
{
	switch (irq)
	{
		case IRQ_EINT(1): printk(KERN_INFO "key 1 is pressed......\n");
				break;
		case IRQ_EINT(2): printk(KERN_INFO "key 2 is pressed......\n");
				break;
		default :
				break;
	}
	return IRQ_HANDLED;
}

static int my_key_open(struct inode *inodep, struct file *filep)
{
	if (request_irq(IRQ_EINT(1),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key1",NULL))
	{
		printk("key 1 request_irq error...\n");
		goto key1;
	}
	if (request_irq(IRQ_EINT(2),hander,IRQF_DISABLED | IRQF_TRIGGER_FALLING,"key2",NULL))
	{
		printk("key 2 request_irq error...\n");
		goto key2;
	}
	printk(KERN_INFO "device opend success..\n");
	return 0;

key2: free_irq(IRQ_EINT(1),NULL);
key1: return 0;

}

static int my_key_release(struct inode *inodep,struct file *filep)
{
	free_irq(IRQ_EINT(2),NULL);
	free_irq(IRQ_EINT(1),NULL);

	printk(KERN_INFO "devices closed.....\n");
	return 0;
}

struct file_operations key_fops = {
	.owner = THIS_MODULE,
	.open  = my_key_open,
	.release = my_key_release,
};
static void char_reg_setup_cdev(void)
{
	int error;
	cdev_init(&cdev,&key_fops);
	error = cdev_add(&cdev,dev,1);
	if (error)
	{
		printk(KERN_NOTICE "char_reg_setup_cdev errro.,....\n");
	}
	return ;
}
static int __init my_key_init(void)
{
	int error;
	dev = MKDEV(key_major,key_minor);
#if 1
	if (key_major)
	{
		register_chrdev_region(dev,1,"mykey");
	}
	else
	{
		alloc_chrdev_region(dev,0,1,"my_key");
	}
#endif 
	error = register_chrdev_region(dev,1,"mykey");
	if (error < 0)
	{
		printk(KERN_WARNING "can't get major %d\n",key_major);
		return error;
	}
	char_reg_setup_cdev();
	gph0con_va = ioremap(GPH0CON,4);
	if (gph0con_va == NULL)
	{
		printk(KERN_WARNING "ioremap error......\n");
		return -ENOMEM;
	}
	writel((readl(gph0con_va)& ~0xff0ffff0) | 0x22022220, gph0con_va);
	printk(KERN_INFO "mykey driver is ok.....\n");
	return 0;
	
}
static void __exit my_key_exit(void)
{
	iounmap(gph0con_va);
	cdev_del(&cdev);
	unregister_chrdev_region(dev,1);
	printk(KERN_INFO "myket clean up\n");
	return ;
}
module_init(my_key_init);
module_exit(my_key_exit);

 测试程序:

 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
	int fd;
	int ret;
//	int key_num;

	fd = open("/dev/my_key",O_RDWR);
	if (fd < 0)
	{
		printf("open error\n");
		return -1;
	}
/*
	while (1)
	{
		ret = read(fd,&key_num,sizeof(int));
		printf("you press the key of %d\n",key_num);
	}
	*/
	while (1);
	close(fd);
	printf("devices close...\n");
	return 0;
}

 

 Makefile文件:

 

$(warning KERNELRELEASE=$(KERNELRELEASE))

ifeq ($(KERNELRELEASE),)

#KERNELDIR ?= /lib/modules/$(shell uname -r)/build
KERNELDIR ?= /home/linux/linux-2.6.35/  #该目录是你的内核的目录
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install


clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions Module* module

.PHONY:modules modules_install clean

else
#	obj-m := hello.o
	obj-m += my_key.o
#	obj-m := char_reg.o
endif

 

原文地址:https://www.cnblogs.com/zhou2011/p/2379073.html