一、代码实现
1、原理图分析
通过原理图分析可以得到,当按键按下后 KEY0 为低电平,当按键释放后 KEY0 为高电平。通过原理图可以确定 KEY0 连接在 UART1_CTS 引脚上。UART1_CTS 引脚的 IO 编号为 GPIO1_IO18。
2、修改设备树
1、修改 SOC 级中断属性
根据《IMX6ULL参考手册》中 Chapter 3:Interrupts and DMA Events描述,具体描述如下:
在 imx6ull.dtsi 源码中已经存在 gpio1 中断相关描述,不需要我们修改,具体内容如下:
gpio1: gpio@0209c000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>, // GIC_SPI:代表中断类型、66:代表中断号、IRQ_TYPE_LEVEL_HIGH:代表触发类型
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>; // 代表子节点 interrupts 属性由 2 个字段描述(第一字段代表 GPIO 号,第二字段代表触发类型)
};
通过以上分析 GPIO1 中断相关属性已经由 NXP 完成,因此不需要修改。
总结:中断设备树定义了中断路由过程路径,子节点触发中断后向父节点路由中断信号。
2、修改设备级中断属性
在 imx6ull-lq-evk.dts 设备树源码中的 lq-key 节点添加中断属性,修改后节点信息如下:
lq-key {
compatible = "lq-key";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_key>;
key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>;
interrupt-parent = <&gpio1>; // 指定中断父节点
interrupts = <18 IRQ_TYPE_EDGE_BOTH>; // 指定 GPIO 引脚和触发类型(双边沿触发)
};
对于触发类型详细描述见 include/linux/irq.h 文档,详细描述如下:
/*
* IRQ line status.
*
* Bits 0-7 are the same as the IRQF_* bits in linux/interrupt.h
*
* IRQ_TYPE_NONE - default, unspecified type
* IRQ_TYPE_EDGE_RISING - rising edge triggered
* IRQ_TYPE_EDGE_FALLING - falling edge triggered
* IRQ_TYPE_EDGE_BOTH - rising and falling edge triggered
* IRQ_TYPE_LEVEL_HIGH - high level triggered
* IRQ_TYPE_LEVEL_LOW - low level triggered
* IRQ_TYPE_LEVEL_MASK - Mask to filter out the level bits
* IRQ_TYPE_SENSE_MASK - Mask for all the above bits
* IRQ_TYPE_DEFAULT - For use by some PICs to ask irq_set_type
* to setup the HW to a sane default (used
* by irqdomain map() callbacks to synchronize
* the HW state and SW flags for a newly
* allocated descriptor).
*
* IRQ_TYPE_PROBE - Special flag for probing in progress
*
* Bits which can be modified via irq_set/clear/modify_status_flags()
* IRQ_LEVEL - Interrupt is level type. Will be also
* updated in the code when the above trigger
* bits are modified via irq_set_irq_type()
* IRQ_PER_CPU - Mark an interrupt PER_CPU. Will protect
* it from affinity setting
* IRQ_NOPROBE - Interrupt cannot be probed by autoprobing
* IRQ_NOREQUEST - Interrupt cannot be requested via
* request_irq()
* IRQ_NOTHREAD - Interrupt cannot be threaded
* IRQ_NOAUTOEN - Interrupt is not automatically enabled in
* request/setup_irq()
* IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set)
* IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context
* IRQ_NESTED_TRHEAD - Interrupt nests into another thread
* IRQ_PER_CPU_DEVID - Dev_id is a per-cpu variable
* IRQ_IS_POLLED - Always polled by another interrupt. Exclude
* it from the spurious interrupt detection
* mechanism and from core side polling.
*/
enum {
IRQ_TYPE_NONE = 0x00000000,
IRQ_TYPE_EDGE_RISING = 0x00000001,
IRQ_TYPE_EDGE_FALLING = 0x00000002,
IRQ_TYPE_EDGE_BOTH = (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING),
IRQ_TYPE_LEVEL_HIGH = 0x00000004,
IRQ_TYPE_LEVEL_LOW = 0x00000008,
IRQ_TYPE_LEVEL_MASK = (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH),
IRQ_TYPE_SENSE_MASK = 0x0000000f,
IRQ_TYPE_DEFAULT = IRQ_TYPE_SENSE_MASK,
IRQ_TYPE_PROBE = 0x00000010,
IRQ_LEVEL = (1 << 8),
IRQ_PER_CPU = (1 << 9),
IRQ_NOPROBE = (1 << 10),
IRQ_NOREQUEST = (1 << 11),
IRQ_NOAUTOEN = (1 << 12),
IRQ_NO_BALANCING = (1 << 13),
IRQ_MOVE_PCNTXT = (1 << 14),
IRQ_NESTED_THREAD = (1 << 15),
IRQ_NOTHREAD = (1 << 16),
IRQ_PER_CPU_DEVID = (1 << 17),
IRQ_IS_POLLED = (1 << 18),
};
3、编译设备树
onlylove@ubuntu:~/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga$ make dtbs
CHK include/config/kernel.release
CHK include/generated/uapi/linux/version.h
CHK include/generated/utsrelease.h
make[1]: 'include/generated/mach-types.h' is up to date.
CHK include/generated/bounds.h
CHK include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
DTC arch/arm/boot/dts/imx6ull-lq-evk.dtb
onlylove@ubuntu:~/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga$
4、验证
/ # ls
bin etc linuxrc proc sbin timer.ko usr
dev lib mnt root sys tmp
/ # cd /sys/firmware/devicetree/base/
/sys/firmware/devicetree/base # ls
#address-cells lq-led
#size-cells memory
aliases model
backlight name
chosen pxp_v4l2
clocks regulators
compatible reserved-memory
cpus soc
gpioled sound
interrupt-controller@00a01000 spi4
lq-key
/sys/firmware/devicetree/base # cd lq-key/
/sys/firmware/devicetree/base/lq-key # ls
compatible interrupts name pinctrl-names
interrupt-parent key-gpio pinctrl-0 status
/sys/firmware/devicetree/base/lq-key # ls -al
total 0
drwxr-xr-x 2 0 0 0 Jan 1 00:01 .
drwxr-xr-x 18 0 0 0 Jan 1 00:01 ..
-r--r--r-- 1 0 0 7 Jan 1 00:01 compatible
-r--r--r-- 1 0 0 4 Jan 1 00:01 interrupt-parent
-r--r--r-- 1 0 0 8 Jan 1 00:01 interrupts
-r--r--r-- 1 0 0 12 Jan 1 00:01 key-gpio
-r--r--r-- 1 0 0 7 Jan 1 00:01 name
-r--r--r-- 1 0 0 4 Jan 1 00:01 pinctrl-0
-r--r--r-- 1 0 0 8 Jan 1 00:01 pinctrl-names
-r--r--r-- 1 0 0 5 Jan 1 00:01 status
/sys/firmware/devicetree/base/lq-key #
通过以上日志可以确定 key 中断属性添加成功。
3、Makefile
KERNELDIR := /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga
CURRENT_PATH := $(shell pwd)
obj-m := irq.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
4、搭建驱动框架
1、代码实现
#include "linux/init.h"
#include "linux/module.h"
#include "linux/kdev_t.h"
#include "linux/fs.h"
#include "linux/printk.h"
#include "linux/cdev.h"
#include "linux/device.h"
#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "newchrdev" /* 名子 */
typedef struct{
struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 */
struct class *class; /* 类 (声明在 "linux/device.h")*/
struct device *device; /* 设备 (声明在 "linux/device.h") */
}newchrdev_t;
newchrdev_t newchrdev;
static const struct file_operations newchrdevops = {
.owner = THIS_MODULE,
};
/*
* MKDEV 声明在 "linux/kdev_t.h"
* register_chrdev_region 声明在 "linux/fs.h"
* printk 声明在 "linux/printk.h"
* alloc_chrdev_region 声明在 "linux/fs.h"
* cdev_init 声明在 "linux/cdev.h"
* cdev_add 声明在 "linux/cdev.h"
* class_create 声明在 "linux/device.h"
*/
/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
int ret;
/* 1、字符设备号分配 */
newchrdev.major = NEWCHRDEV_MAJOR;
if(newchrdev.major){
/* 指定设备号 */
newchrdev.minor = NEWCHRDEV_MINOR;
newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);
ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
printk("newchrdev.major > 0!\r\n");
}else{
/* 系统分配设备号 */
ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
newchrdev.major = MAJOR(newchrdev.devid);
newchrdev.minor = MINOR(newchrdev.devid);
printk("newchrdev.major = 0!\r\n");
}
if(ret < 0){
printk("newchrdev xxx_chrdev_region failed!\r\n");
goto newchrdev_chrdev_region_failed;
}
printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);
/* 2、注册字符设备 */
newchrdev.dev.owner = THIS_MODULE;
cdev_init(&newchrdev.dev,&newchrdevops);
ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);
if(ret < 0){
printk("newchrdev cdev_add failed!\r\n");
goto newchrdev_cdev_add_failed;
}
/* 3、创建类 */
newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.class)) {
printk("newchrdev class_create failed!\r\n");
goto newchrdev_class_create_failed;
}
/* 4、创建设备 */
newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.device)){
printk("newchrdev device_create failed!\r\n");
goto neschrdev_device_creat_failed;
}
return 0;
//neschrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */
// device_destroy(newchrdev.class,newchrdev.devid);
neschrdev_device_creat_failed: /* 删除类 */
class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */
cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */
printk("failed!\r\n");
return ret;
}
/*
* device_destroy 声明在 "linux/device.h"
* class_destroy 声明在 "linux/device.h"
* cdev_del 声明在 "linux/cdev.h"
* unregister_chrdev_region 声明在 "linux/fs.h"
*/
/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
/* 4、删除设备 */
device_destroy(newchrdev.class,newchrdev.devid);
/* 3、删除类 */
class_destroy(newchrdev.class);
/* 2、注销字符设备 */
cdev_del(&newchrdev.dev);
/* 1、释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
printk("newchrdev_exit succed!\r\n");
}
/*
* module_init 声明在 "linux/init.h"
* module_exit 声明在 "linux/init.h"
* MODULE_LICENSE 声明在 "linux/module.h"
*/
// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");
2、编译
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls -l
total 12
-rw-rw-r-- 1 onlylove onlylove 4367 Jan 18 07:09 irq.c
-rw-rw-r-- 1 onlylove onlylove 273 Jan 18 04:49 Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/8_irq modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
CC [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/onlylove/linux/driver/linux_driver/8_irq/irq.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls -l
total 36
-rw-rw-r-- 1 onlylove onlylove 4367 Jan 18 07:09 irq.c
-rw-rw-r-- 1 onlylove onlylove 5664 Jan 18 07:09 irq.ko
-rw-rw-r-- 1 onlylove onlylove 1266 Jan 18 07:09 irq.mod.c
-rw-rw-r-- 1 onlylove onlylove 2536 Jan 18 07:09 irq.mod.o
-rw-rw-r-- 1 onlylove onlylove 3844 Jan 18 07:09 irq.o
-rw-rw-r-- 1 onlylove onlylove 273 Jan 18 04:49 Makefile
-rw-rw-r-- 1 onlylove onlylove 61 Jan 18 07:09 modules.order
-rw-rw-r-- 1 onlylove onlylove 0 Jan 18 07:09 Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$
3、测试
/ # ls
bin etc lib mnt root sys usr
dev irq.ko linuxrc proc sbin tmp
/ # lsmod
Module Size Used by Not tainted
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ # rmmod irq.ko
newchrdev_exit succed!
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ # rmmod irq.ko
newchrdev_exit succed!
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ # rmmod irq.ko
newchrdev_exit succed!
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ # rmmod irq.ko
newchrdev_exit succed!
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ # rmmod irq.ko
newchrdev_exit succed!
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
/ # lsmod
Module Size Used by Tainted: G
irq 1494 0
/ # ls /dev/newchrdev -l
crw-rw---- 1 0 0 248, 0 Jan 1 02:29 /dev/newchrdev
/ #
/ #
/ # rmmod irq.ko
newchrdev_exit succed!
/ # lsmod
Module Size Used by Tainted: G
/ # ls /dev/newchrdev -l
ls: /dev/newchrdev: No such file or directory
5、key 引脚初始化
1、添加 key 数据类型
/* key 相关数据 */
typedef struct{
int gpio; /* gpio */
int irqnum; /* 中断号 */
unsigned char value; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
}key_t;
关于 key 使用的相关数据组织成结构体类型便于管理,多个按键可以定义多个变量。
特变说明:按键相关设备树节点放在 newchrdev_t,因为它只有一个节点,不可能出现多个。
2、完善 newchrdev_t 类型
typedef struct{
struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 */
struct class *class; /* 类 (声明在 "linux/device.h")*/
struct device *device; /* 设备 (声明在 "linux/device.h") */
struct device_node *nd; /* 设备节点 (声明在 "linux/of.h") */
key_t key_data; /* 按键相关数据 */
}newchrdev_t;
新增加 nd 和 key_data 成员。
3、初始化 key 引脚
1、查找设备节点
2、从设备树中获取 GPIO 配置信息
3、申请一个 GPIO
4、设置 GPIO 为输入
5、获取中断号
6、申请中断
6、驱动代码:功能实现
1、源码
#include "linux/init.h"
#include "linux/module.h"
#include "linux/kdev_t.h"
#include "linux/fs.h"
#include "linux/printk.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/of.h"
#include "linux/of_gpio.h"
#include "linux/gpio.h"
#include "linux/of_irq.h"
#include "linux/interrupt.h"
#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "newchrdev" /* 名子 */
int key_pin_init(void);
static irqreturn_t key_handler(int irq, void *dev_id);
void key_pin_exit(void);
/* key 相关数据 */
typedef struct{
int gpio; /* gpio */
int irqnum; /* 中断号 */
unsigned char value; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
}newkey_t;
typedef struct{
struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 */
struct class *class; /* 类 (声明在 "linux/device.h")*/
struct device *device; /* 设备 (声明在 "linux/device.h") */
struct device_node *nd; /* 设备节点 (声明在 "linux/of.h") */
newkey_t key_data; /* 按键相关数据 */
}newchrdev_t;
newchrdev_t newchrdev;
static const struct file_operations newchrdevops = {
.owner = THIS_MODULE,
};
/*
* MKDEV 声明在 "linux/kdev_t.h"
* register_chrdev_region 声明在 "linux/fs.h"
* printk 声明在 "linux/printk.h"
* alloc_chrdev_region 声明在 "linux/fs.h"
* cdev_init 声明在 "linux/cdev.h"
* cdev_add 声明在 "linux/cdev.h"
* class_create 声明在 "linux/device.h"
*/
/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
int ret;
/* 1、字符设备号分配 */
newchrdev.major = NEWCHRDEV_MAJOR;
if(newchrdev.major){
/* 指定设备号 */
newchrdev.minor = NEWCHRDEV_MINOR;
newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);
ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
printk("newchrdev.major > 0!\r\n");
}else{
/* 系统分配设备号 */
ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
newchrdev.major = MAJOR(newchrdev.devid);
newchrdev.minor = MINOR(newchrdev.devid);
printk("newchrdev.major = 0!\r\n");
}
if(ret < 0){
printk("newchrdev xxx_chrdev_region failed!\r\n");
goto newchrdev_chrdev_region_failed;
}
printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);
/* 2、注册字符设备 */
newchrdev.dev.owner = THIS_MODULE;
cdev_init(&newchrdev.dev,&newchrdevops);
ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);
if(ret < 0){
printk("newchrdev cdev_add failed!\r\n");
goto newchrdev_cdev_add_failed;
}
/* 3、创建类 */
newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.class)) {
printk("newchrdev class_create failed!\r\n");
goto newchrdev_class_create_failed;
}
/* 4、创建设备 */
newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.device)){
printk("newchrdev device_create failed!\r\n");
goto neschrdev_device_creat_failed;
}
/* 5、key 初始化 */
ret = key_pin_init();
if(ret < 0){
printk("key_pin_init!\r\n");
goto neschrdev_device_xxx_failed;
}
return 0;
neschrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */
device_destroy(newchrdev.class,newchrdev.devid);
neschrdev_device_creat_failed: /* 删除类 */
class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */
cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */
printk("failed!\r\n");
return ret;
}
/*
* device_destroy 声明在 "linux/device.h"
* class_destroy 声明在 "linux/device.h"
* cdev_del 声明在 "linux/cdev.h"
* unregister_chrdev_region 声明在 "linux/fs.h"
*/
/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
/* 5、key退出 */
key_pin_exit();
/* 4、删除设备 */
device_destroy(newchrdev.class,newchrdev.devid);
/* 3、删除类 */
class_destroy(newchrdev.class);
/* 2、注销字符设备 */
cdev_del(&newchrdev.dev);
/* 1、释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
printk("newchrdev_exit succed!\r\n");
}
/*
* module_init 声明在 "linux/init.h"
* module_exit 声明在 "linux/init.h"
* MODULE_LICENSE 声明在 "linux/module.h"
*/
// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");
/*
* @description : 按键 pin 初始化(从设备树中获取相关初始化数据)
* @param : 无
* @return : 无
*
* of_find_node_by_path 声明在 "linux/of.h"
* of_get_named_gpio 声明在 "linux/of_gpio.h"
* memcpy 声明在 "asm/string.h"
* gpio_request 声明在 "linux/gpio.h"
* gpio_direction_input 声明在 "linux/gpio.h"
* irq_of_parse_and_map 声明在 "linux/of_irq.h"
* request_irq 声明在 "linux/interrupt.h"
* IRQF_TRIGGER_FALLING 定义在 "linux/interrupt.h"
* IRQF_TRIGGER_RISING 定义在 "linux/interrupt.h"
*/
int key_pin_init(void)
{
int ret = 0;
/* 1、查找设备节点 */
newchrdev.nd = of_find_node_by_path("/lq-key");
if (newchrdev.nd== NULL){
printk("key node not find!\r\n");
return -1;
}
/* 2、从设备树中获取 GPIO 配置信息 */
newchrdev.key_data.gpio = of_get_named_gpio(newchrdev.nd,"key-gpio",0);
if(newchrdev.key_data.gpio < 0){
printk("can't get key-gpio\r\n");
return -2;
}
/* 3、申请一个 GPIO */
memcpy(newchrdev.key_data.name,"KEY",sizeof("KEY"));
gpio_request(newchrdev.key_data.gpio,newchrdev.key_data.name);
/* 4、设置 GPIO 为输入 */
gpio_direction_input(newchrdev.key_data.gpio);
/* 5、获取中断号 */
newchrdev.key_data.irqnum = irq_of_parse_and_map(newchrdev.nd,0);
printk("key:gpio=%d, irqnum=%d\r\n",newchrdev.key_data.gpio, newchrdev.key_data.irqnum);
/* 6、申请中断 */
newchrdev.key_data.handler = key_handler;
ret = request_irq(newchrdev.key_data.irqnum,newchrdev.key_data.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,newchrdev.key_data.name,&newchrdev);
if(ret < 0){
printk("can't request irq!\r\n");
return -3;
}
return 0;
}
/* @description : 中断服务函数,开启定时器,延时10ms,
* 定时器用于按键消抖。
* @param - irq : 中断号
* @param - dev_id : 设备结构。
* @return : 中断执行结果
*/
static irqreturn_t key_handler(int irq, void *dev_id)
{
printk("key0_handler\r\n");
return IRQ_RETVAL(IRQ_HANDLED);
}
void key_pin_exit(void)
{
free_irq(newchrdev.key_data.irqnum,&newchrdev);
}
2、编译
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/8_irq modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
CC [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/onlylove/linux/driver/linux_driver/8_irq/irq.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c irq.ko irq.mod.c irq.mod.o irq.o Makefile modules.order Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$
3、验证
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
key:gpio=18, irqnum=49
/ # rmmod irq.ko
newchrdev_exit succed!
/ #
/ # ls
bin etc lib mnt root sys usr
dev irq.ko linuxrc proc sbin tmp
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
key:gpio=18, irqnum=49
/ # key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
key0_handler
/ #
通过以上验证,功能基本实现,但按键消抖未实现。
7、驱动代码:使用中断下半部
我们使用 tasklet 作为中断下半部。
1、源码
#include "linux/init.h"
#include "linux/module.h"
#include "linux/kdev_t.h"
#include "linux/fs.h"
#include "linux/printk.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/of.h"
#include "linux/of_gpio.h"
#include "linux/gpio.h"
#include "linux/of_irq.h"
#include "linux/interrupt.h"
#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "newchrdev" /* 名子 */
int key_pin_init(void);
static irqreturn_t key_handler(int irq, void *dev_id);
void key_pin_exit(void);
static void key_tasklet(unsigned long data);
/* key 相关数据 */
typedef struct{
int gpio; /* gpio */
int irqnum; /* 中断号 */
unsigned char value; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
struct tasklet_struct tasklet; /* tasklet 变量(中断下半部) */
unsigned long tasklet_data; /* 传递给 tasklet 处理函数的参数 */
}newkey_t;
typedef struct{
struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 */
struct class *class; /* 类 (声明在 "linux/device.h")*/
struct device *device; /* 设备 (声明在 "linux/device.h") */
struct device_node *nd; /* 设备节点 (声明在 "linux/of.h") */
newkey_t key_data; /* 按键相关数据 */
}newchrdev_t;
newchrdev_t newchrdev;
static const struct file_operations newchrdevops = {
.owner = THIS_MODULE,
};
/*
* MKDEV 声明在 "linux/kdev_t.h"
* register_chrdev_region 声明在 "linux/fs.h"
* printk 声明在 "linux/printk.h"
* alloc_chrdev_region 声明在 "linux/fs.h"
* cdev_init 声明在 "linux/cdev.h"
* cdev_add 声明在 "linux/cdev.h"
* class_create 声明在 "linux/device.h"
*/
/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
int ret;
/* 1、字符设备号分配 */
newchrdev.major = NEWCHRDEV_MAJOR;
if(newchrdev.major){
/* 指定设备号 */
newchrdev.minor = NEWCHRDEV_MINOR;
newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);
ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
printk("newchrdev.major > 0!\r\n");
}else{
/* 系统分配设备号 */
ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
newchrdev.major = MAJOR(newchrdev.devid);
newchrdev.minor = MINOR(newchrdev.devid);
printk("newchrdev.major = 0!\r\n");
}
if(ret < 0){
printk("newchrdev xxx_chrdev_region failed!\r\n");
goto newchrdev_chrdev_region_failed;
}
printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);
/* 2、注册字符设备 */
newchrdev.dev.owner = THIS_MODULE;
cdev_init(&newchrdev.dev,&newchrdevops);
ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);
if(ret < 0){
printk("newchrdev cdev_add failed!\r\n");
goto newchrdev_cdev_add_failed;
}
/* 3、创建类 */
newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.class)) {
printk("newchrdev class_create failed!\r\n");
goto newchrdev_class_create_failed;
}
/* 4、创建设备 */
newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.device)){
printk("newchrdev device_create failed!\r\n");
goto neschrdev_device_creat_failed;
}
/* 5、key 初始化 */
ret = key_pin_init();
if(ret < 0){
printk("key_pin_init!\r\n");
goto neschrdev_device_xxx_failed;
}
return 0;
neschrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */
device_destroy(newchrdev.class,newchrdev.devid);
neschrdev_device_creat_failed: /* 删除类 */
class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */
cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */
printk("failed!\r\n");
return ret;
}
/*
* device_destroy 声明在 "linux/device.h"
* class_destroy 声明在 "linux/device.h"
* cdev_del 声明在 "linux/cdev.h"
* unregister_chrdev_region 声明在 "linux/fs.h"
*/
/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
/* 5、key退出 */
key_pin_exit();
/* 4、删除设备 */
device_destroy(newchrdev.class,newchrdev.devid);
/* 3、删除类 */
class_destroy(newchrdev.class);
/* 2、注销字符设备 */
cdev_del(&newchrdev.dev);
/* 1、释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
printk("newchrdev_exit succed!\r\n");
}
/*
* module_init 声明在 "linux/init.h"
* module_exit 声明在 "linux/init.h"
* MODULE_LICENSE 声明在 "linux/module.h"
*/
// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");
/*
* @description : 按键 pin 初始化(从设备树中获取相关初始化数据)
* @param : 无
* @return : 无
*
* of_find_node_by_path 声明在 "linux/of.h"
* of_get_named_gpio 声明在 "linux/of_gpio.h"
* memcpy 声明在 "asm/string.h"
* gpio_request 声明在 "linux/gpio.h"
* gpio_direction_input 声明在 "linux/gpio.h"
* irq_of_parse_and_map 声明在 "linux/of_irq.h"
* request_irq 声明在 "linux/interrupt.h"
* IRQF_TRIGGER_FALLING 定义在 "linux/interrupt.h"
* IRQF_TRIGGER_RISING 定义在 "linux/interrupt.h"
* tasklet_init 声明在 "linux/interrupt.h"
*/
int key_pin_init(void)
{
int ret = 0;
/* 1、查找设备节点 */
newchrdev.nd = of_find_node_by_path("/lq-key");
if (newchrdev.nd== NULL){
printk("key node not find!\r\n");
return -1;
}
/* 2、从设备树中获取 GPIO 配置信息 */
newchrdev.key_data.gpio = of_get_named_gpio(newchrdev.nd,"key-gpio",0);
if(newchrdev.key_data.gpio < 0){
printk("can't get key-gpio\r\n");
return -2;
}
/* 3、申请一个 GPIO */
memcpy(newchrdev.key_data.name,"KEY",sizeof("KEY"));
gpio_request(newchrdev.key_data.gpio,newchrdev.key_data.name);
/* 4、设置 GPIO 为输入 */
gpio_direction_input(newchrdev.key_data.gpio);
/* 5、获取中断号 */
newchrdev.key_data.irqnum = irq_of_parse_and_map(newchrdev.nd,0);
printk("key:gpio=%d, irqnum=%d\r\n",newchrdev.key_data.gpio, newchrdev.key_data.irqnum);
/* 6、申请中断 */
newchrdev.key_data.handler = key_handler;
ret = request_irq(newchrdev.key_data.irqnum,newchrdev.key_data.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,newchrdev.key_data.name,&newchrdev);
if(ret < 0){
printk("can't request irq!\r\n");
return -3;
}
/* 7、初始化中断下半部(tasklet) */
tasklet_init(&newchrdev.key_data.tasklet,key_tasklet,newchrdev.key_data.tasklet_data);
return 0;
}
/* @description : 中断服务函数,开启定时器,延时10ms,
* 定时器用于按键消抖。
* @param - irq : 中断号
* @param - dev_id : 设备结构。
* @return : 中断执行结果
*/
static irqreturn_t key_handler(int irq, void *dev_id)
{
printk("key0_handler\r\n");
tasklet_schedule(&newchrdev.key_data.tasklet);
return IRQ_RETVAL(IRQ_HANDLED);
}
void key_pin_exit(void)
{
free_irq(newchrdev.key_data.irqnum,&newchrdev);
}
static void key_tasklet(unsigned long data)
{
printk("key_tasklet\r\n");
}
以上源码为根据 Linux 中断设计思路二完善源码。
2、编译
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/8_irq modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
CC [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/onlylove/linux/driver/linux_driver/8_irq/irq.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c irq.ko irq.mod.c irq.mod.o irq.o Makefile modules.order Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ cp irq.ko ~/linux/nfs/rootfs-l
rootfs-l/ rootfs-l.tar.gz
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ cp irq.ko ~/linux/nfs/rootfs-l/
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c irq.ko irq.mod.c irq.mod.o irq.o Makefile modules.order Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$
3、验证
/ # ls
bin etc lib mnt root sys usr
dev irq.ko linuxrc proc sbin tmp
/ # lsmod
Module Size Used by Not tainted
/ #
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
key:gpio=18, irqnum=49
/ #
/ # lsmod
Module Size Used by Tainted: G
irq 2563 0
/ # rmmod irq.ko
newchrdev_exit succed!
/ #
/ # lsmod
Module Size Used by Tainted: G
/ #
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
key:gpio=18, irqnum=49
/ #
/ # key0_handler
key_tasklet
key0_handler
key_tasklet
/ # rmmod irq.ko
newchrdev_exit succed!
/ #
8、驱动代码:按键消抖
使用内核定时器完成按键消抖。
1、源码
#include "linux/init.h"
#include "linux/module.h"
#include "linux/kdev_t.h"
#include "linux/fs.h"
#include "linux/printk.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include "linux/of.h"
#include "linux/of_gpio.h"
#include "linux/gpio.h"
#include "linux/of_irq.h"
#include "linux/interrupt.h"
#include "linux/jiffies.h"
#define NEWCHRDEV_MAJOR 0 /* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0 /* 次设备号 */
#define NEWCHRDEV_COUNT 1 /* 设备号个数 */
#define NEWCHRDEV_NAME "newchrdev" /* 名子 */
int key_pin_init(void);
static irqreturn_t key_handler(int irq, void *dev_id);
void key_pin_exit(void);
static void key_tasklet(unsigned long data);
void timer_function(unsigned long arg);
void key_timer_init(void);
void key_timer_exit(void);
/* key 相关数据 */
typedef struct{
int gpio; /* gpio */
int irqnum; /* 中断号 */
unsigned char value; /* 按键对应的键值 */
char name[10]; /* 名字 */
irqreturn_t (*handler)(int, void *); /* 中断服务函数 */
struct tasklet_struct tasklet; /* tasklet 变量(中断下半部) */
unsigned long tasklet_data; /* 传递给 tasklet 处理函数的参数 */
struct timer_list key_timer; /* 中断定时器,用于按键消抖 */
}newkey_t;
typedef struct{
struct cdev dev; /* cdev 结构体 (声明在 "linux/cdev.h")*/
int major; /* 主设备号 */
int minor; /* 次设备号 */
dev_t devid; /* 设备号 */
struct class *class; /* 类 (声明在 "linux/device.h")*/
struct device *device; /* 设备 (声明在 "linux/device.h") */
struct device_node *nd; /* 设备节点 (声明在 "linux/of.h") */
newkey_t key_data; /* 按键相关数据 */
}newchrdev_t;
newchrdev_t newchrdev;
static const struct file_operations newchrdevops = {
.owner = THIS_MODULE,
};
/*
* MKDEV 声明在 "linux/kdev_t.h"
* register_chrdev_region 声明在 "linux/fs.h"
* printk 声明在 "linux/printk.h"
* alloc_chrdev_region 声明在 "linux/fs.h"
* cdev_init 声明在 "linux/cdev.h"
* cdev_add 声明在 "linux/cdev.h"
* class_create 声明在 "linux/device.h"
*/
/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
int ret;
/* 1、字符设备号分配 */
newchrdev.major = NEWCHRDEV_MAJOR;
if(newchrdev.major){
/* 指定设备号 */
newchrdev.minor = NEWCHRDEV_MINOR;
newchrdev.devid = MKDEV(newchrdev.major, newchrdev.minor);
ret = register_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
printk("newchrdev.major > 0!\r\n");
}else{
/* 系统分配设备号 */
ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
newchrdev.major = MAJOR(newchrdev.devid);
newchrdev.minor = MINOR(newchrdev.devid);
printk("newchrdev.major = 0!\r\n");
}
if(ret < 0){
printk("newchrdev xxx_chrdev_region failed!\r\n");
goto newchrdev_chrdev_region_failed;
}
printk("newchrdev devid = %d newchrdev major=%d,minor=%d\r\n",newchrdev.devid,newchrdev.major,newchrdev.minor);
/* 2、注册字符设备 */
newchrdev.dev.owner = THIS_MODULE;
cdev_init(&newchrdev.dev,&newchrdevops);
ret = cdev_add(&newchrdev.dev,newchrdev.devid,NEWCHRDEV_COUNT);
if(ret < 0){
printk("newchrdev cdev_add failed!\r\n");
goto newchrdev_cdev_add_failed;
}
/* 3、创建类 */
newchrdev.class = class_create(THIS_MODULE,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.class)) {
printk("newchrdev class_create failed!\r\n");
goto newchrdev_class_create_failed;
}
/* 4、创建设备 */
newchrdev.device = device_create(newchrdev.class,NULL,newchrdev.devid,NULL,NEWCHRDEV_NAME);
if(IS_ERR(newchrdev.device)){
printk("newchrdev device_create failed!\r\n");
goto neschrdev_device_creat_failed;
}
/* 5、key 初始化 */
ret = key_pin_init();
if(ret < 0){
printk("key_pin_init!\r\n");
goto neschrdev_device_xxx_failed;
}
return 0;
neschrdev_device_xxx_failed: /* 其他操作失败(添加其他文件时,打开此选项) */
device_destroy(newchrdev.class,newchrdev.devid);
neschrdev_device_creat_failed: /* 删除类 */
class_destroy(newchrdev.class);
newchrdev_class_create_failed: /*注销字符设备 */
cdev_del(&newchrdev.dev);
newchrdev_cdev_add_failed: /* 释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
newchrdev_chrdev_region_failed: /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */
printk("failed!\r\n");
return ret;
}
/*
* device_destroy 声明在 "linux/device.h"
* class_destroy 声明在 "linux/device.h"
* cdev_del 声明在 "linux/cdev.h"
* unregister_chrdev_region 声明在 "linux/fs.h"
*/
/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
/*6、删除内核定时器(用于按键消抖)*/
key_timer_exit();
/* 5、key退出 */
key_pin_exit();
/* 4、删除设备 */
device_destroy(newchrdev.class,newchrdev.devid);
/* 3、删除类 */
class_destroy(newchrdev.class);
/* 2、注销字符设备 */
cdev_del(&newchrdev.dev);
/* 1、释放设备号 */
unregister_chrdev_region(newchrdev.devid,NEWCHRDEV_COUNT);
printk("newchrdev_exit succed!\r\n");
}
/*
* module_init 声明在 "linux/init.h"
* module_exit 声明在 "linux/init.h"
* MODULE_LICENSE 声明在 "linux/module.h"
*/
// 头文件 "linux/init.h"
module_init(newchrdev_init);
module_exit(newchrdev_exit);
MODULE_LICENSE("GPL");
/*
* @description : 按键 pin 初始化(从设备树中获取相关初始化数据)
* @param : 无
* @return : 无
*
* of_find_node_by_path 声明在 "linux/of.h"
* of_get_named_gpio 声明在 "linux/of_gpio.h"
* memcpy 声明在 "asm/string.h"
* gpio_request 声明在 "linux/gpio.h"
* gpio_direction_input 声明在 "linux/gpio.h"
* irq_of_parse_and_map 声明在 "linux/of_irq.h"
* request_irq 声明在 "linux/interrupt.h"
* IRQF_TRIGGER_FALLING 定义在 "linux/interrupt.h"
* IRQF_TRIGGER_RISING 定义在 "linux/interrupt.h"
* tasklet_init 声明在 "linux/interrupt.h"
* init_timer 声明在 "linux/timer.h"
*/
int key_pin_init(void)
{
int ret = 0;
/* 1、查找设备节点 */
newchrdev.nd = of_find_node_by_path("/lq-key");
if (newchrdev.nd== NULL){
printk("key node not find!\r\n");
return -1;
}
/* 2、从设备树中获取 GPIO 配置信息 */
newchrdev.key_data.gpio = of_get_named_gpio(newchrdev.nd,"key-gpio",0);
if(newchrdev.key_data.gpio < 0){
printk("can't get key-gpio\r\n");
return -2;
}
/* 3、申请一个 GPIO */
memcpy(newchrdev.key_data.name,"KEY",sizeof("KEY"));
gpio_request(newchrdev.key_data.gpio,newchrdev.key_data.name);
/* 4、设置 GPIO 为输入 */
gpio_direction_input(newchrdev.key_data.gpio);
/* 5、获取中断号 */
newchrdev.key_data.irqnum = irq_of_parse_and_map(newchrdev.nd,0);
printk("key:gpio=%d, irqnum=%d\r\n",newchrdev.key_data.gpio, newchrdev.key_data.irqnum);
/* 6、申请中断 */
newchrdev.key_data.handler = key_handler;
ret = request_irq(newchrdev.key_data.irqnum,newchrdev.key_data.handler,IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,newchrdev.key_data.name,&newchrdev);
if(ret < 0){
printk("can't request irq!\r\n");
return -3;
}
/* 7、初始化中断下半部(tasklet) */
tasklet_init(&newchrdev.key_data.tasklet,key_tasklet,newchrdev.key_data.tasklet_data);
/* 8、初始化定时器(用于按键消抖) */
key_timer_init();
return 0;
}
/* @description : 中断服务函数,开启定时器,延时10ms,
* 定时器用于按键消抖。
* @param - irq : 中断号
* @param - dev_id : 设备结构。
* @return : 中断执行结果
*/
static irqreturn_t key_handler(int irq, void *dev_id)
{
printk("key0_handler\r\n");
tasklet_schedule(&newchrdev.key_data.tasklet);
return IRQ_RETVAL(IRQ_HANDLED);
}
void key_pin_exit(void)
{
free_irq(newchrdev.key_data.irqnum,&newchrdev);
}
/*
* jiffies 定义在 "linux/jiffies.h"
* msecs_to_jiffies 声明在 "linux/jiffies.h"
*
*/
static void key_tasklet(unsigned long data)
{
printk("key_tasklet\r\n");
newchrdev.key_data.key_timer.data = (unsigned long)(&newchrdev.key_data.key_timer);
mod_timer(&newchrdev.key_data.key_timer,jiffies + msecs_to_jiffies(2000));
}
void timer_function(unsigned long arg)
{
printk("timer_function\r\n");
}
void key_timer_init(void)
{
init_timer(&newchrdev.key_data.key_timer);
newchrdev.key_data.key_timer.function = timer_function;
}
void key_timer_exit(void)
{
del_timer(&newchrdev.key_data.key_timer);
}
2、编译
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c Makefile
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ make
make -C /home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga M=/home/onlylove/linux/driver/linux_driver/8_irq modules
make[1]: Entering directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
CC [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/onlylove/linux/driver/linux_driver/8_irq/irq.mod.o
LD [M] /home/onlylove/linux/driver/linux_driver/8_irq/irq.ko
make[1]: Leaving directory '/home/onlylove/linux/linux/lq_linux/linux-imx-rel_imx_4.1.15_2.1.0_ga'
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c irq.ko irq.mod.c irq.mod.o irq.o Makefile modules.order Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ cp irq.ko ~/linux/nfs/rootfs-l
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$ ls
irq.c irq.ko irq.mod.c irq.mod.o irq.o Makefile modules.order Module.symvers
onlylove@ubuntu:~/linux/driver/linux_driver/8_irq$
3、验证
/ # ls
bin etc lib mnt root sys usr
dev irq.ko linuxrc proc sbin tmp
/ # rm irq.ko
/ # random: nonblocking pool is initialized
ls
bin etc lib mnt root sys usr
dev irq.ko linuxrc proc sbin tmp
/ # insmod irq.ko
newchrdev.major = 0!
newchrdev devid = 260046848 newchrdev major=248,minor=0
key:gpio=18, irqnum=49
/ # key0_handler
key_tasklet
key0_handler
key0_handler
key_tasklet
timer_function
key0_handler
key_tasklet
key0_handler
key_tasklet
timer_function
key0_handler
key_tasklet
key0_handler
key_tasklet
timer_function
/ # rmmod irq.ko
newchrdev_exit succed!
/ # ls
bin etc lib mnt root sys usr
dev irq.ko linuxrc proc sbin tmp
/ #