1. 程式人生 > >按鍵驅動程式

按鍵驅動程式

=============
混雜裝置
在Linux系統中,存在一類字元裝置,它們擁有
相同的主裝置號(10),但次裝置號不同,我們稱這類裝置為混雜裝置(miscdevice)。所有的
混雜裝置形成一個連結串列,對裝置訪問時核心根據次裝置號查詢到相應的混雜裝置。


1.裝置描述
Linux中使用struct miscdevice來描述一個混雜裝置。
struct miscdevice  {
int minor;   /* 次裝置號*/
const char *name; /* 裝置名*/
const struct file_operations *fops; /*檔案操作*/
struct list_head list; 
struct device *parent;
struct device *this_device;
};


初始化miscdevice


2.設備註冊
Linux中使用misc_register函式來註冊一個混雜設
備驅動。
int misc_register(struct miscdevice * misc)




中斷處理
中斷註冊
request_irq函式用於註冊中斷。
int request_irq(unsigned int irq, 
void (*handler)(int, void*, struct pt_regs *),   
unsigned long flags, 
const char *devname, 
void *dev_id)
返回0表示成功,或者返回一個錯誤碼


 unsigned int irq 中斷號。
void (*handler)(int,void *) 中斷處理函式。
unsigned long flags 與中斷管理有關的各種選項。
const char * devname 裝置名
void *dev_id 共享中斷時使用



ioremap將一個IO
地址空間
對映到核心的虛擬地址空間上去,便於訪問。
中文名
ioremap
size
要對映的空間的大小
flags
對映的IO空間的和許可權有關的標誌
phys_addr
是要對映的實體地址



#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>


#define GPFCON 0x56000050


irqreturn_t key_int1(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key1 down!\n");
    
    return 0;
   
}
irqreturn_t key_int2(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key2 down!\n");
    
    return 0;
   
}
irqreturn_t key_int3(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key3 down!\n");
    
    return 0;
   
}
irqreturn_t key_int4(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key4 down!\n");
    
    return 0;
   
}
irqreturn_t key_int5(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key5 down!\n");
    
    return 0;
   
}
irqreturn_t key_int6(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key6 down!\n");
    
    return 0;
   
}


void key_hw_init()
{
    unsigned short data; 
    unsigned int *gpio_config;
    
    gpio_config = ioremap(GPFCON,4);
    data = readw(gpio_config);
    data &= ~0b11;
    data |= 0b10;
    writew(data,gpio_config);
}




int key_open(struct inode *node,struct file *filp)
{
    return 0;
}


struct file_operations key_fops = 
{
    .open = key_open,
};


struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "2440key",
    .fops = &key_fops,
};


static int button_init()
{
    misc_register(&key_miscdev);
    
    //按鍵硬體初始化
    key_hw_init();
    
    //註冊中斷處理程式
    //request_irq(IRQ_EINT8,key_int1,IRQF_TRIGGER_MASK,"2440key",0);
//request_irq(IRQ_EINT8,key_int1,IRQF_TRIGGER_NONE,"2440key",0);
//request_irq(IRQ_EINT11,key_int2,IRQF_TRIGGER_FALLING,"2440key",0);
//request_irq(IRQ_EINT11,key_int2,IRQF_TRIGGER_PROBE,"2440key",0);
    //request_irq(IRQ_EINT13,key_int3,IRQF_TRIGGER_RISING,"2440key",0);
request_irq(IRQ_EINT14,key_int4,IRQF_TRIGGER_HIGH,"2440key",0);//gao buduandayin
    request_irq(IRQ_EINT15,key_int5,IRQF_TRIGGER_LOW,"2440key",0);
//request_irq(IRQ_EINT19,key_int6,IRQF_TRIGGER_PROBE,"2440key",0);
    
    return 0;
}




static void button_exit()
{
    misc_deregister(&key_miscdev);
}




module_init(button_init);
module_exit(button_exit);




============================================
中斷分層
-----------------------------------
#include <linux/init.h>
#include <linux/module.h>


struct workqueue_struct *my_wq;
struct work_struct *work1;
struct work_struct *work2;


MODULE_LICENSE("GPL");


void work1_func(struct work_struct *work)
{
    printk("this is work1->\n");
}


void work2_func(struct work_struct *work)
{
    printk("this is work2->\n");
}


