当前位置: 首页 > news >正文

i.MX 6ULL 驱动开发 九:中断

一、GICv2 基本概念

GICv2 整理_lqonlylove的博客-CSDN博客

二、Linux 中断子系统和中断上下部

Linux 驱动开发 三十九:Linux 中断(一)_lqonlylove的博客-CSDN博客_linux msi中断

三、设备树关于中断属性说明

Linux 驱动开发 九:《Power_ePAPR_APPROVED_v1.12》翻译_lqonlylove的博客-CSDN博客

Linux 驱动开发 十:《Devicetree Specification》翻译_lqonlylove的博客-CSDN博客

Linux 驱动开发 三十七:《gic.txt》翻译_lqonlylove的博客-CSDN博客

四、imx6ull在设备树中添加中断属性说明

Linux 驱动开发 三十八:《fsl-imx-gpio.txt》翻译_lqonlylove的博客-CSDN博客

五、原理图

在这里插入图片描述
通过原理图分析可以得到,当按键按下KEY0低电平,当按键释放KEY0高电平。通过原理图可以确定 KEY0 连接在 UART1_CTS 引脚上。

六、添加设备树

1、确定引脚

通过原理图可以确定 key 使用 UART1_CTS 引脚。

2、查找引脚定义是否冲突

3、添加 pinctrl 子系统相关配置

pinctrl_key: keygrp {
	fsl,pins = <
		MX6UL_PAD_UART1_CTS_B__GPIO1_IO18		0xF080	/* KEY0 */
	>;
};

4、添加 gpio 子系统相关配置

key {
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "lq-key";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_key>;
	key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
	status = "okay";
};

5、添加 interrupts 相关配置

key {
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "lq-key";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_key>;
	key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
	interrupt-parent = <&gpio1>;
	interrupts = <18 IRQ_TYPE_LEVEL_LOW>;  /* 低电平触发 */
	status = "okay";
};

主要添加 interrupt-parentinterrupts 属性。

6、测试

1、编译设备树

onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ 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-alientek-emmc.dtb
  DTC     arch/arm/boot/dts/imx6ull-alientek-nand.dtb
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$

2、拷贝编译成功的设备树文件

onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ ls /home/onlylove/my/tftp/ -l
total 11528
-rwxrwxr-x 1 onlylove onlylove 5901744 Sep 17 04:04 zImage
-rwxrwxr-x 1 onlylove onlylove 5901752 Aug 20 01:24 zImage.bak
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb /home/onlylove/my/tftp
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$ ls /home/onlylove/my/tftp/ -l
total 11568
-rw-rw-r-- 1 onlylove onlylove   39084 Sep 23 21:18 imx6ull-alientek-emmc.dtb
-rwxrwxr-x 1 onlylove onlylove 5901744 Sep 17 04:04 zImage
-rwxrwxr-x 1 onlylove onlylove 5901752 Aug 20 01:24 zImage.bak
onlylove@ubuntu:~/my/linux/linux-imx-4.1.15$

3、启动 linux 查看设备树解析是否成功

/ # cd /proc/
/proc # ls
1              41             65             driver         mounts
10             46             66             execdomains    mtd
11             47             7              fb             net
12             48             8              filesystems    pagetypeinfo
13             49             83             fs             partitions
14             5              84             interrupts     self
15             50             9              iomem          softirqs
16             51             95             ioports        stat
17             52             asound         irq            swaps
18             53             buddyinfo      kallsyms       sys
19             54             bus            key-users      sysrq-trigger
2              55             cgroups        keys           sysvipc
20             56             cmdline        kmsg           thread-self
21             57             consoles       kpagecount     timer_list
22             58             cpu            kpageflags     tty
23             59             cpuinfo        loadavg        uptime
24             6              crypto         locks          version
3              60             device-tree    meminfo        vmallocinfo
4              61             devices        misc           vmstat
40             62             diskstats      modules        zoneinfo
/proc # cd device-tree/
/sys/firmware/devicetree/base # ls
#address-cells                 key
#size-cells                    memory
aliases                        model
alphaled                       name
backlight                      pxp_v4l2
beep                           regulators
chosen                         reserved-memory
clocks                         sii902x-reset
compatible                     soc
cpus                           sound
gpioled                        spi4
interrupt-controller@00a01000
/sys/firmware/devicetree/base # cd key/
/sys/firmware/devicetree/base/key # ls
#address-cells    interrupt-parent  name              status
#size-cells       interrupts        pinctrl-0
compatible        key-gpio          pinctrl-names
/sys/firmware/devicetree/base/key # cat compatible
lq-key
/sys/firmware/devicetree/base/key #
/sys/firmware/devicetree/base/key #

