#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/cdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <linux/poll.h>
struct str_key{
int major;
dev_t devid;
struct class *class;
struct device *device;
struct cdev cdev;
struct device_node *nd;
int key_gpio;
struct mutex lock;
int key_irq_num;
//int led_used;
wait_queue_head_t r_wait;
} key;
enum key_status {
KEY_PRESS = 0, // 按键按下
KEY_RELEASE, // 按键松开
KEY_KEEP, // 按键状态保持
};
static atomic_t status; /* 按键状态 */
irqreturn_t key_irq_func(int irq,void *dev_id)
{
//printk("kernel:key press,noblock\r\n");
//status=KEY_PRESS;
atomic_set(&status,KEY_PRESS);
wake_up_interruptible(&key.r_wait);
//wake_up_interruptible(&key.r_wait);
return IRQ_HANDLED;
}
int key_init_from_dts_using_gpio(void)
{
int ret;
const char *str;
int irq_flags;
/* 1.获取led设备节点 */
key.nd = of_find_node_by_path("/key");
if(NULL == key.nd) {
printk(KERN_ERR "key_gpio node can not found!\r\n");
return -1;
}
/* 2.读取status属性 */
ret = of_property_read_string(key.nd, "status", &str);
if(!ret) {
if (strcmp(str, "okay"))
return -1;
}
/* 2、获取compatible属性值并进行匹配 */
ret = of_property_read_string(key.nd, "compatible", &str);
if(0 > ret)
return -1;
if (strcmp(str, "xhl,key_gpio"))
return -1;
printk(KERN_ERR "key device matching successful!\r\n");
//led_ioremap();
//key.key_gpio=of_get_named_gpio(key.nd,"gpios",0);
key.key_gpio=of_get_gpio(key.nd,0);
if(key.key_gpio<0)
return -1;
ret=gpio_request(key.key_gpio,"key_gpio");
if(ret)
return -1;
printk("key_gpio=%d\r\n",key.key_gpio);
gpio_direction_input(key.key_gpio);
//中断处理
//1.申请中断号
key.key_irq_num=irq_of_parse_and_map(key.nd,0);
printk("key_irq_num=%d\r\n",key.key_irq_num);
//2.获取中断类型
irq_flags=irq_get_trigger_type(key.key_irq_num);
//3.申请中断
ret=request_irq(key.key_irq_num,key_irq_func,irq_flags,"key irq",NULL);
if(ret<0)
{
gpio_free(key.key_gpio);
return ret;
}
return ret;
}
static int mio_set(int value)
{
//u32 val;
//int ret;
if(value==0)
{
gpio_set_value(key.key_gpio,0);
}
else
{
gpio_set_value(key.key_gpio,1);
}
return 0;
}
#define KEY_NUM 1
#define KEY_NAME "key"
int key_open(struct inode *inode,struct file *file)
{
int ret=0;
/*unsigned long flags;
spin_lock_irqsave(&key.lock,flags);
if(key.led_used)
{
spin_unlock_irqrestore(&key.lock,flags);
return -1;
}
key.led_used=1;
spin_unlock_irqrestore(&key.lock,flags);
*/
//if(mutex_lock_interruptible(&key.lock))
//return -1;
printk("kernel:key_open\r\n");
return ret;
}
int key_close(struct inode *inode,struct file *file)
{
int ret=0;
/*unsigned long flags;
spin_lock_irqsave(&key.lock,flags);
if(key.led_used)
{
key.led_used=0;
//spin_unlock_irqrestore(&key.lock,flags);
//return -1;
}
spin_unlock_irqrestore(&key.lock,flags);
*/
//mutex_unlock(&key.lock);
//atomic_inc(&key.lock);
printk("kernel:key_close\r\n");
return ret;
}
ssize_t key_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
return 0;
}
ssize_t key_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos)
{
int ret=0;
//int read_buf=gpio_get_value(key.key_gpio);
//ret=wait_event_interruptible(key.r_wait,KEY_PRESS==atomic_read(&status));
if(ret)
return ret;
ret=copy_to_user(buf,&status,sizeof(int));
if(ret<0)
return -1;
atomic_set(&status,KEY_KEEP);
printk("kernel:key_read\r\n");
return ret;
}
static unsigned int key_poll(struct file *file,struct poll_table_struct *wait)
{
unsigned int mask = 0;
poll_wait(file, &key.r_wait, wait);
if(KEY_KEEP != atomic_read(&status)) // 按键按下或松开动作发生
mask = POLLIN | POLLRDNORM; // 返回PLLIN
return mask;
}
struct file_operations ops={
.owner =THIS_MODULE,
.open =key_open,
.write =key_write,
.read =key_read,
.release=key_close,
.poll =key_poll,
};
static int __init mykey_init(void)
{
int ret=0;
//1.设备号
key.major=0;
if(key.major)
{
key.devid=MKDEV(key.major,0);
ret=register_chrdev_region(key.devid,KEY_NUM,KEY_NAME);
}
else
{
ret=alloc_chrdev_region(&key.devid,0,KEY_NUM,KEY_NAME);
key.major=MAJOR(key.devid);
}
printk("major=%d\r\n",key.major);
//2.
key.class=class_create(THIS_MODULE,KEY_NAME);
if(IS_ERR(key.class))
{
printk("error:class_create\r\n");
ret=PTR_ERR(key.class);
goto error_class_create;
}
//3.
key.device=device_create(key.class,NULL,key.devid,NULL,KEY_NAME);
if(IS_ERR(key.device))
{
printk("error:device_create\r\n");
ret=PTR_ERR(key.device);
goto error_device_create;
}
printk("device_create ok\r\n");
//4.添加设备
cdev_init(&key.cdev,&ops);
ret=cdev_add(&key.cdev,key.devid,KEY_NUM);
if(ret<0)
{
printk("error:cdev_add\r\n");
goto error_cdev_add;
}
printk("cdev_add ok\r\n");
//mio_init();
key_init_from_dts_using_gpio();
//atomic_set(&key.lock,1);
//spin_lock_init(&key.lock);
mutex_init(&key.lock);
init_waitqueue_head(&key.r_wait);
atomic_set(&status,KEY_KEEP);
//key.led_used=0;
printk("key_init3\r\n");
return ret;
error_cdev_add:
device_destroy(key.class, key.devid);
error_device_create:
class_destroy(key.class);
error_class_create:
unregister_chrdev_region(key.devid,KEY_NUM);
return ret;
}
static void __exit mykey_exit(void)
{
/* 2.取消内存映射 */
/*iounmap(data_addr);
iounmap(dirm_addr);
iounmap(outen_addr);
iounmap(intdis_addr);
iounmap(aper_clk_ctrl_addr);
*/
free_irq(key.key_irq_num,NULL);
cdev_del(&key.cdev);
device_destroy(key.class, key.devid);
class_destroy(key.class);
unregister_chrdev_region(key.devid,KEY_NUM);
printk("key_exit22\r\n");
//return 0;
}
module_init(mykey_init);
module_exit(mykey_exit);
MODULE_LICENSE("GPL");