按键驱动(等待队列+设备树+属性文件)

9 篇文章 0 订阅

吐舌头驱动:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/fs.h>
#include <linux/of.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h> 
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/device.h>

//--------------------------------------------------------------------------------
// 查看 exynos4412-origen.dts 
// --> #include "exynos4412.dtsi" 
// 查看 exynos4412.dtsi 
// --> #include "exynos4x12.dtsi" 
// 查看 exynos4x12.dtsi
// -->#include "exynos4.dtsi"
//	  #include "exynos4x12-pinctrl.dtsi"
// 查看 exynos4x12-pinctrl.dtsi
/* -->  gpx1: gpx1 {
			gpio-controller;
			#gpio-cells = <2>;

			interrupt-controller;
			interrupt-parent = <&gic>;
			interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
				         <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
			#interrupt-cells = <2>;
		};
	我们看到gpx1的中断控制父节点是gic(generic interrupt controller)
	查看 exynos4.dtsi --> #include "skeleton.dtsi"
	目标出现:------------------>
	gic: interrupt-controller@10490000 {
		compatible = "arm,cortex-a9-gic";
		#interrupt-cells = <3>;
		interrupt-controller;
		reg = <0x10490000 0x1000>, <0x10480000 0x100>;
	};
	查阅exynos4412手册-->
	0x1048_0000 GIC_controller
	0x1049_0000 GIC_distributor
	--------------------------------------------------------------------------------
*/
// 26 58 – EINT[10] External Interrupt
// 25 57 – EINT[9]  External Interrupt
/*DTS
	mykey{
		compatible = "btn2";
		interrupt-parent = <&gpx1>; // 父节点gpx1
		interrupts = <1 2>, <2 2>; // 锁定25, 26号中断<0 25 0>,<0 26 0>
	};
*/
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("key driver example!");

#define NAME  _IOW('K', 0, int)
#define VALU  _IOW('K', 1, int)

struct key{
	dev_t num;
	int count; 
	int irq_num;
	int key_val;
	int press_flag;
	spinlock_t lock;
	char key_name[10];
	struct cdev cdev_key;
	struct class *class;
	struct device *device;
	struct resource *res;
	struct timer_list btn_timer;
	wait_queue_head_t r_wq;
}btn;

void btnserver_func(unsigned long arg)
{
	spin_lock(&btn.lock);
	btn.press_flag = 1;
	spin_unlock(&btn.lock);

	wake_up_interruptible(&btn.r_wq); // 唤醒等待队列上的休眠进程
	printk("btn pressed: %d!\n", btn.count++);

}

irqreturn_t btn_int_handler(int n, void *dev)
{
	// 定时器去抖
	mod_timer(&btn.btn_timer, jiffies+50);

	return IRQ_HANDLED;
}

static int key_open(struct inode *inodp, struct file *filp)
{
	printk("key open call \n");	
	strcpy(btn.key_name, "xxdk");
	btn.key_val = 'A';

	return 0;
}
ssize_t key_read (struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
	if(!btn.press_flag) {
		wait_event_interruptible(btn.r_wq,  btn.press_flag != 0);
		printk("something happened\n");
	} 

	if(btn.press_flag){
		printk("report to user.\n");
		if(copy_to_user(buf, btn.key_name, 10)) {
			printk("copy to user fail1\n");
			return -EINVAL;
		}
		spin_lock(&btn.lock);
		btn.press_flag = 0;
		spin_unlock(&btn.lock);
	}
	
	return 0;
}
static long key_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	char buf[10] = {0};
	int val = 0;
	int ret;
	switch(cmd)
	{
	// 重置按键名称
	case NAME:
		ret = copy_from_user((void*)buf, (void*)arg, strlen((char*)arg));
		if(ret){
			printk("copy from user fail1\n");
			return -EINVAL;
		}
		printk("set btn name: %s\n", buf);
		memset(btn.key_name, 0, 10);
		strcpy(btn.key_name, buf);
		break;
	// 配置按键键值
	case VALU:
		ret = copy_from_user((void*)&val, (void*)arg, 4);
		if(ret){
			printk("copy from user fail1\n");
			return -EINVAL;
		}
		printk("set btn valu: %d\n", val);
		btn.key_val = val;
		break;
	}

	return 0;
}

static int key_release(struct inode *inodep, struct file *filep)
{
	printk("btn closed\n");

	return 0;
}