七、按键消抖

使用内核定时器进行按键消抖。

Linux 驱动开发 三十四:Linux 内核定时器原理_lqonlylove的博客-CSDN博客_linux内核定时器原理

八、代码逻辑

1、设置按键中断为低电平触发。

2、触发按键中断后,保存按键状态。

3、使用原子变量保存按键状态。

4、在驱动的 read 接口中将按键状态返回给应用程序,并设置按键状态变量值。

九、驱动编写

#include "linux/init.h"
#include "linux/module.h"
#include "linux/kdev_t.h"
#include "linux/fs.h"
#include "linux/cdev.h"
#include "linux/device.h"
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>

#define NEWCHRDEV_MAJOR 0   		/* 主设备号(如果为0则让系统自动分配,如果大于0则使用指定设备号) */
#define NEWCHRDEV_MINOR 0   		/* 次设备号 */
#define NEWCHRDEV_COUNT 1   		/* 设备号个数 */
#define NEWCHRDEV_NAME  "key"       /* 名子 */
#define TIMEOUT 1000                 /* 定时器超时时间 */

typedef struct{
    struct device_node	*nd;        /* 设备节点 */
    int gpio;                       /* key gpio */
    int irqnum;                     /* 中断号 */
    struct tasklet_struct tasklet;  /* tasklet 变量(中断下半部) */
    unsigned long tasklet_data;     /* 传递给 tasklet 处理函数的参数 */
    struct work_struct work;        /* 工作队列 变量(中断下半部) */
    struct timer_list timer;        /* key 消抖定时器 */
    atomic_t keyvalue;              /* 按键状态 */
}lqkey_t;

typedef struct{
    struct cdev dev;        /* cdev 结构体 */
    int major;              /* 主设备号 */
    int minor;              /* 次设备号 */
    dev_t devid;            /* 设备号 */
    struct class *class;    /* 类 */
    struct device *device;  /* 设备 */
    lqkey_t key_data;         /* key 相关数据 */
}newchrdev_t;

newchrdev_t newchrdev;

/*
 * @description	: 按键IO初始化
 * @param 		: 无
 * @return 		: 无
 */
static int keyio_init(void)
{
    int ret = 0;
    /***** 处理设备树 *****/
    /* 1、获取设备树节点 */
    newchrdev.key_data.nd = of_find_node_by_path("/key");
	if (newchrdev.key_data.nd== NULL){
		printk("key node not find!\r\n");
		goto keyio_init_error1;
	}else {
		printk("beep node find!\r\n");
	}
    /* 1、获取设备树中的gpio属性 */
    newchrdev.key_data.gpio = of_get_named_gpio(newchrdev.key_data.nd, "key-gpio", 0);
	if(newchrdev.key_data.gpio < 0) {
		printk("can't get key-gpio");
		goto keyio_init_error1;
	}
	printk("key num = %d\r\n", newchrdev.key_data.gpio);

    /***** 使用gpio子系统设置引脚 *****/
    /* 1、向 gpio 子系统申请 GPIO 管脚 */
    ret = gpio_request(newchrdev.key_data.gpio,"key");
    if(ret){
        printk("can't request key-gpio!\r\n");
        goto keyio_init_error1;
    }
    /* 2、设置key-gpio为输入 */
    gpio_direction_input(newchrdev.key_data.gpio);
    if(ret < 0) {
		printk("can't set key-gpio!\r\n");
        goto keyio_init_error2;
	}
    return 0;

keyio_init_error2:
    gpio_free(newchrdev.key_data.gpio);
keyio_init_error1:
    return 1;
}