int init_que(void)
{
    //1. 建立工作佇列
    my_wq = create_workqueue("my_que");
   
    //2. 建立工作
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work1, work1_func);
    
    //3. 掛載(提交)工作
    queue_work(my_wq,work1);
   
     
    //2. 建立工作
    work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work2, work2_func);
    
    //3. 掛載(提交)工作
    queue_work(my_wq,work2);
   
    return 0;
}


void clean_que()
{

}


module_init(init_que);
module_exit(clean_que);


------------------------------------
#include <linux/init.h>
#include <linux/module.h>


struct workqueue_struct *my_wq;
struct work_struct *work1;
struct work_struct *work2;


MODULE_LICENSE("GPL");


void work1_func(struct work_struct *work)
{
    printk("this is work1->\n");
}


void work2_func(struct work_struct *work)
{
    printk("this is work2->\n");
}


int init_que(void)
{
    //2. 建立工作
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work1, work1_func);
    
    //3. 掛載(提交)工作
    schedule_work(work1);
   
     
    //2. 建立工作
    work2 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work2, work2_func);
    
    //3. 掛載(提交)工作
    schedule_work(work2);
   
    return 0;
}


void clean_que()
{

}


module_init(init_que);
module_exit(clean_que);
-----------------------------------


#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/io.h>
MODULE_LICENSE("GPL");
#define GPFCON 0x56000050


struct work_struct *work1;
struct work_struct *work2;
struct work_struct *work3;
struct work_struct *work4;


void work1_func(struct work_struct *work)
{
    printk("key1 down this is work1->\n");
}
void work2_func(struct work_struct *work)
{
    printk("key2 down this is work2->\n");
}
void work3_func(struct work_struct *work)
{
    printk("key3 down this is work3->\n");
}
void work4_func(struct work_struct *work)
{
    printk("key4 down this is work4->\n");
}


irqreturn_t key_int1(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    //頻繁點選按鈕,列印的內容workqueue的少於key_init
    
    //3. 列印按鍵值
    printk("key1 down!\n");
    schedule_work(work1);
    return 0;
   
}
irqreturn_t key_int2(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key2 down!\n");
    schedule_work(work2);
    return 0;
   
}
irqreturn_t key_int3(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key3 down!\n");
    schedule_work(work3);
    return 0;
   
}
irqreturn_t key_int4(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key4 down!\n");
    schedule_work(work4);
    return 0;
   
}
irqreturn_t key_int5(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key5 down!\n");
    
    return 0;
   
}
irqreturn_t key_int6(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    
    //2. 清除已經發生的按鍵中斷
    
    
    //3. 列印按鍵值
    printk("key6 down!\n");
    
    return 0;
   
}


void key_hw_init()
{
    unsigned short data; 
    unsigned int *gpio_config;
    
    gpio_config = ioremap(GPFCON,4);
    data = readw(gpio_config);
    data &= ~0b11;
    data |= 0b10;
    writew(data,gpio_config);
}




int key_open(struct inode *node,struct file *filp)
{
    return 0;
}


struct file_operations key_fops = 
{
    .open = key_open,
};


struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "2440key",
    .fops = &key_fops,
};


static int button_init()
{
    misc_register(&key_miscdev);
    
    //按鍵硬體初始化
    key_hw_init();
    
    //註冊中斷處理程式
    request_irq(IRQ_EINT8,key_int1,IRQF_TRIGGER_FALLING,"2440key",0);
//request_irq(IRQ_EINT8,key_int1,IRQF_TRIGGER_NONE,"2440key",0);
request_irq(IRQ_EINT11,key_int2,IRQF_TRIGGER_FALLING,"2440key",0);
//request_irq(IRQ_EINT11,key_int2,IRQF_TRIGGER_PROBE,"2440key",0);
    request_irq(IRQ_EINT13,key_int3,IRQF_TRIGGER_RISING,"2440key",0);
request_irq(IRQ_EINT14,key_int4,IRQF_TRIGGER_LOW,"2440key",0);//gao buduandayin
    //request_irq(IRQ_EINT15,key_int5,IRQF_TRIGGER_LOW,"2440key",0);
//request_irq(IRQ_EINT19,key_int6,IRQF_TRIGGER_PROBE,"2440key",0);
    work1 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
INIT_WORK(work1, work1_func);
work2 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
INIT_WORK(work2, work2_func);
work3 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
INIT_WORK(work3, work3_func);
work4 = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
INIT_WORK(work4, work4_func);
    return 0;
}


static void button_exit()
{
    misc_deregister(&key_miscdev);
}