struct file_operations key_ops = {
	.owner = THIS_MODULE,
	.open = key_open,
	.read = key_read,
	.unlocked_ioctl  = key_ioctl,
	.release = key_release
};

#ifdef CONFIG_OF

struct of_device_id key_table[] = {
	{ .compatible = "key" },
	{ }
};

#endif

int key_probe(struct platform_device *pdev);
int key_remove(struct platform_device *pdev);

struct platform_driver key_driver = {
	.probe = key_probe,
	.remove = key_remove,
	.driver = {
		.name = "key",
		.of_match_table = of_match_ptr(key_table)
	}
};

static ssize_t key_name_show(struct device_driver *driver, char *buf)
{
	printk("show btn name to user.\n");
	return scnprintf(buf, ARRAY_SIZE(btn.key_name), "%s\n", btn.key_name);
}
static ssize_t key_name_store(struct device_driver *driver, 
							  const char *buf, size_t count)
{
	
	printk("store btn from user: %s\n", buf);
	memset(btn.key_name, 0, ARRAY_SIZE(btn.key_name));
	strncpy(btn.key_name, buf, count);

	return count;
}
static DRIVER_ATTR(btnname, 0666, key_name_show, key_name_store);


int key_probe(struct platform_device *pdev)
{
	int ret = 0;
	printk("key probe !\n");
	//1. 配置定时器
	setup_timer(&btn.btn_timer, btnserver_func, 0);
	add_timer(&btn.btn_timer);

	//2. 解析中断号
	btn.res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	btn.irq_num = btn.res->start;
	printk("irqnum: %#x\n", btn.res->start);

	//3. 申请中断资源
	ret = request_irq(btn.irq_num, btn_int_handler, (btn.res->flags&IRQF_TRIGGER_MASK) | IRQF_DISABLED, "btn", NULL);

	//4. 申请设备号
	ret = alloc_chrdev_region(&btn.num, 0, 1, "btn");
	if(ret)
	{
		printk("devnum alloc fail!\n");
		return ret;
	}
	printk("num: %d\n", MAJOR(btn.num));

	//5. init cdev ops
	cdev_init(&btn.cdev_key,  &key_ops);
	btn.cdev_key.owner = THIS_MODULE;

	//register cdev into kernel
	ret = cdev_add(&btn.cdev_key, btn.num, 1);
	if(ret)
	{
		printk("add cdev fail!\n");
		goto cdev_add_out;
	}
	//6. 
	btn.class = class_create(THIS_MODULE, "btn");
	btn.device = device_create(btn.class, NULL, btn.num, NULL, "btn%d", 0);

	//7. 
	ret = driver_create_file(&key_driver.driver, &driver_attr_btnname);

	init_waitqueue_head(&btn.r_wq);
	spin_lock_init(&btn.lock);

	return 0;

cdev_add_out:
	unregister_chrdev_region(btn.num, 1);
	

	return ret;
}

int key_remove(struct platform_device *pdev)
{
	printk("key module release\n");

	driver_remove_file(&key_driver.driver, &driver_attr_btnname);
	del_timer(&btn.btn_timer);
	//unregister cdev from kernel
	cdev_del(&btn.cdev_key);

	//release dev num
	unregister_chrdev_region(btn.num, 1); 
	free_irq(btn.irq_num, NULL);

	device_del(btn.device);
	class_destroy(btn.class);

	return 0;
}

static int xkey_init(void)
{
	printk("module install\n");

	//add into platform bus
	platform_driver_register(&key_driver);

	return 0;
}

static void xkey_exit(void)
{
	printk("module release\n");
	//del from platform bus
	platform_driver_unregister(&key_driver);
}


module_init(xkey_init);
module_exit(xkey_exit);
吐舌头测试:

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

#define NAME  _IOW('K', 0, int)
#define VALU  _IOW('K', 1, int)
#define DEV_PATH "/dev/btn0"

int main(int argc, char **argv)
{
	int fd, val;
	char buf[10] = {0};

	fd = open(DEV_PATH, O_RDWR);
	if(-1 == fd) {
		perror("open");
		return -1;
	}
#if 0
	printf("please input btn name: ");
	scanf("%s", buf);
	ioctl(fd, NAME, buf);
	printf("please input btn valu: ");
	fflush(stdin);
	scanf("%d", &val);
	ioctl(fd, VALU, &val);
#endif
	while(1) {
//		sleep(1);
		printf("btn fetch process------------>\n");
		read(fd, buf, 10);
		printf("get button pressed: %s\n", buf);
	}

	close(fd);
	
	return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值