#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPBCON 0x56000010
#define GPBDAT 0x56000014
#define GPBUP 0x56000018
#define DEVICE_NAME "my_irq_test"
volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
volatile unsigned long *gpfup = NULL;
unsigned int my_major = 0;
unsigned int my_minor = 0;
unsigned int baseminor = 0;
unsigned int wait_count = 0;
char *my_name = "leds_test";
dev_t dev;
struct cdev *cdev;
char key_value;
int print_key_value;
/*定义并初始化小任务队列*/
void my_do_tasklet(unsigned long);
DECLARE_TASKLET(my_tasklet, my_do_tasklet, 0);
/*初始化等待队列*/
DECLARE_WAIT_QUEUE_HEAD(my_queue);
/*定义定时器变量*/
struct timer_list my_timer;
int ret;
/*tasklet中断下半部处理函数:打印键值*/
void my_do_tasklet(unsigned long my)
{
printk("now key_value is %d\n", print_key_value);
}
/*设置定时器超时处理程序:1s后灭灯*/
static void timer_func(unsigned long data)
{
*gpfdat |= (0xF<<5);
}
/*中断上半部处理函数*/
static irqreturn_t my_interrupt(int irq, void *dev_id)
{
switch(irq)
{
case 17: //IRQ_EINT1
printk("--enter interrupt 1--\n");
key_value=0x1;
break;
case 18: //IRQ_EINT2
printk("--enter interrupt 2--\n");
key_value=0x2;
break;
case 19: //IRQ_EINT3
printk("--enter interrupt 3--\n");
key_value=0x3;
break;
case 48: //IRQ_EINT4
printk("--enter interrupt 4--\n");
key_value=0x4;
break;
defaule:
printk("%d\n",irq);
break;
}
print_key_value = (int)key_value;
wait_count= 1;
/* 唤醒休眠的进程 */
wake_up_interruptible(&my_queue);
/*调度my_tasklet函数*/
tasklet_schedule(&my_tasklet);
return IRQ_HANDLED;
}
/*设置为中断模式并下降沿触发 */
static void init_key_exint(void)
{
/*key configure, 1、2、3可以不用配置,因为本开发板已经实现*/
s3c2410_gpio_cfgpin(S3C2410_GPF1,S3C2410_GPF1_EINT1);
s3c2410_gpio_pullup(S3C2410_GPF1,1);
s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_EINT2);
s3c2410_gpio_pullup(S3C2410_GPF4,1);
s3c2410_gpio_cfgpin(S3C2410_GPF3,S3C2410_GPF3_EINT3);
s3c2410_gpio_pullup(S3C2410_GPF4,1);
s3c2410_gpio_cfgpin(S3C2410_GPF4,S3C2410_GPF4_EINT4);
s3c2410_gpio_pullup(S3C2410_GPF4,1);
/*EXINT1-4:falling edge triger*/
/*__raw_writel((__raw_readl(S3C2410_EXTINT0) &
(~(0xfff<<4)))|(0x492<<4), S3C2410_EXTINT0);
*/
//用以上的方法经常会出现异常情况,原因?
set_irq_type(IRQ_EINT1 , IRQ_TYPE_EDGE_FALLING);
set_irq_type(IRQ_EINT2 , IRQ_TYPE_EDGE_FALLING);
set_irq_type(IRQ_EINT3 , IRQ_TYPE_EDGE_FALLING);
set_irq_type(IRQ_EINT4 , IRQ_TYPE_EDGE_FALLING);
}
static int chr_open(struct inode *inode,struct file *file)
{
init_key_exint();
if (request_irq(IRQ_EINT1, &my_interrupt, IRQF_DISABLED, DEVICE_NAME, NULL))
return -EAGAIN;
if (request_irq(IRQ_EINT2, &my_interrupt, IRQF_DISABLED, DEVICE_NAME, NULL))
return -EAGAIN;
if (request_irq(IRQ_EINT3, &my_interrupt, IRQF_DISABLED, DEVICE_NAME, NULL))
return -EAGAIN;
if (request_irq(IRQ_EINT4, &my_interrupt, IRQF_DISABLED, DEVICE_NAME, NULL))
return -EAGAIN;
return 0;
}
static int chr_close(struct inode *inode,struct file *file)
{
free_irq(IRQ_EINT1,NULL);
free_irq(IRQ_EINT2,NULL);
free_irq(IRQ_EINT3,NULL);
free_irq(IRQ_EINT4,NULL);
del_timer(&my_timer);
return 0;
}
static ssize_t chr_write(struct file *file, const int __user *user_buf, size_t count, loff_t *ofset)
{
printk("write finished\n");
return count;
}
static ssize_t chr_read(struct file *file,char __user *user_buf, size_t count, loff_t *ofset)
{
wait_count = 0;
if (count < 1024)
{
if (!wait_count)
{
if (file->f_flags & O_NONBLOCK)
{
return -EAGAIN;
}
else
{
/* 如果wait_count等于0,休眠 */
printk("wait for interrupt...\n");
wait_event_interruptible(my_queue, wait_count);
}
}
/* 执行到这里时,ev_press等于1,将它清0 */
wait_count = 0;
/* 将按键状态复制给用户,并清0 */
if(copy_to_user(user_buf,&key_value,count))
{
printk("copy to user fail \n");
return -EFAULT;
}
*ofset += count;
}
else
{
return -EINVAL;
}
return count;
}
static int test_chardev_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
switch(cmd)
{
case 1:
*gpfdat=(*gpfdat & ~(0xF<<5)) | (0xE<<5);
break;
case 2:
*gpfdat=(*gpfdat & ~(0xF<<5)) | (0xD<<5);
break;
case 3:
*gpfdat=(*gpfdat & ~(0xF<<5)) | (0xB<<5);
break;
case 4:
*gpfdat=(*gpfdat & ~(0xF<<5)) | (0x7<<5);
break;
default:
printk("ioctl is fail\n");
return -1;
}
/*初始化并启动定时器,完成下半部工作部署*/
init_timer(&my_timer);
my_timer.expires = jiffies + HZ;
my_timer.function = timer_func;
printk("1s letar led turn off\n");
/*激活定时器*/
add_timer(&my_timer);
return 0;
}
static struct file_operations f_ops = {
.owner = THIS_MODULE,
.open = chr_open,
.release = chr_close,
.write = chr_write,
.read = chr_read,
.ioctl = test_chardev_ioctl,
};
static int __init leds_drv_init(void)
{
request_mem_region(GPBCON,4,"GPBC");
request_mem_region(GPBDAT,4,"GPBD");
request_mem_region(GPBUP ,4,"GPBU");
/*IO内存映射*/
gpfcon = ioremap_nocache(GPBCON,4);
gpfdat = ioremap_nocache(GPBDAT,4);
gpfup = ioremap_nocache(GPBUP ,4);
/*对映射后的IO内存进行位操作*/
*gpfcon = (*gpfcon & ~(0xFF<<10)) | (0x55<<10); //配置GPB5-8为输出
*gpfdat = (*gpfdat & ~(0xF<<5)) | (0xF<<5); //配置GPB5-8输出 1
*gpfup = (*gpfup & ~(0xF<<5)) | (0xF<<5); //配置GPB5-8 上拉电阻禁止
/*分配设备编号*/
if (my_major)
{
dev = MKDEV(my_major, my_minor);
register_chrdev_region(dev, 1, my_name);
}
else
{
alloc_chrdev_region(&dev, baseminor, 1, my_name);
}
/*注册字符设备*/
cdev = cdev_alloc();
cdev_init(cdev, &f_ops);
cdev->owner = THIS_MODULE;
cdev_add(cdev, dev, 1);
return 0;
}
static void __exit leds_drv_exit(void)
{
unregister_chrdev_region(dev, 1);
cdev_del(cdev);
/*IO内存释放*/
release_mem_region(GPBCON,4);
release_mem_region(GPBDAT,4);
release_mem_region(GPBUP,4);
/*IO内存解除映射*/
iounmap(gpfcon);
iounmap(gpfdat);
iounmap(gpfup);
}
module_init(leds_drv_init);
module_exit(leds_drv_exit);
MODULE_LICENSE("GPL");
通过前两天学习了设备模型(新内核的一种重要的机制),故可将以上程序改为如下(程序已在S3C2440开发板运行过):
/*
*按键中断,点亮LED,打印键值,其中涉及到中断、小任务队列,内核定时器,platform平台等内容
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "my_dev"
#define GPBCON 0x56000010
#define GPBDAT 0x56000014
#define GPBUP 0x56000018
volatile unsigned long *gpbc = NULL;
volatile unsigned long *gpbd = NULL;
volatile unsigned long *gpbu = NULL;
static unsigned my_major = 0;
static unsigned my_minor = 0;
static struct cdev *test_cdev;
static dev_t dev;
int print_key_value;
int key_value;
int wait_count;
int result;
/*初始化小任务队列*/
void my_do_tasklet(unsigned long my);
DECLARE_TASKLET(my_tasklet, my_do_tasklet, 0);
/*初始化等待队列*/
DECLARE_WAIT_QUEUE_HEAD(my_queue);
/*定义定时器变量*/
struct timer_list my_timer;
/*设置小任务队列函数作为中断下半部处理:打印键值*/
void my_do_tasklet(unsigned long my)
{
printk("deal to my_tasklet\n");
printk("key_value is %d\n", print_key_value);
}
/*设置定时器处理函数:1秒后让灯灭*/
static void timer_fuc(unsigned long data)
{
printk("time out\n");
*gpbd |= (0xF<<5);
}
/*中断上部处理函数*/
static irqreturn_t interrupt_handler(int irq, void * dev_id)
{
switch(irq)
{
case 17: //IRQ_EINT1
printk("--enter interrupt 1--\n");
key_value=0x1;
break;
case 18: //IRQ_EINT2
printk("--enter interrupt 2--\n");
key_value=0x2;
break;
case 19: //IRQ_EINT3
printk("--enter interrupt 3--\n");
key_value=0x3;
break;
case 48: //IRQ_EINT4
printk("--enter interrupt 4--\n");
key_value=0x4;
break;
default:
printk("irq wrong\n");
break;
}
print_key_value = (int)key_value;
wait_count = 1;
/*唤醒休眠的进程*/
wake_up_interruptible(&my_queue);
/*调度小任务队列*/
tasklet_schedule(&my_tasklet);
return IRQ_HANDLED;
}
/*按键初始化*/
static void init_key_exint(void)
{
/*key configure*/
s3c2410_gpio_cfgpin(S3C2410_GPF1,S3C2410_GPF1_EINT1);
s3c2410_gpio_pullup(S3C2410_GPF1, 1);
s3c2410_gpio_cfgpin(S3C2410_GPF2,S3C2410_GPF2_EINT2);
s3c2410_gpio_pullup(S3C2410_GPF2, 1);
s3c2410_gpio_cfgpin(S3C2410_GPF3,S3C2410_GPF3_EINT3);
s3c2410_gpio_pullup(S3C2410_GPF3, 1);
s3c2410_gpio_cfgpin(S3C2410_GPF4,S3C2410_GPF4_EINT4);
s3c2410_gpio_pullup(S3C2410_GPF4, 1);
/*EXINT1-4:falling edge triger*/
set_irq_type(IRQ_EINT1 , IRQ_TYPE_EDGE_FALLING);
set_irq_type(IRQ_EINT2 , IRQ_TYPE_EDGE_FALLING);
set_irq_type(IRQ_EINT3 , IRQ_TYPE_EDGE_FALLING);
set_irq_type(IRQ_EINT4 , IRQ_TYPE_EDGE_FALLING);
}
static int my_chr_file_open(struct inode *inode, struct file *file)
{
//按键初始化
init_key_exint();
//中断申请
if (request_irq(IRQ_EINT1, &interrupt_handler, IRQF_DISABLED, "my_irq", NULL))
return -EAGAIN;
if (request_irq(IRQ_EINT2, &interrupt_handler, IRQF_DISABLED, "my_irq", NULL))
return -EAGAIN;
if (request_irq(IRQ_EINT3, &interrupt_handler, IRQF_DISABLED, "my_irq", NULL))
return -EAGAIN;
if (request_irq(IRQ_EINT4, &interrupt_handler, IRQF_DISABLED, "my_irq", NULL))
return -EAGAIN;
//返回
return 0;
}
static void my_chr_file_release(struct inode *inode, struct file *file)
{
//中断释放
free_irq(IRQ_EINT1,NULL);
free_irq(IRQ_EINT2,NULL);
free_irq(IRQ_EINT3,NULL);
free_irq(IRQ_EINT4,NULL);
//定时器释放
del_timer(&my_timer);
}
static ssize_t my_chr_file_read(struct file *file, const int __user *user_buf, size_t count, loff_t *ofset)
{
wait_count = 0;
if (count < 1024)
{
if (!wait_count)
{
if (file->f_flags & O_NONBLOCK)
{
return -EAGAIN;
}
else
{
printk("wait for interrupt.....\n");
wait_event_interruptible(my_queue, wait_count);
}
}
/*被唤醒后执行到此,此时将wait_count清0*/
wait_count = 0;
if (copy_to_user(user_buf, &key_value, count))
{
printk("copy to user fail \n");
return -EFAULT;
}
*ofset += count;
}
else
{
return -EINVAL;
}
return count;
}
static ssize_t my_chr_file_write(struct file *file, const int __user *user_buf, size_t count, loff_t *ofset)
{
printk("write finish\n");
return 0;
}
static int my_chr_file_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long arg)
{
switch (cmd)
{
case 1:
*gpbd =(*gpbd & ~(0xF<<5)) | (0xE<<5);
break;
case 2:
*gpbd =(*gpbd & ~(0xF<<5)) | (0xD<<5);
break;
case 3:
*gpbd =(*gpbd & ~(0xF<<5)) | (0xB<<5);
break;
case 4:
*gpbd =(*gpbd & ~(0xF<<5)) | (0x7<<5);
break;
default:
printk("ioctl fail\n");
break;
}
/*初始化并启动定时器,完成下半部工作部署*/
init_timer(&my_timer);
my_timer.expires = jiffies + HZ;
my_timer.function = timer_fuc;
add_timer(&my_timer); //激活定时器
return 0;
}
/*文件操作结构体定义*/
static struct file_operations f_ops = {
.owner = THIS_MODULE,
.open = my_chr_file_open,
.release = my_chr_file_release,
.read = my_chr_file_read,
.write = my_chr_file_write,
.ioctl = my_chr_file_ioctl,
};
static void my_dev_release(struct device *dev)
{
printk("goodby my_dev release\n");
}
/*平台设备结构体定义*/
static struct platform_device my_chr_device = {
.name = "my_chr_dev",
.id = -1,
.dev = {
.release = my_dev_release,
},
};
EXPORT_SYMBOL(my_chr_device); //声明一个内核的全局符号my_dev_device
/*设备探测*/
static int __devinit my_dev_probe(struct platform_device *pdev)
{
//分配设备号
if (my_major)
{
dev = MKDEV(my_major, my_minor);
register_chrdev_region(dev, 1, DEVICE_NAME);
}
else
{
result= alloc_chrdev_region(&dev, my_minor, 1, DEVICE_NAME);
my_major = MAJOR(dev);
}
if (result < 0)
{
printk(KERN_WARNING"my_dev: cannot get major %d \n",my_major);
return result;
}
//字符设备注册
test_cdev = cdev_alloc();
cdev_init(test_cdev, &f_ops);
test_cdev->owner = THIS_MODULE;
result = cdev_add(test_cdev, dev, 1);
if(result)
{
printk("<1>Error %d while register device!\n",result);
}
//IO内存申请
request_mem_region(GPBCON, 4, "GPBC");
request_mem_region(GPBDAT, 4, "GPBD");
request_mem_region(GPBUP, 4, "GPBU");
//IO内存映射
gpbc = ioremap_nocache(GPBCON, 4);
gpbd = ioremap_nocache(GPBDAT, 4);
gpbu = ioremap_nocache(GPBUP, 4);
//对映射后的IO进行位操作
*gpbc = (*gpbc & ~(0xFF<<10)) | (0x55<<10); //配置GPB5-8为输出
*gpbd = (*gpbd & ~(0xF<<5)) | (0xF<<5); //GPB5-8输出为1
*gpbu = (*gpbu & ~(0xF<<5)) | (0xF<<5); //禁止上拉
return 0;
}
static void __devexit my_dev_remove(struct platform_device *pdev)
{
//注销字符设备
unregister_chrdev_region(dev, 1);
cdev_del(test_cdev);
//IO内存释放
release_mem_region(GPBCON, 4);
release_mem_region(GPBDAT, 4);
release_mem_region(GPBUP, 4);
//IO去映射
iounmap(gpbc);
iounmap(gpbd);
iounmap(gpbu);
}
/*平台驱动结构体定义*/
static struct platform_driver my_chr_dev_driver = {
.probe = my_dev_probe, //驱动探测
.remove = __devexit_p(my_dev_remove), //驱动移除
.driver = {
.name = "my_chr_dev", //务必和平台设备的名字一样
.owner = THIS_MODULE,
},
};
EXPORT_SYMBOL(my_chr_dev_driver); //声明一个内核的全局符号my_dev_driver
static int __init my_chr_dev_init(void)
{
//平台设备注册
platform_device_register(&my_chr_device);
printk("go to my_dev_probe init\n");
//平台驱动注册
return platform_driver_register(&my_chr_dev_driver);
}
static void __exit my_chr_dev_exit(void)
{
//平台设备注销
platform_device_unregister(&my_chr_device);
printk("goodby my_dev probe cleanup\n");
//平台驱动注销
platform_driver_unregister(&my_chr_dev_driver);
}
module_init(my_chr_dev_init);
module_exit(my_chr_dev_exit);
MODULE_LICENSE("GPL");