module_init(button_init);
module_exit(button_exit);


中斷分層
上半部:當中斷髮生時,它進行相應地硬體讀
寫,並“登記”該中斷。通常由中斷處理程式
充當上半部。
下半部:在系統空閒的時候對上半部“登記”
的中斷進行後續處理。


工作佇列
工作佇列是一種將任務推後執行的形式,他
把推後的任務交由一個核心執行緒去執行。這
樣下半部會在程序上下文執行,它允許重新
排程甚至睡眠。 每個被推後的任務叫做“工
作”,由這些工作組成的佇列稱為工作佇列


Linux核心使用struct  work_struct來描述一個工作佇列:
struct workqueue_struct {
struct cpu_workqueue_struct *cpu_wq;
struct list_head list;
const char *name;/*workqueue name*/
int singlethread;
int freezeable; /* Freeze threads during suspend */
int rt;
};


Linux核心使用struct  work_struct來描述一個工作項:
struct work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
};
typedef void (*work_func_t)(struct work_struct *work);




step1. 建立工作佇列
create_workqueue
step2. 建立工作
INIT_WORK
step3. 提交工作
queue_work


在大多數情況下, 驅動並不需要自己建立工作
佇列,只需定義工作, 然後將工作提交到核心已
經定義好的工作佇列keventd_wq中。
1. 提交工作到預設佇列
schedule_work


驅動支援多按鍵優化
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>




#define GPFCON 0x56000050
#define GPFDAT 0x56000054


struct work_struct *work1;


struct timer_list buttons_timer;


unsigned int *gpio_data;


unsigned int key_num;


void work1_func(struct work_struct *work)
{
    mod_timer(&buttons_timer, jiffies + (HZ /10));
}


void buttons_timer_function(unsigned long data)  
{
    unsigned int key_val;
    
    key_val = readw(gpio_data)&0x1; 
    if (key_val == 0)
       key_num = 4;
    
    
    key_val = readw(gpio_data)&0x4;
    if (key_val == 0)
        key_num = 3;
    





irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    //2. 清除已經發生的按鍵中斷
    
    //3. 提交下半部
    schedule_work(work1);
    
    //return 0;
    return IRQ_HANDLED;
   
}


void key_hw_init()

    unsigned int *gpio_config;
    unsigned short data;
   
    gpio_config = ioremap(GPFCON,4);
    data = readw(gpio_config);
    data &= ~0b110011;
    data |= 0b100010;


    writew(data,gpio_config);
    
    gpio_data = ioremap(GPFDAT,4);


}




int key_open(struct inode *node,struct file *filp)
{
    return 0;
}


ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)
{
    printk("in kernel :key num is %d\n",key_num);
    copy_to_user(buf, &key_num, 4);
    
    return 4;
}


struct file_operations key_fops = 
{
    .open = key_open,
    .read = key_read,
};


struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "key",
    .fops = &key_fops,
};


static int button_init()
{
    int ret;
    ret = misc_register(&key_miscdev);
    
    if (ret !=0)
        printk("register fail!\n");
    
    //註冊中斷處理程式
    request_irq(IRQ_EINT0,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
    request_irq(IRQ_EINT2,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);
    
    //按鍵初始化
    key_hw_init();
    
    //. 建立工作
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work1, work1_func);
    
    /* 初始化定時器 */  
    init_timer(&buttons_timer);   
    buttons_timer.function  = buttons_timer_function;  
    
    /* 向核心註冊一個定時器 */  
    add_timer(&buttons_timer);  
 
    return 0;
    
}




static void button_exit()
{
    misc_deregister(&key_miscdev);
}




module_init(button_init);
module_exit(button_exit);




#include <stdio.h>
#include <stdlib.h>
#include <errno.h>


int main(int argc, char **argv)
{
    int fd;
    int key_num;


    fd = open("/dev/2440key", 0);
    
    if (fd<0)
        printf("open fail\n");


    read(fd, &key_num, 4);


    printf("key is %d\n",key_num);


    close(fd);


    return 0;
}




----------------------
阻塞型驅動
核心等待佇列
定義等待佇列
wait_queue_head_t my_queue
2、初始化等待佇列
init_waitqueue_head(&my_queue)
3、定義+初始化等待佇列
DECLARE_WAIT_QUEUE_HEAD(my_queue)


