按鍵驅動程式
阿新 • • 發佈:2019-01-25
=============
混雜裝置
在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 地址空間對映到核心的虛擬地址空間上去,便於訪問。
#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;
}
混雜裝置
在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