static int keyio_exit(void)
{
    /* 1、向gpio子系统请求释放gpio */
    gpio_free(newchrdev.key_data.gpio);
    return 0;
}

static void key_tasklet(unsigned long data)
{
    printk("key_tasklet\r\n");
}

static void key_work(struct work_struct *work)
{
    printk("key_work\r\n");
    mod_timer(&newchrdev.key_data.timer, jiffies + msecs_to_jiffies(TIMEOUT));
}

/* @description		: 中断服务函数
 *				  	  定时器用于按键消抖
 * @param - irq 	: 中断号 
 * @param - dev_id	: 设备结构
 * @return 			: 中断执行结果
 */
static irqreturn_t key0_handler(int irq, void *dev_id)
{
    printk("key0_handler\r\n");
//    tasklet_schedule(&newchrdev.key_data.tasklet);
    schedule_work(&newchrdev.key_data.work);
	return IRQ_RETVAL(IRQ_HANDLED);
}

/*
 * @description	: key中断初始化
 * @param 		: 无
 * @return 		: 无
 */
static int keyirq_init(void)
{
    int ret = 0;
    /***** 使用中断子系统设置 *****/
    /* 1、从设备树获取key-gpio对于中断号 */
    newchrdev.key_data.irqnum = irq_of_parse_and_map(newchrdev.key_data.nd, 0);
    if (!newchrdev.key_data.irqnum){
        goto keyirq_init_error1;
    }
    /* 2、向中断子系统请求中断号 */
    ret = request_irq(newchrdev.key_data.irqnum, key0_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,"KEY0",&newchrdev);
    if(ret < 0){
        printk("can't request irq!\r\n");
        goto keyirq_init_error1;
    }
    /***** 使用中断上下部(tasklet和工作队列选择一个) *****/
    /* 1、初始化中断下半部 */
//    tasklet_init(&newchrdev.key_data.tasklet,key_tasklet,newchrdev.key_data.tasklet_data);
    INIT_WORK(&newchrdev.key_data.work, key_work);
    return 0;
keyirq_init_error1:
    return 1;
}

static int keyirq_exit(void)
{
    /* 1、向中断子系统请求释放中断号 */
    free_irq(newchrdev.key_data.irqnum,&newchrdev);
    /***** 使用中断上下部(tasklet和工作队列选择一个) *****/
    /* 1、销毁中断下半部 */
//    tasklet_kill(&newchrdev.key_data.tasklet);
    flush_work(&newchrdev.key_data.work);
    return 0;
}

void key_timer_function(unsigned long arg)
{
    printk("key_timer_function\r\n");
    /* 1、设置按键状态(按键按下) */
    atomic_set(&newchrdev.key_data.keyvalue,0x01);
}

/*
 * @description	: key消抖定时器初始化
 * @param 		: 无
 * @return 		: 无
 */
static int keytimer_init(void)
{
    /* 1、创建定时器 */
    init_timer(&newchrdev.key_data.timer);
    newchrdev.key_data.timer.function = key_timer_function;
    newchrdev.key_data.timer.expires = jiffies + msecs_to_jiffies(TIMEOUT);
    newchrdev.key_data.timer.data = (unsigned long)&newchrdev;
    return 0;
}

