keys.c
/*
* linux-3.10.27
* arm-linux-gcc-4.5.1
*
* @ keys driver
*/
#include <linux/module.h>
#include <linux/init.h> /* module_init, ... */
#include <linux/kernel.h> /* everything */
#include <linux/cdev.h> /* cdev_init, ... */
#include <linux/fs.h> /* file_operations, */
#include <linux/device.h> /* class_create,... */
#include <linux/platform_device.h>
#include <linux/slab.h> /* kmalloc, ... */
#include <asm/io.h> /* ioremap,... */
#include <linux/uaccess.h> /* copy_from_user, ... */
#include <linux/irq.h> /* IRQ_EINT(x), ... */
#include <linux/gpio.h>
#include <linux/interrupt.h> /* irq_request(),IRQF_xxx, ... */
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/timer.h>
#define DEF_DEVICE_NAME "mykeys"
struct priv_data {
char *name ;
int major ;
dev_t dev;
struct cdev *cdev;
struct class *class;
};
/*
* GPIO OF KEY FOR SMART210
* K1 - GPH2-0 - EXINT16
* K2 - GPH2-1 - EXINT17
* K3 - GPH2-2 - EXINT18
* K4 - GPH2-3 - EXINT19
* K5 - GPH3-0 - EXINT24
* K6 - GPH3-1 - EXINT25
* K7 - GPH3-2 - EXINT26
* K8 - GPH3-3 - EXINT27
*
* LOW MEANS PRESSED
*
*/
struct key_dev {
char *name;
unsigned gpio;
int irq;
unsigned int flags;
int val;
};
static struct priv_data *priv_data;
#define IRQF_TRIGGER_BOTH_EDGE IRQF_TRIGGER_FALLING + IRQF_TRIGGER_RISING
static struct key_dev keys[] =
{
{"K1", S5PV210_GPH2(0), IRQ_EINT(16),IRQF_TRIGGER_BOTH_EDGE, 10},
{"K2", S5PV210_GPH2(1), IRQ_EINT(17),IRQF_TRIGGER_BOTH_EDGE, 20},
{"K3", S5PV210_GPH2(2), IRQ_EINT(18),IRQF_TRIGGER_BOTH_EDGE, 30},
{"K4", S5PV210_GPH2(3), IRQ_EINT(19),IRQF_TRIGGER_BOTH_EDGE, 40},
{"K5", S5PV210_GPH3(0), IRQ_EINT(24),IRQF_TRIGGER_BOTH_EDGE, 50},
{"K6", S5PV210_GPH3(1), IRQ_EINT(25),IRQF_TRIGGER_BOTH_EDGE, 60},
{"K7", S5PV210_GPH3(2), IRQ_EINT(26),IRQF_TRIGGER_BOTH_EDGE, 70},
{"K8", S5PV210_GPH3(3), IRQ_EINT(27),IRQF_TRIGGER_BOTH_EDGE, 80},
};
static DECLARE_WAIT_QUEUE_HEAD(keys_waitq);
static int pressed = 0;
static int key_val = 0;
static struct timer_list mytimer;
static struct key_dev *cur_key = NULL;
void mytimer_callback(unsigned long data)
{
if(!cur_key)
return;
key_val = cur_key->val + gpio_get_value(cur_key->gpio);
pressed = 1;
wake_up_interruptible(&keys_waitq);
}
irqreturn_t keys_handler(int irq, void *dev_id)
{
cur_key = (struct key_dev *)dev_id;
//pr_info("%s pressed.\n", key->name);
mod_timer(&mytimer, jiffies + HZ / 50);
return IRQC_IS_HARDIRQ;
}
static int keys_open (struct inode *inode, struct file *file)
{
pr_info("%s called .\n", __func__);
return 0;
}
static int keys_close (struct inode *inode, struct file *file)
{
pr_info("%s called .\n", __func__);
return 0;
}
static ssize_t keys_read (struct file *file, char __user *usrbuf, size_t len,
loff_t *offset)
{
wait_event_interruptible(keys_waitq, pressed);
if(copy_to_user(usrbuf, &key_val, 1)){
return -EFAULT;
}
/* clear the flag */
pressed = 0;
return 0;
}
const struct file_operations fops =
{
.owner = THIS_MODULE,
.open = keys_open,
.release = keys_close,
.read = keys_read,
};
static int keys_init(void)
{
int ret = 0, i = 0;
pr_info("%s called .\n", __func__);
priv_data = kmalloc(sizeof(*priv_data), GFP_KERNEL);
if(!priv_data){
pr_err("no mem.\n");
return -ENOMEM;
}
priv_data->name = DEF_DEVICE_NAME;
priv_data->major = 0;
if(priv_data->major){
priv_data->dev = MKDEV(priv_data->major, 0);
ret = register_chrdev_region(priv_data->dev, 1, priv_data->name );
}
else
{
ret = alloc_chrdev_region(&priv_data->dev, 1, 1, priv_data->name );
priv_data->major = MAJOR(priv_data->dev);
}
if(ret < 0){
pr_err("register chrdev err.\n");
kfree(priv_data);
return -EFAULT;
}
/* add cdev */
priv_data->cdev = cdev_alloc();
if(!priv_data->cdev){
pr_err("cdev alloc err.\n");
goto cdev_err;
}
cdev_init(priv_data->cdev, &fops);
priv_data->cdev->ops = &fops;
priv_data->cdev->owner = THIS_MODULE;
cdev_add(priv_data->cdev, priv_data->dev, 1);
/* add device */
priv_data->class = class_create(THIS_MODULE, priv_data->name);
if(!priv_data->class){
pr_err("class create err.\n");
goto cls_err;
}
device_create(priv_data->class, NULL, priv_data->dev, NULL, priv_data->name);
for(i = 0; i < ARRAY_SIZE(keys); i++){
if(request_irq(keys[i].irq, keys_handler, keys[i].flags,
keys[i].name, &keys[i]))
break;
}
init_timer(&mytimer);
mytimer.function = mytimer_callback;
add_timer(&mytimer);
return 0;
cls_err:
cdev_del(priv_data->cdev);
cdev_err:
unregister_chrdev_region(priv_data->dev, 1);
kfree(priv_data);
return -EFAULT;
}
static void keys_exit(void)
{
int i = 0;
pr_info("%s called .\n", __func__);
del_timer(&mytimer);
for(i = 0; i < ARRAY_SIZE(keys); i++)
{
free_irq(keys[i].irq, &keys[i]);
}
device_destroy(priv_data->class, priv_data->dev);
class_destroy(priv_data->class);
cdev_del(priv_data->cdev);
unregister_chrdev_region(priv_data->dev, 1);
kfree(priv_data);
}
module_init(keys_init);
module_exit(keys_exit);
MODULE_LICENSE("GPL");
测试程序如下:
keys_test.c
#include <stdio.h>
#include <fcntl.h>
int main(void){
int fd = -1;
unsigned char key_val = 0;
fd = open("/dev/mykeys", O_RDWR);
if(fd < 0){
printf("open err ! \n");
return -1;
}
while(1){
read(fd, &key_val, 1);
printf("key_val : %d \n", key_val);
}
return 0;
}
编译测试程序
#!/bin/bash
export PATH=$PATH:/home/flinn/tools/4.5.1/bin
arm-linux-gcc -o keys_test keys_test.c
sudo cp keys_test /home/flinn/smart210-SDK/fs/drv/