这几天又回到了驱动程序学习的轨道上来。还是从简单的硬件开始一步步的来学习驱动。使用TQ2440开发板上的按键来设计按键驱动程序。
★关于按键的结构体
按键的结构体主要有:对应的中断号,中断触发方式,按键名称
struct button_irq_desc
{
int irq; //中断号
unsigned long flags;//中断触发方式
char *name;//中断名称
};
struct button_irq_desc button[4] =
{
{IRQ_EINT1,IRQF_TRIGGER_FALLING,"KEY1"},
{IRQ_EINT4,IRQF_TRIGGER_FALLING,"KEY2"},
{IRQ_EINT2,IRQF_TRIGGER_FALLING,"KEY3"},
{IRQ_EINT0,IRQF_TRIGGER_FALLING,"KEY4"},
};
★关于等待队列
在本驱动程序中,等待队列可以休眠或者唤醒进程。首先要定义和初始化一个等待队列,使用宏定义
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
另外还有其他定义初始化等待队列的方式:
定义等待队列button_waitq:wait_queue_head_t button_waitq;
初始化等待队列button_waitq:init_waitqueue_head(&button_wait);
休眠进程:
wait_event(queue,condition);
condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_UNINTERRUPTIBLE模式的睡眠,并挂在queue参数所指定的等待队列上。
wait_event_interrupt(queue,condition);
condition(布尔表达式)为真时,立即返回 ;否则让进程进入TASK_INTERRUPTIBLE模式睡眠,并挂在queue参数所指定的等待队列上。
wait_event_killable(queue,condition);
condition(布尔表达式)为真时,立即返回;否则让进程进入TASK_KILLABLE的睡眠,并挂在queue参数所指定的等待队列上。
★关于模块初始化与退出函数
◇模块初始化函数实现的步骤
1)注册设备号
2)注册字符设备驱动
3)创建类实现自动创建设备文件
button_class = class_create(THIS_MODULE, "Button_char_driver");
if(IS_ERR(button_class))
{
printk("failed ^^^^^^^^^");
return -1;
}
device_create(button_class,NULL, dev,NULL,"Button_char_driver");
◇模块退出函数实现步骤
退出模块就是把上面完成的任务全部注销掉
cdev_del(&button_cdev);
device_destroy(button_class, MKDEV(Button_major, 0));
class_destroy(button_class);
unregister_chrdev_region(MKDEV(Button_major, 0), 1);
★关于Button_open函数
此函数内主要实现了中断函数的注册以及释放
err=request_irq(button[i].irq, Button_interrupt, button[i].flags, button[i].name, (void *)&Button_press[i]);
第一个参数为中断号;第二个参数为中断函数;第三个参数为中断触发标志;第四个参数为中断名;第五个参数为不固定的传入的值(在这里表示存储按键被按下的次数)
对于第三个参数中断触发标志有这么几种触发标志:低电平触发、高电平触发、上升沿触发、下降沿触发等。定义触发方式的头文件在include/linux/interrupt.h下
#define IRQF_TRIGGER_NONE 0x00000000
#define IRQF_TRIGGER_RISING 0x00000001
#define IRQF_TRIGGER_FALLING 0x00000002
#define IRQF_TRIGGER_HIGH 0x00000004
#define IRQF_TRIGGER_LOW 0x00000008
#define IRQF_TRIGGER_MASK (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW | \
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)
#define IRQF_TRIGGER_PROBE 0x00000010
free_irq(button[i].irq, (void *)&Button_press[i]);
释放中断函数
★关于Button_read函数
此函数的作用是记录按键陪按下的次数,然后将这个记录从内核空间复制到用户空间
static int Button_read(struct file *file,char __user *buff,size_t count,loff_t *offp)
{
printk("Button_read \n");
#if 1
unsigned long err;
printk("process is stopped\n");
wait_event_interruptible(button_waitq, en_press);
en_press = 0;
err = copy_to_user(buff, (const void *)Button_press, min(sizeof(Button_press),count));
//将内核空间数据拷贝到用户空间
printk("copy_to_user return value = %u\n",err);
memset((void *)Button_press,0,sizeof(Button_press));
return err ? -EFAULT : 0;
#endif
}
如果按键没有被按下,调用此驱动函数的进程就会被wait_event_interruptible阻塞,因为这时候en_press=0满足被阻塞的条件;只有当按键被按下,中断产生en_press=1,唤醒进程,执行下面的程序。
copy_to_user是将内核空间的内容复制到用户空间;对应的程序是copy_from_user是将用户空间的内容复制到内核空间。
★关于中断处理函数
static irqreturn_t Button_interrupt(int irq,void *dev_id)
{
volatile int *press_cnt = (volatile int *)dev_id;
*press_cnt = *press_cnt +1;
en_press = 1;
wake_up_interruptible(&button_waitq);
return IRQ_RETVAL(IRQ_HANDLED);
}
中断处理函数主要是这个返回值,总共有三种
enum irqreturn
@IRQ_NONE interrupt was not from this device
@IRQ_HANDLED interrupt was handled by this device
@IRQ_WAKE_THREAD handler requests to wake the handler thread
enum irqreturn {
IRQ_NONE,
IRQ_HANDLED,
IRQ_WAKE_THREAD,
};
#include
#include
#include
#include
#include //关于中断号的头文件
#include
#include
#include //copy_to_user copy_from_user
#define BUTTON_MAJOR 0
static int Button_major = 0;
static int en_press = 0;
struct cdev button_cdev;
struct button_irq_desc
{
int irq; //中断号
unsigned long flags;//中断触发方式
char *name;//中断名称
};
struct button_irq_desc button[4] =
{
{IRQ_EINT1,IRQF_TRIGGER_FALLING,"KEY1"},
{IRQ_EINT4,IRQF_TRIGGER_FALLING,"KEY2"},
{IRQ_EINT2,IRQF_TRIGGER_FALLING,"KEY3"},
{IRQ_EINT0,IRQF_TRIGGER_FALLING,"KEY4"},
};
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
static volatile int Button_press[]= {0,0,0,0};
/**中断处理函数**/
static irqreturn_t Button_interrupt(int irq,void *dev_id)
{
volatile int *press_cnt = (volatile int *)dev_id;
*press_cnt = *press_cnt +1;
en_press = 1;
wake_up_interruptible(&button_waitq);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int Button_open(struct inode *inode, struct file *file)
{
int i,err;
for(i = 0;i < 4;i++)
{
err=request_irq(button[i].irq, Button_interrupt, button[i].flags, button[i].name, (void *)&Button_press[i]);
if(err)
break;
}
if(err)
{
for(;i>0;i--)
free_irq(button[i].irq, (void *)&Button_press[i]);
return -EBUSY;
}
return 0;
}
static int Button_read(struct file *file,char __user *buff,size_t count,loff_t *offp)
{
printk("Button_read \n");
#if 1
unsigned long err;
printk("process is stopped\n");
wait_event_interruptible(button_waitq, en_press);
en_press = 0;
err = copy_to_user(buff, (const void *)Button_press, min(sizeof(Button_press),count));
//将内核空间数据拷贝到用户空间
printk("copy_to_user return value = %u\n",err);
memset((void *)Button_press,0,sizeof(Button_press));
return err ? -EFAULT : 0;
#endif
}
static int Button_close(struct inode *inode,struct file *file)
{
int i;
for(i = 0;i < 4;i++)
{
free_irq(button[i].irq, (void *)&Button_press[i]);
}
return 0;
}
struct file_operations char_operation =
{
.owner = THIS_MODULE,
.open = Button_open,
.read = Button_read,
.release = Button_close,
};
static struct class *button_class;
static int __init button_init(void)
{
int result,err;
dev_t dev = MKDEV(BUTTON_MAJOR, 0);
if(BUTTON_MAJOR)
{
result = register_chrdev_region(dev, 1, "Button");
}
else
{
result = alloc_chrdev_region(&dev, 0, 1, "Button");
Button_major = MAJOR(dev);
}
if(result < 0)
return result;
cdev_init(&button_cdev, &char_operation);
button_cdev.owner = THIS_MODULE;
err = cdev_add(&button_cdev,dev, 1);
if(err)
printk(KERN_NOTICE "Error %d adding globalmem",err);
#if 1
button_class = class_create(THIS_MODULE, "Button_char_driver");
if(IS_ERR(button_class))
{
printk("failed ^^^^^^^^^");
return -1;
}
device_create(button_class,NULL, dev,NULL,"Button_char_driver");
#endif
return 0;
}
static void __exit button_exit(void)
{
cdev_del(&button_cdev);
device_destroy(button_class, MKDEV(Button_major, 0));
class_destroy(button_class);
unregister_chrdev_region(MKDEV(Button_major, 0), 1);
}
MODULE_LICENSE("Dual BSD/GPL");
module_init(button_init);
module_exit(button_exit);