目前下面的程序在卸载时会出现问题,原因有待查找,问题出现在input_unregister_device(),程序如下:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/gpio.h>
#include <asm/irq.h>
#include <linux/sched.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/timer.h>
#include <linux/poll.h>
#include <linux/fcntl.h>
#include <linux/input.h>
#define DRVNAME "s5pv210_gpio"
struct led_device_ops{
struct input_dev *input;
wait_queue_head_t my_waitqueue;
struct timer_list timer; /* statistic timer */
int irq;
unsigned long virt;
};
static struct led_device_ops led_info;
static uint32_t last_timer;
static volatile int press = 0;
struct pin_desc{
unsigned char key_val;
};
static unsigned char witch_key = 0;
static struct pin_desc key_descs[8] ={
[0] = {
.key_val = KEY_1,
},
[1] = {
.key_val = KEY_2,
},
[2] = {
.key_val = KEY_3,
},
[3] = {
.key_val = KEY_4,
},
[4] = {
.key_val = KEY_5,
},
[5] = {
.key_val = KEY_6,
},
[6] = {
.key_val = KEY_7,
},
[7] = {
.key_val = KEY_8,
},
};
volatile unsigned long *GPC0CON, *GPC0DAT;//用与存放两个个寄存器的地址
volatile unsigned long *GPH0CON, *GPH0DAT;//按键
static unsigned char KeyFlag;
static void all_leds_off(void);
static void led_config(void)
{
volatile unsigned long phys;//用于存放虚拟地址和物理地址
phys = 0xE0200060;
led_info.virt =(unsigned long)ioremap(phys, 0xf00);
GPC0CON = (unsigned long *)(led_info.virt + 0x00);//指定需要操作的三个寄存器的地址
GPC0DAT = (unsigned long *)(led_info.virt + 0x04);
GPH0CON = (unsigned long *)(led_info.virt + 0xc00-0x60); //keY1配置为输入
GPH0DAT = (unsigned long *)(led_info.virt + 0xc00-0x60+0x04);
*GPC0CON &= ~(0xFF << 12);
*GPC0CON |= 0x11 << 12; // 配置GPC0_3和GPC0_4为输出
*GPH0CON &= ~0x0F; //配置为输入
all_leds_off();
}
static void led1_on(void)
{
*GPC0DAT |= 1 << 3;
*GPC0DAT &= ~(0x01 << 4);
// printk("led1 light\n");
}
static void led2_on(void)
{
*GPC0DAT |= 1 << 4;
*GPC0DAT &= ~(0x01 << 3);
// printk("led2 light\n");
}
static void all_leds_on(void)
{
*GPC0DAT |= 1 << 3; // 点亮LED1
*GPC0DAT |= 1 << 4; // 点亮LED2
printk("all leds light\n");
}
static void all_leds_off(void)
{
*GPC0DAT &= ~(0x3 << 3); // 熄灭LED1和LED2
printk("all leds off\n");
}
static void led_timer_event(unsigned long arg)
{
press = 1;
KeyFlag = !KeyFlag;
if(KeyFlag)
{
led1_on();
}
else
{
led2_on();
}
printk("Timer event time = %ld\n",jiffies-last_timer);
input_event(led_info.input, EV_KEY, witch_key, 0);
input_sync(led_info.input);
}
static irqreturn_t led_handler(int irq, void *devid)
{
struct pin_desc * pindesc = (struct pin_desc *)devid;
mod_timer(&led_info.timer,jiffies+HZ); //这里设置定时中断程序多久之后执行,可适当变短些,
last_timer=jiffies; //这里为了使程序实验效果更加明显,直接用1s
witch_key = pindesc->key_val;
printk("KERNEL:irq == %d witch_key= %d\n",irq,witch_key);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int led_device_open(struct inode *inode, struct file *file)
{
printk("led_drv_open\n");
file->private_data = &led_info;
return 0;
}
static int led_device_release(struct inode *inode, struct file *file)
{
return 0;
}
static const struct file_operations led_device_fileops = {
.owner = THIS_MODULE,
.open = led_device_open,
.release = led_device_release,
};
static int led_device_init(void)
{
int rc,i,error;
//struct led_device_ops *ddata;
//struct input_dev *input;
//ddata = kzalloc(sizeof(struct led_device_ops),GFP_KERNEL);
led_info.input = input_allocate_device();
if (!led_info.input) {
printk(KERN_ERR"failed to allocate state\n");
error = -ENOMEM;
goto fail1;
}
//ddata->input = input;
led_info.input->name = "key_device";
set_bit(EV_KEY, led_info.input->evbit);
set_bit(KEY_1, led_info.input->keybit);
set_bit(KEY_2, led_info.input->keybit);
set_bit(KEY_3, led_info.input->keybit);
set_bit(KEY_4, led_info.input->keybit);
set_bit(KEY_5, led_info.input->keybit);
set_bit(KEY_6, led_info.input->keybit);
set_bit(KEY_7, led_info.input->keybit);
set_bit(KEY_8, led_info.input->keybit);
//led_info.input = ddata->input;
error = input_register_device(led_info.input);
if (error) {
printk(KERN_ERR"Unable to register input device, error: %d\n",
error);
goto fail2;
}
input_sync(led_info.input);
for(i=0;i<5;i++)
{
rc = request_irq(IRQ_EINT(i), &led_handler, IRQ_TYPE_EDGE_FALLING,"led_irq", &key_descs[i]);
if(rc)
{
printk(KERN_ERR"function request_irq(%d) excute error\n",i);
goto led_request_irq_error;
}
}
init_timer(&led_info.timer);
led_info.timer.function = led_timer_event;
add_timer(&led_info.timer);
led_config();
all_leds_off();
return 0; /* succeed */
led_request_irq_error:
fail2:
fail1:
input_free_device(led_info.input);
return error;
}
static void led_device_exit(void)
{
iounmap((void *)led_info.virt); //撤销映射关系
free_irq(IRQ_EINT(0), &key_descs[0]);
free_irq(IRQ_EINT(1), &key_descs[1]);
free_irq(IRQ_EINT(2), &key_descs[2]);
free_irq(IRQ_EINT(3), &key_descs[3]);
free_irq(IRQ_EINT(4), &key_descs[4]);
printk("led_drv_exit ....excute ok\n");
//all_leds_off();
printk("led_drv_exit ....excute ok0\n");
printk("led_drv_exit ....excute ok1\n");
printk("led_drv_exit ....excute ok2\n");
input_free_device(led_info.input);
printk("led_drv_exit ....excute ok3\n");
input_unregister_device(led_info.input); //此处有段错误出现
printk("led_drv_exit ....excute ok4\n");
}
module_init(led_device_init);
module_exit(led_device_exit);
MODULE_AUTHOR("Ethyn blog:http://blog.csdn.net/humanspider1");
MODULE_DESCRIPTION("cortex a8 S5pv210 led test Driver");