static int keytimer_exit(void)
{
    /* 删除、创建定时器 */
    del_timer(&newchrdev.key_data.timer);
    return 0;
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量
 * 					  一般在open的时候将private_data指向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int newchrdev_open(struct inode *inode, struct file *filp)
{
    printk("newchrdev_open!\r\n");
    filp->private_data = &newchrdev; /* 设置私有数据 */
    return 0;
}

/*
 * @description		: 从设备读取数据 
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t newchrdev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
    int ret = 0;
    unsigned char keystatus = 0;
//    printk("newchrdev_read!\r\n");
    keystatus = atomic_read(&newchrdev.key_data.keyvalue);
    if(keystatus == 0x01){
        ret = copy_to_user(buf, &keystatus, sizeof(keystatus));
        atomic_set(&newchrdev.key_data.keyvalue,0xFF);
    }else{
        goto data_error;
    }
	return 0;
data_error:
	return -EINVAL;
}

/*
 * @description		: 向设备写数据 
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t newchrdev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
    printk("newchrdev_write!\r\n");
    return 0;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int newchrdev_release(struct inode *inode, struct file *filp)
{
    printk("newchrdev_release!\r\n");
	return 0;
}

static const struct file_operations newchrdevops = {
    .owner   = THIS_MODULE,
    .open = newchrdev_open,
	.read = newchrdev_read,
	.write = newchrdev_write,
	.release = newchrdev_release,
};

/* 驱动入口函数 */
static int __init newchrdev_init(void)
{
    /* 驱动入口函数具体内容 */
    /* 1、字符设备号分配 */
    int ret;
    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);
    }else{
        ret = alloc_chrdev_region(&newchrdev.devid,0,NEWCHRDEV_COUNT,NEWCHRDEV_NAME);
        newchrdev.major = MAJOR(newchrdev.devid);
        newchrdev.minor = MINOR(newchrdev.devid);
    }
    if(ret < 0){
        printk("newchrdev xxx_chrdev_region failed!\r\n");
        goto newchrdev_chrdev_region_failed;
    }
    printk("newchrdev major=%d,minor=%d\r\n",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;
    }

    ret = keyio_init();
    if(ret != 0){
        goto keyio_init_failed;
    }

    ret = keyirq_init();
    if(ret != 0){
        goto keyirq_init_failed;
    }

    keytimer_init();
    /* 初始化按键状态值 */
    atomic_set(&newchrdev.key_data.keyvalue,0xFF);

    return 0;
keyirq_init_failed:
    keyio_exit();
keyio_init_failed:
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:   /* 字符设备号分配失败处理函数(未分配资源,因此不做处理) */
    return ret;
}

/* 驱动卸载函数 */
static void __exit newchrdev_exit(void)
{
    keytimer_exit();
    keyirq_exit();
    keyio_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);
}

module_init(newchrdev_init);
module_exit(newchrdev_exit);

MODULE_LICENSE("GPL");

十、应用程序编写

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "stdio.h"

int main(int argc, char *argv[])
{
    int fd = 0, retvalue = 0;
    char readbuf = 0;
    if(argc != 2){
		printf("Error Usage!\r\n");
		return -1;
	}

    fd = open(argv[1],O_RDWR);
    if(fd < 0){
        printf("Can't open file %s\r\n", argv[1]);
        return -1;
    }
    printf("sizeof(readbuf) = %d\r\n",sizeof(readbuf));
    while(1){
        retvalue = read(fd, &readbuf, sizeof(readbuf));
        if (retvalue < 0){

        }else{
            if (readbuf)
                printf("key value = %#X\r\n", readbuf);
        }
    }
    close(fd);
    return 0;
}

十一、测试

1、加载驱动

/ # ls
beep.ko        home           lib            newchrdev_app  tmp
beep_app       include        linuxrc        proc           usr
bin            key.ko         minicom.log    root           var
dev            key_app        mnt            sbin           video
drivers        led.ko         music          share
etc            led_app        newchrdev.ko   sys
/ # insmod key.ko
newchrdev major=248,minor=0
beep node find!
key num = 18
/ # rmmod key.ko
/ # insmod key.ko
newchrdev major=248,minor=0
beep node find!
key num = 18
/ # rmmod key.ko
/ # insmod key.ko
newchrdev major=248,minor=0
beep node find!
key num = 18
/ # rmmod key.ko

2、确定中断是否注册成功

/ # ls
beep.ko        home           lib            newchrdev_app  tmp
beep_app       include        linuxrc        proc           usr
bin            key.ko         minicom.log    root           var
dev            key_app        mnt            sbin           video
drivers        led.ko         music          share
etc            led_app        newchrdev.ko   sys
/ # cat /proc/interrupts
           CPU0
 16:       2578       GPC  55 Level     i.MX Timer Tick
 18:          0       GPC  33 Level     2010000.ecspi
 19:        769       GPC  26 Level     2020000.serial
 20:          0       GPC  98 Level     sai
 21:          0       GPC  50 Level     2034000.asrc
 37:          0  gpio-mxc   9 Edge      edt-ft5426
 47:          0  gpio-mxc  19 Edge      2190000.usdhc cd