4、進入等待佇列,睡眠
? wait_event(queue,condition)
當condition(布林表示式)為真時,立即返回;否則讓程序
進入TASK_UNINTERRUPTIBLE模式的睡眠,並掛在queue參
數所指定的等待佇列上。


 wait_event_interruptible(queue,condition)
當condition(布林表示式)為真時,立即返回;否則讓
程序進入TASK_INTERRUPTIBLE的睡眠,並掛在queue引數所指定的等待佇列上。


int wait_event_killable(queue, condition)
當condition(一個布林表示式)為真時,立即返回;
否則讓程序進入TASK_KILLABLE的睡眠,並掛
在queue引數所指定的等待佇列上。


5、從等待佇列中喚醒程序
wake_up(wait_queue_t *q)
從等待佇列q中喚醒狀態為TASK_UNINTERRUPTIBLE,
TASK_INTERRUPTIBLE,TASK_KILLABLE 的所有程序。
2.核心等待佇列
wake_up_interruptible(wait_queue_t *q)
從等待佇列q中喚醒狀態為TASK_INTERRUPTIBLE 的程序


#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/fs.h>
#include <asm/uaccess.h>




#define GPFCON 0x56000050
#define GPFDAT 0x56000054


struct work_struct *work1;


struct timer_list buttons_timer;


unsigned int *gpio_data;


unsigned int key_num = 0;


wait_queue_head_t  key_q;


void work1_func(struct work_struct *work)
{
    mod_timer(&buttons_timer, jiffies + (HZ /10));
}


void buttons_timer_function(unsigned long data)  
{
    unsigned int key_val;
    
    key_val = readw(gpio_data)&0x1; 
    if (key_val == 0)
       key_num = 4;
    
    
    key_val = readw(gpio_data)&0x4;
    if (key_val == 0)
        key_num = 3;
    
    wake_up(&key_q);
    





irqreturn_t key_int(int irq, void *dev_id)
{
    //1. 檢測是否發生了按鍵中斷
    
    //2. 清除已經發生的按鍵中斷
    
    //3. 提交下半部
    schedule_work(work1);
    
    //return 0;
    return IRQ_HANDLED;
   
}


void key_hw_init()

    unsigned int *gpio_config;
    unsigned short data;
   
    gpio_config = ioremap(GPFCON,4);
    data = readw(gpio_config);
    data &= ~0b110011;
    data |= 0b100010;


    writew(data,gpio_config);
    
    gpio_data = ioremap(GPFDAT,4);


}




int key_open(struct inode *node,struct file *filp)
{
    return 0;
}


ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos)

    wait_event(key_q,key_num);
    
    printk("in kernel :key num is %d\n",key_num);
    copy_to_user(buf, &key_num, 4);
    
    key_num = 0;
  
    return 4;
}


struct file_operations key_fops = 
{
    .open = key_open,
    .read = key_read,
};


struct miscdevice key_miscdev = {
    .minor = 200,
    .name = "key",
    .fops = &key_fops,
};


static int button_init()
{
    int ret;
    ret = misc_register(&key_miscdev);
    
    if (ret !=0)
        printk("register fail!\n");
    
    //註冊中斷處理程式
    request_irq(IRQ_EINT0,key_int,IRQF_TRIGGER_FALLING,"key",(void *)4);
    request_irq(IRQ_EINT2,key_int,IRQF_TRIGGER_FALLING,"key",(void *)3);
    
    //按鍵初始化
    key_hw_init();
    
    //. 建立工作
    work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);
    INIT_WORK(work1, work1_func);
    
    /* 初始化定時器 */  
    init_timer(&buttons_timer);   
    buttons_timer.function  = buttons_timer_function;  
    
    /* 向核心註冊一個定時器 */  
    add_timer(&buttons_timer);  
    
    /*初始化等待佇列*/
    init_waitqueue_head(&key_q);
 
    return 0;
    
}




static void button_exit()
{
    misc_deregister(&key_miscdev);
}




module_init(button_init);
module_exit(button_exit);


#include <stdio.h>
#include <stdlib.h>
#include <errno.h>


int main(int argc, char **argv)
{
    int fd;
    int key_num;


    fd = open("/dev/2440key", 0);
    
    if (fd<0)
        printf("open fail\n");


    read(fd, &key_num, 4);


    printf("key is %d\n",key_num);


    close(fd);


    return 0;
}

obj-m := led.o
KDIR=/home/yaya/mini2440/linux-2.6.32.2 //使用的核心原始碼原始碼路徑
all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
	rm -f *.ko *.o *.mod.o *.symvers *.bak *.order