驱动:
#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;
}