196:          0       GPC   4 Level     20cc000.snvs:snvs-powerkey
197:       9778       GPC 120 Level     20b4000.ethernet
198:          0       GPC 121 Level     20b4000.ethernet
199:          0       GPC  80 Level     20bc000.wdog
202:          0       GPC  49 Level     imx_thermal
207:          0       GPC  19 Level     rtc alarm
213:          0       GPC   2 Level     sdma
218:          0       GPC  43 Level     2184000.usb
219:         35       GPC  42 Level     2184200.usb
220:          0       GPC 118 Level     2188000.ethernet
221:          0       GPC 119 Level     2188000.ethernet
222:         34       GPC  22 Level     mmc0
223:         64       GPC  23 Level     mmc1
224:          1       GPC 100 Level     2198000.adc
225:          0       GPC  36 Level     21a0000.i2c
226:        196       GPC  37 Level     21a4000.i2c
229:          2       GPC   5 Level     21c8000.lcdif
232:          1       GPC 107 Level
233:          0       GPC  28 Level     21ec000.serial
IPI0:          0  CPU wakeup interrupts
IPI1:          0  Timer broadcast interrupts
IPI2:          0  Rescheduling interrupts
IPI3:          0  Function call interrupts
IPI4:          0  Single function call interrupts
IPI5:          0  CPU stop interrupts
IPI6:          0  IRQ work interrupts
IPI7:          0  completion interrupts
Err:          0
/ # insmod key.ko
newchrdev major=248,minor=0
beep node find!
key num = 18
/ # cat /proc/interrupts
           CPU0
 16:       2718       GPC  55 Level     i.MX Timer Tick
 18:          0       GPC  33 Level     2010000.ecspi
 19:        903       GPC  26 Level     2020000.serial
 20:          0       GPC  98 Level     sai
 21:          0       GPC  50 Level     2034000.asrc
 37:          0  gpio-mxc   9 Edge      edt-ft5426
 46:          0  gpio-mxc  18 Edge      KEY0
 47:          0  gpio-mxc  19 Edge      2190000.usdhc cd
196:          0       GPC   4 Level     20cc000.snvs:snvs-powerkey
197:       9851       GPC 120 Level     20b4000.ethernet
198:          0       GPC 121 Level     20b4000.ethernet
199:          0       GPC  80 Level     20bc000.wdog
202:          0       GPC  49 Level     imx_thermal
207:          0       GPC  19 Level     rtc alarm
213:          0       GPC   2 Level     sdma
218:          0       GPC  43 Level     2184000.usb
219:         35       GPC  42 Level     2184200.usb
220:          0       GPC 118 Level     2188000.ethernet
221:          0       GPC 119 Level     2188000.ethernet
222:         34       GPC  22 Level     mmc0
223:         64       GPC  23 Level     mmc1
224:          1       GPC 100 Level     2198000.adc
225:          0       GPC  36 Level     21a0000.i2c
226:        196       GPC  37 Level     21a4000.i2c
229:          2       GPC   5 Level     21c8000.lcdif
232:          1       GPC 107 Level
233:          0       GPC  28 Level     21ec000.serial
IPI0:          0  CPU wakeup interrupts
IPI1:          0  Timer broadcast interrupts
IPI2:          0  Rescheduling interrupts
IPI3:          0  Function call interrupts
IPI4:          0  Single function call interrupts
IPI5:          0  CPU stop interrupts
IPI6:          0  IRQ work interrupts
IPI7:          0  completion interrupts
Err:          0
/ #

通过以上日志可以确定,按键中断加载成功后会出现 46: 0 gpio-mxc 18 Edge KEY0 行。

3、测试驱动程序和应用程序

/ # ls
beep.ko        home           lib            newchrdev_app  tmp
beep_app       include        linuxrc        proc           usr
bin            key.ko         minicom.log    root           var
dev            key_app        mnt            sbin           video
drivers        led.ko         music          share
etc            led_app        newchrdev.ko   sys
/ # insmod key.ko
newchrdev major=248,minor=0
beep node find!
key num = 18
/ # ls -l /dev/key
crw-rw----    1 root     0         248,   0 Jan  1 10:46 /dev/key
/ # rmmod key.ko
/ # ls -l /dev/key
ls: /dev/key: No such file or directory
/ # insmod key.ko
newchrdev major=248,minor=0
beep node find!
key num = 18
/ # ls -l /dev/key
crw-rw----    1 root     0         248,   0 Jan  1 10:46 /dev/key
/ # ./key_app /dev/key
newchrdev_open!
sizeof(readbuf) = 1
key0_handler
key_work
key0_handler
key_work
key_timer_function
key value = 0X1
key0_handler
key_work
key0_handler
key0_handler
key_work
key_timer_function
key value = 0X1
key0_handler
key_work
key0_handler
key0_handler
key_work
key_timer_function
key value = 0X1
^Cnewchrdev_release!

/ # rmmod key.ko
/ # ls -l /dev/key
ls: /dev/key: No such file or directory
/ #

十二、特别说明

在驱动相关接口中一定要处理异常情况。如果在驱动接口中缺少异常相关处理,会出现未定义现象,极难调试。

记一次失败经历:应用程序读取按键值时,未对 newchrdev_read 进行异常处理,出现以下现象:

  • 应用程序中 read 始终返回 0,无法读取到数据。
  • 应用程序中 read 始终可以读取到数据,但驱动中未向应用程序传输数据。

相关文章:

  • typename 的用法
  • java题2
  • 【Python深度学习】深度学习框架搭建模版
  • 双绞线连接网卡和集线器时的制作步骤
  • vue实战-mockjs模拟数据
  • 深度学习优化算法之动量法[公式推导](MXNet)
  • tomcat面试和Spring的面试题
  • 网课查题公众号接口
  • 基于Hive的搜狗搜索日志与结果Python可视化设计
  • vue+echarts项目四:地区销量趋势(堆叠折线图)
  • SpringSecurity实战-第6-8章
  • 前端 基础知识
  • 【极客时间2】左耳听风
  • 六级高频词汇——Group07
  • C++类和对象详解(中篇)
  • 08.Android之View事件问题
  • 4个实用的微服务测试策略
  • js作用域和this的理解
  • laravel5.5 视图共享数据
  • Linux链接文件
  • MYSQL 的 IF 函数
  • php中curl和soap方式请求服务超时问题
  • REST架构的思考
  • Spring技术内幕笔记(2):Spring MVC 与 Web
  • 多线程 start 和 run 方法到底有什么区别?
  • 记录一下第一次使用npm
  • 理解IaaS, PaaS, SaaS等云模型 (Cloud Models)
  • 面试遇到的一些题
  • 那些年我们用过的显示性能指标
  • 区块链共识机制优缺点对比都是什么
  • 如何优雅的使用vue+Dcloud(Hbuild)开发混合app
  • 设计模式走一遍---观察者模式
  • 世界上最简单的无等待算法(getAndIncrement)
  • 温故知新之javascript面向对象
  • 小程序01:wepy框架整合iview webapp UI
  • 运行时添加log4j2的appender
  • Semaphore
  • 正则表达式-基础知识Review
  • ​学习一下,什么是预包装食品?​
  • #{}和${}的区别是什么 -- java面试
  • #HarmonyOS:基础语法
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • #我与Java虚拟机的故事#连载19:等我技术变强了,我会去看你的 ​
  • (11)MSP430F5529 定时器B
  • (附源码)计算机毕业设计SSM疫情居家隔离服务系统
  • (九)One-Wire总线-DS18B20
  • (七)Knockout 创建自定义绑定
  • (三)uboot源码分析
  • .[hudsonL@cock.li].mkp勒索加密数据库完美恢复---惜分飞
  • .aanva
  • .equal()和==的区别 怎样判断字符串为空问题: Illegal invoke-super to void nio.file.AccessDeniedException
  • .NET 使用 ILMerge 合并多个程序集,避免引入额外的依赖
  • .NET 中创建支持集合初始化器的类型
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)