arm 驅(qū)動linux內(nèi)核驅(qū)動之中斷下半部編程
中斷上半部要求執(zhí)行時間間隔段,所以往往將處理時間較長的代碼放在中斷下半部來處理
本文引用地址:http://m.butianyuan.cn/article/201611/318223.htm中斷下半部的應(yīng)用:網(wǎng)卡驅(qū)動上半部初始化網(wǎng)卡驅(qū)動等短時間的事件,下半部收發(fā)數(shù)據(jù)
中斷下半部:
a, 下半部產(chǎn)生的原因:
1,中斷上下文中不能阻塞,這也限制了中斷上下文中能干的事
2,中斷處理函數(shù)執(zhí)行過程中仍有可能被其他中斷打斷,都希望中斷處理函數(shù)執(zhí)行得越快越好。
基于上面的原因,內(nèi)核將整個的中斷處理流程分為了上半部和下半部。上半部就是之前所說的中斷處理函數(shù),它能最快的響應(yīng)中斷,并且做一些必須在中斷響應(yīng)之后馬上要做的事情。而一些需要在中斷處理函數(shù)后繼續(xù)執(zhí)行的操作,內(nèi)核建議把它放在下半部執(zhí)行。
比如:在linux內(nèi)核中,當(dāng)網(wǎng)卡一旦接受到數(shù)據(jù),網(wǎng)卡會通過中斷告訴內(nèi)核處理數(shù)據(jù),內(nèi)核會在網(wǎng)卡中斷處理函數(shù)(上半部)執(zhí)行一些網(wǎng)卡硬件的必要設(shè)置,因為這是在中斷響應(yīng)后急切要干的事情。接著,內(nèi)核調(diào)用對應(yīng)的下半部函數(shù)來處理網(wǎng)卡接收到的數(shù)據(jù),因為數(shù)據(jù)處理沒必要在中斷處理函數(shù)里面馬上執(zhí)行,可以將中斷讓出來做更緊迫的事情
b,中斷下半部實現(xiàn)的機(jī)制分類
tasklet:
workqueue:工作隊列
timer:定時器
其實還有一種,叫softirq,但要寫代碼的話,就必須修改原來的內(nèi)核框架代碼,在實際開發(fā)中用的比較少,tasklet內(nèi)部實現(xiàn)就是用softeirq
c, 中斷下半部實現(xiàn)方法
1, tasklet的編程方式
1.1 : 定義并初始化結(jié)構(gòu)體tasklet_struct(一般在哪里初始化:是在模塊卸載方法中)
struct tasklet_struct
{
struct tasklet_struct *next; // l鏈表
unsigned long state;
atomic_t count;
void (*func)(unsigned long); // 下半部處理方法的實現(xiàn)
unsigned long data;//給處理函數(shù)的傳參
};
初始化方式:
靜態(tài):DECLARE_TASKLET(name, func, data);
DCLARE_TASKLET_DISABLED初始化后的處于禁止?fàn)顟B(tài),暫時不能被使用(不是中斷),除非被激活
參數(shù)1:tasklet_struct 的變量名字,自定義
參數(shù)2:中斷下半部執(zhí)行的處理函數(shù).類型為函數(shù)指針
參數(shù)3:處理函數(shù)帶的參數(shù)
動態(tài):void tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data);
參數(shù)1:tasklet_struct 對象
參數(shù)2:中斷下半部執(zhí)行的處理函數(shù)
參數(shù)3:處理函數(shù)帶的參數(shù)
1.2: 在中斷上半部中調(diào)度下半部
void tasklet_schedule(struct tasklet_struct * t);
1.3: 在模塊卸載時,銷毀這個tasklet_struct 對象
void tasklet_kill(struct tasklet_struct *t)
1.4:原理:初始化好struct tasklet_struct對象后,tasklet_schedule()會將tasklet對象加到鏈表中,內(nèi)核稍后會去調(diào)度這個tasklet對象
1.5: 特點:優(yōu)先級高,調(diào)度快,運行在中斷上下文中,所以在處理方法中,不能執(zhí)行阻塞/睡眠的操作
2,workqueque編程方式:
2.1 :定義并初始化workqueue_struct(一個隊列)和work_struct(隊列中的一項工作)對象
work_struct對象的初始化
struct work_struct {
atomic_long_t data; // 傳遞給work的參數(shù)
struct list_head entry; // 所在隊列的鏈表
work_func_t func; // work對應(yīng)的處理方法
};
靜態(tài):DECLARE_WORK(n, f)
參數(shù)1: 變量名,自定義
參數(shù)2:work對應(yīng)的處理方法,類型為函數(shù)指針
動態(tài):INIT_WORK(_work, _func)
參數(shù)1: 指針,先聲明一個struct work_struct變量,將變量地址填入
參數(shù)2:work對應(yīng)的處理方法,類型為函數(shù)指針
返回值: 返回值為void
workqueue_struct對象的初始化:(其實就是一個內(nèi)核線程)
1, 重新創(chuàng)建一個隊列
create_workqueue(name)//這個本身就是一個宏
參數(shù):名字,自定義,用于識別
返回值:struct workqueue_struct *
2, 系統(tǒng)在開機(jī)的時候自動創(chuàng)建一個隊列
2.2 將工作對象加入工作隊列中,并參與調(diào)度(注意不是馬上調(diào)度,該步驟也是中斷上半部中調(diào)用)
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
參數(shù)1:工作隊列
參數(shù)2: 工作對象
返回值: 0表示已經(jīng)放到隊列上了(也即時說本次屬于重復(fù)操作),其實一般我們都不會去做出錯處理
2.3 在模塊注銷的時候,銷毀工作隊列和工作對象
void flush_workqueue(struct workqueue_struct * wq)
該函數(shù)會一直等待,知道指定的等待隊列中所有的任務(wù)都執(zhí)行完畢并從等待隊列中移除。
void destroy_workqueue(struct workqueue_struct * wq);
該函數(shù)是是創(chuàng)建等待隊列的反操作,注銷掉指定的等待隊列。
2.4: 對于使用內(nèi)核自帶的工作隊列events, 操作步驟如下:
2.4.1 初始化工作對象,無需創(chuàng)建隊列了
靜態(tài):DECLARE_WORK(n, f)
動態(tài):INIT_WORK(_work, _func)
2.4.2將工作加入隊列并調(diào)度(在中斷上半部中調(diào)度)
int schedule_work(struct work_struct * work)
只要兩步驟就完成,也不需要刷新,也不要銷毀,因為這個工作隊列是系統(tǒng)管理的,我們不用管
2.5:原理梳理:在工作隊列中,有專門的工作者線程來處理加入到工作對列中的任務(wù)。工作對列對應(yīng)的工作者線程可能不止一個,每個處理器有且僅有一個工作隊列 對應(yīng)的工作者線程,在內(nèi)核中有一個默認(rèn)的工作隊列events,對于單處理器只有一個對應(yīng)的工作者線程
3, 定時器timer編程方式:(以上兩個下半部處理都是內(nèi)核在一個特定的時候進(jìn)行調(diào)度,時間不定,而timer可以指定某個時間點執(zhí)行)
3.1, jiffies,表示從系統(tǒng)啟動到當(dāng)前的時間值,一般做加法(+5HZ(5s),)
3.2, 定義并初始化 timer_list對象
struct timer_list {
struct list_head entry; // 鏈表
unsigned long expires; // 過期時間。也及時在什么時候執(zhí)行處理方法
struct tvec_base *base;
void (*function)(unsigned long); // 處理方法
unsigned long data; // 處理方法可以傳遞的參數(shù)
int slack;
};
靜態(tài)初始化:TIMER_INITIALIZER(_function, _expires, _data)
動態(tài)初始化:void init_timer(timer)
參數(shù):為一個指針,需要傳遞一個struct timer_list對象的地址
該函數(shù)只是初始化了timer_list對象的部分成員,還有以下成員是需要編程的:
struct timer_list mytimer;
init_timer(&mytimer);
mytimer.expires = jiffies + 2HZ
mytimer.fuction = my_timer_func; // 自己去實現(xiàn)
mytimer.data = (unsigned long)99; // 可以傳遞參數(shù)
3.3, 激活timer,開始計時 (一般也是放在中斷上半部完成)
void add_timer(&mytimer);
3.4 計時結(jié)束是,也就是要執(zhí)行處理函數(shù)時,執(zhí)行函數(shù)中要下一次計時的話,必須修改timer
mod_timer(&my_timer, jiffies + 2*HZ);
// 2s之后再來,相當(dāng)于如下:
my_timer.expires = jiffies + 2*HZ; //重新設(shè)定時間,在兩秒后再執(zhí)行
add_timer(&my_timer); //再次激活定時器
3.5 定時器的銷毀
int del_timer(struct timer_list *timer) // 該函數(shù)用來刪除還沒超時的定時器
timer定時器的中斷上下半部代碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include arm/param.h>
#include
#include
#define VIRTUAL_MAJOR 250
int major = VIRTUAL_MAJOR;
struct myirq_desc{
int irq_id;
char *irq_name;
int irq_code;
};
struct myirq_desc myirq_descs[3]= {
{S3C2410_GPF0, "s2", KEY_A},
{S3C2410_GPF2, "s3", KEY_K},
{S3C2410_GPG3, "s4", KEY_Z},
};
struct VirtualDisk{
struct class *mycdevclass;//在/sys/class創(chuàng)建類
struct class_device *mycdevclassdevice;//在/dev下創(chuàng)建設(shè)備
struct cdev mycdev;//給設(shè)備添加相關(guān)的fileoption
struct timer_list mytimer;
};
struct VirtualDisk *myvirtualdisk;
static struct file_operations mydev_fops = {
.owner = THIS_MODULE,
};
static void mytimer_func(unsigned long fundata){
printk("*******%s********n", __FUNCTION__);
struct VirtualDisk *myvirtualdisk_fun = (struct VirtualDisk *)(fundata);
myvirtualdisk_fun->mytimer.expires =jiffies + 2 * HZ;
add_timer(&myvirtualdisk_fun->mytimer);
printk("timer func happened!n");
}
static irqreturn_t myirq_handle(int irq, void *dev_id){
struct myirq_desc *myirq_descone = (struct myirq_desc *)dev_id;
printk("*******%s********n", __FUNCTION__);
printk("irq = %d, irq_id = %d,irq_name = %s, irq_code = %cn", irq, myirq_descone->irq_id, myirq_descone->irq_name, myirq_descone->irq_code);
mod_timer(&myvirtualdisk->mytimer, jiffies + 2*HZ);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int __init cdevtest_init(void){
dev_t mydev = MKDEV(major, 0);
int ret;
int i = 0;
printk("*******%s********n", __FUNCTION__);
if(major){//注冊proc/devices
ret = register_chrdev_region(mydev, 1, "mynewdriver");
}else{
ret = alloc_chrdev_region(&mydev, 0, 1, "mynewdriver");
major = MAJOR(mydev);
}
if(ret < 0){
printk(KERN_ERR "register_chrdev_region failed!n");
ret = -EINVAL;
return ret;
}
myvirtualdisk = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);
if(!myvirtualdisk){
ret = -ENOMEM;
printk(KERN_ERR "kmalloc myvirtualdisk failed!n");
goto release_chrdev;
}
myvirtualdisk->mycdevclass = class_create(THIS_MODULE, "mynewdriver");
if(IS_ERR(myvirtualdisk->mycdevclass)){
ret = PTR_ERR(myvirtualdisk->mycdevclass);
printk(KERN_ERR "class_create failed!n");
goto release_mem_malloc;
}
myvirtualdisk->mycdevclassdevice = class_device_create(myvirtualdisk->mycdevclass, NULL, MKDEV(major, 0), NULL, "mynewdriver");
if(IS_ERR(myvirtualdisk->mycdevclassdevice)){
ret = PTR_ERR(myvirtualdisk->mycdevclassdevice);
printk(KERN_ERR "class_device_create failed!n");
goto release_class_create;
}
cdev_init(&(myvirtualdisk->mycdev), &mydev_fops);
myvirtualdisk->mycdev.owner = THIS_MODULE;
ret = cdev_add(&myvirtualdisk->mycdev, MKDEV(major, 0), 1);
//這里把timer相關(guān)的放在irq前面,不然會有bug出現(xiàn)(當(dāng)在insmod時候按下按鈕就會出錯)
init_timer(&myvirtualdisk->mytimer);
myvirtualdisk->mytimer.function = mytimer_func;
myvirtualdisk->mytimer.data = (unsigned long)myvirtualdisk;
if(ret < 0){
goto release_device_class_create;
}
for(i = 0; i < 3; i++)
{
ret = request_irq(gpio_to_irq(myirq_descs[i].irq_id), myirq_handle, IRQF_TRIGGER_FALLING, myirq_descs[i].irq_name, &myirq_descs[i]);
if(ret < 0){
printk(KERN_ERR "request irq failed!n");
ret =-EFAULT;
goto release_cdevandtimer;
}
}
return 0;
release_cdevandtimer:
del_timer(&myvirtualdisk->mytimer);
cdev_del(&myvirtualdisk->mycdev);
release_device_class_create:
class_device_unregister(myvirtualdisk->mycdevclassdevice);
release_class_create:
class_destroy(myvirtualdisk->mycdevclass);
release_mem_malloc:
kfree(myvirtualdisk);
release_chrdev:
unregister_chrdev_region(MKDEV(major, 0), 1);
return ret;
}
static void __exit cdevtest_exit(void){
int i = 0;
printk("*******%s****1****n", __FUNCTION__);
del_timer(&myvirtualdisk->mytimer);
printk("*******%s*****2***n", __FUNCTION__);
for(i = 0; i < 3; i++)free_irq(gpio_to_irq(myirq_descs[i].irq_id), &myirq_descs[i]);
printk("*******%s*****3***n", __FUNCTION__);
cdev_del(&myvirtualdisk->mycdev);
printk("*******%s*****4***n", __FUNCTION__);
class_device_unregister(myvirtualdisk->mycdevclassdevice);
printk("*******%s*****5***n", __FUNCTION__);
class_destroy(myvirtualdisk->mycdevclass);
printk("*******%s*****6***n", __FUNCTION__);
kfree(myvirtualdisk);
printk("*******%s********n", __FUNCTION__);
unregister_chrdev_region(MKDEV(major, 0), 1);
printk("*******%s*****7***n", __FUNCTION__);
}
module_init(cdevtest_init);
module_exit(cdevtest_exit);
MODULE_LICENSE("GPL");
完整的tasklet任務(wù)中斷上下半部代碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define VIRTUAL_MAJOR 250
int major = VIRTUAL_MAJOR;
struct myirq_desc{
int irq_id;
char *irq_name;
int irq_code;
};
struct myirq_desc myirq_descs[3]= {
{S3C2410_GPF0, "s2", KEY_J},
{S3C2410_GPF2, "s3", KEY_K},
{S3C2410_GPG3, "s4", KEY_Z},
};
struct VirtualDisk{
struct class *mycdevclass;//在/sys/class創(chuàng)建類
struct class_device *mycdevclassdevice;//在/dev下創(chuàng)建設(shè)備
struct cdev mycdev;//給設(shè)備添加相關(guān)的fileoption
struct timer_list mytimer;
struct tasklet_struct mytasklet;
};
struct VirtualDisk *myvirtualdisk;
static struct file_operations mydev_fops = {
.owner = THIS_MODULE,
};
static void mytasklet_func(unsigned long fundata){
printk("*****%s******,date = %ldn", __FUNCTION__, fundata);
}
static void mytimer_func(unsigned long fundata){
printk("*******%s********n", __FUNCTION__);
struct VirtualDisk *myvirtualdisk_fun = (struct VirtualDisk *)(fundata);
//myvirtualdisk_fun->mytimer.expires =jiffies + 2 * HZ;
//add_timer(&myvirtualdisk_fun->mytimer);
printk("timer func happened!n");
}
static irqreturn_t myirq_handle(int irq, void *dev_id){
struct myirq_desc *myirq_descone = (struct myirq_desc *)dev_id;
printk("*******%s********n", __FUNCTION__);
printk("irq = %d, irq_id = %d,irq_name = %s, irq_code = %cn", irq, myirq_descone->irq_id, myirq_descone->irq_name, myirq_descone->irq_code);
tasklet_schedule(&myvirtualdisk->mytasklet);//激發(fā)任務(wù),將mytasklet_func加入系統(tǒng)任務(wù)
mod_timer(&myvirtualdisk->mytimer, jiffies + 2*HZ);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int __init cdevtest_init(void){
dev_t mydev = MKDEV(major, 0);
int ret;
int i = 0;
printk("*******%s********n", __FUNCTION__);
if(major){//注冊proc/devices
ret = register_chrdev_region(mydev, 1, "mynewdriver");
}else{
ret = alloc_chrdev_region(&mydev, 0, 1, "mynewdriver");
major = MAJOR(mydev);
}
if(ret < 0){
printk(KERN_ERR "register_chrdev_region failed!n");
ret = -EINVAL;
return ret;
}
myvirtualdisk = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);
if(!myvirtualdisk){
ret = -ENOMEM;
printk(KERN_ERR "kmalloc myvirtualdisk failed!n");
goto release_chrdev;
}
myvirtualdisk->mycdevclass = class_create(THIS_MODULE, "mynewdriver");
if(IS_ERR(myvirtualdisk->mycdevclass)){
ret = PTR_ERR(myvirtualdisk->mycdevclass);
printk(KERN_ERR "class_create failed!n");
goto release_mem_malloc;
}
myvirtualdisk->mycdevclassdevice = class_device_create(myvirtualdisk->mycdevclass, NULL, MKDEV(major, 0), NULL, "mynewdriver");
if(IS_ERR(myvirtualdisk->mycdevclassdevice)){
ret = PTR_ERR(myvirtualdisk->mycdevclassdevice);
printk(KERN_ERR "class_device_create failed!n");
goto release_class_create;
}
cdev_init(&(myvirtualdisk->mycdev), &mydev_fops);
myvirtualdisk->mycdev.owner = THIS_MODULE;
ret = cdev_add(&myvirtualdisk->mycdev, MKDEV(major, 0), 1);
//tasklet 任務(wù)調(diào)度
tasklet_init(&myvirtualdisk->mytasklet, mytasklet_func, (unsigned long) 90);
//這里把timer相關(guān)的放在irq前面,不然會有bug出現(xiàn)(當(dāng)在insmod時候按下按鈕就會出錯)
init_timer(&myvirtualdisk->mytimer);
myvirtualdisk->mytimer.function = mytimer_func;
myvirtualdisk->mytimer.data = (unsigned long)myvirtualdisk;
if(ret < 0){
goto release_device_class_create;
}
for(i = 0; i < 3; i++)
{
ret = request_irq(gpio_to_irq(myirq_descs[i].irq_id), myirq_handle, IRQF_TRIGGER_FALLING, myirq_descs[i].irq_name, &myirq_descs[i]);
if(ret < 0){
printk(KERN_ERR "request irq failed!n");
ret =-EFAULT;
goto release_cdevandtimer;
}
}
return 0;
release_cdevandtimer:
tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務(wù)
del_timer(&myvirtualdisk->mytimer);
cdev_del(&myvirtualdisk->mycdev);
release_device_class_create:
class_device_unregister(myvirtualdisk->mycdevclassdevice);
release_class_create:
class_destroy(myvirtualdisk->mycdevclass);
release_mem_malloc:
kfree(myvirtualdisk);
release_chrdev:
unregister_chrdev_region(MKDEV(major, 0), 1);
return ret;
}
static void __exit cdevtest_exit(void){
int i = 0;
printk("*******%s****1****n", __FUNCTION__);
tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務(wù)
del_timer(&myvirtualdisk->mytimer);
printk("*******%s*****2***n", __FUNCTION__);
for(i = 0; i < 3; i++)free_irq(gpio_to_irq(myirq_descs[i].irq_id), &myirq_descs[i]);
printk("*******%s*****3***n", __FUNCTION__);
cdev_del(&myvirtualdisk->mycdev);
printk("*******%s*****4***n", __FUNCTION__);
class_device_unregister(myvirtualdisk->mycdevclassdevice);
printk("*******%s*****5***n", __FUNCTION__);
class_destroy(myvirtualdisk->mycdevclass);
printk("*******%s*****6***n", __FUNCTION__);
kfree(myvirtualdisk);
printk("*******%s********n", __FUNCTION__);
unregister_chrdev_region(MKDEV(major, 0), 1);
printk("*******%s*****7***n", __FUNCTION__);
}
module_init(cdevtest_init);
module_exit(cdevtest_exit);
MODULE_LICENSE("GPL");
完整的workqueue工作隊列中斷上下半部代碼
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define VIRTUAL_MAJOR 250
int major = VIRTUAL_MAJOR;
struct myirq_desc{
int irq_id;
char *irq_name;
int irq_code;
};
struct myirq_desc myirq_descs[3]= {
{S3C2410_GPF0, "s2", KEY_J},
{S3C2410_GPF2, "s3", KEY_K},
{S3C2410_GPG3, "s4", KEY_Z},
};
struct VirtualDisk{
struct class *mycdevclass;//在/sys/class創(chuàng)建類
struct class_device *mycdevclassdevice;//在/dev下創(chuàng)建設(shè)備
struct cdev mycdev;//給設(shè)備添加相關(guān)的fileoption
struct timer_list mytimer;
struct tasklet_struct mytasklet;
struct workqueue_struct *myworkqueue;//工作隊列
struct work_struct mywork;//工作;工作隊列中的一項工作
};
struct VirtualDisk *myvirtualdisk;
static struct file_operations mydev_fops = {
.owner = THIS_MODULE,
};
static void mywork_func(struct work_struct *work){
printk("*******%s********n", __FUNCTION__);
}
static void mytasklet_func(unsigned long fundata){
printk("*****%s******,date = %ldn", __FUNCTION__, fundata);
}
static void mytimer_func(unsigned long fundata){
printk("*******%s********n", __FUNCTION__);
//struct VirtualDisk *myvirtualdisk_fun = (struct VirtualDisk *)(fundata);
//myvirtualdisk_fun->mytimer.expires =jiffies + 2 * HZ;
//add_timer(&myvirtualdisk_fun->mytimer);
printk("timer func happened!n");
}
static irqreturn_t myirq_handle(int irq, void *dev_id){
struct myirq_desc *myirq_descone = (struct myirq_desc *)dev_id;
printk("*******%s********n", __FUNCTION__);
printk("irq = %d, irq_id = %d,irq_name = %s, irq_code = %cn", irq, myirq_descone->irq_id, myirq_descone->irq_name, myirq_descone->irq_code);
queue_work(myvirtualdisk->myworkqueue, &myvirtualdisk->mywork);
tasklet_schedule(&myvirtualdisk->mytasklet);//激發(fā)任務(wù),將mytasklet_func加入系統(tǒng)任務(wù)
mod_timer(&myvirtualdisk->mytimer, jiffies + 2*HZ);
return IRQ_RETVAL(IRQ_HANDLED);
}
static int __init cdevtest_init(void){
dev_t mydev = MKDEV(major, 0);
int ret;
int i = 0;
printk("*******%s********n", __FUNCTION__);
if(major){//注冊proc/devices
ret = register_chrdev_region(mydev, 1, "mynewdriver");
}else{
ret = alloc_chrdev_region(&mydev, 0, 1, "mynewdriver");
major = MAJOR(mydev);
}
if(ret < 0){
printk(KERN_ERR "register_chrdev_region failed!n");
ret = -EINVAL;
return ret;
}
myvirtualdisk = kmalloc(sizeof(struct VirtualDisk), GFP_KERNEL);
if(!myvirtualdisk){
ret = -ENOMEM;
printk(KERN_ERR "kmalloc myvirtualdisk failed!n");
goto release_chrdev;
}
myvirtualdisk->mycdevclass = class_create(THIS_MODULE, "mynewdriver");
if(IS_ERR(myvirtualdisk->mycdevclass)){
ret = PTR_ERR(myvirtualdisk->mycdevclass);
printk(KERN_ERR "class_create failed!n");
goto release_mem_malloc;
}
myvirtualdisk->mycdevclassdevice = class_device_create(myvirtualdisk->mycdevclass, NULL, MKDEV(major, 0), NULL, "mynewdriver");
if(IS_ERR(myvirtualdisk->mycdevclassdevice)){
ret = PTR_ERR(myvirtualdisk->mycdevclassdevice);
printk(KERN_ERR "class_device_create failed!n");
goto release_class_create;
}
//工作和工作隊列
INIT_WORK(&myvirtualdisk->mywork, mywork_func);
myvirtualdisk->myworkqueue = create_workqueue("myworkqueue");
if (!myvirtualdisk->myworkqueue) {
ret = -ENOMEM;
goto release_class_create;
}
cdev_init(&(myvirtualdisk->mycdev), &mydev_fops);
myvirtualdisk->mycdev.owner = THIS_MODULE;
ret = cdev_add(&myvirtualdisk->mycdev, MKDEV(major, 0), 1);
//tasklet 任務(wù)調(diào)度
tasklet_init(&myvirtualdisk->mytasklet, mytasklet_func, (unsigned long) 90);
//這里把timer相關(guān)的放在irq前面,不然會有bug出現(xiàn)(當(dāng)在insmod時候按下按鈕就會出錯)
init_timer(&myvirtualdisk->mytimer);
myvirtualdisk->mytimer.function = mytimer_func;
myvirtualdisk->mytimer.data = (unsigned long)myvirtualdisk;
if(ret < 0){
goto release_device_class_create;
}
for(i = 0; i < 3; i++)
{
ret = request_irq(gpio_to_irq(myirq_descs[i].irq_id), myirq_handle, IRQF_TRIGGER_FALLING, myirq_descs[i].irq_name, &myirq_descs[i]);
if(ret < 0){
printk(KERN_ERR "request irq failed!n");
ret =-EFAULT;
goto release_cdevandtimer;
}
}
/*在模塊注銷的時候,銷毀工作隊列和工作對象
void flush_workqueue(struct workqueue_struct * wq)
該函數(shù)會一直等待,知道指定的等待隊列中所有的任務(wù)都執(zhí)行完畢并從等待隊列中移除。
void destroy_workqueue(struct workqueue_struct * wq);
該函數(shù)是是創(chuàng)建等待隊列的反操作,注銷掉指定的等待隊列。*/
return 0;
release_cdevandtimer:
tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務(wù)
del_timer(&myvirtualdisk->mytimer);
cdev_del(&myvirtualdisk->mycdev);
/*在模塊注銷的時候,銷毀工作隊列和工作對象
void flush_workqueue(struct workqueue_struct * wq)
該函數(shù)會一直等待,知道指定的等待隊列中所有的任務(wù)都執(zhí)行完畢并從等待隊列中移除。
void destroy_workqueue(struct workqueue_struct * wq);
該函數(shù)是是創(chuàng)建等待隊列的反操作,注銷掉指定的等待隊列。*/
flush_workqueue(myvirtualdisk->myworkqueue);
destroy_workqueue(myvirtualdisk->myworkqueue);
release_device_class_create:
class_device_unregister(myvirtualdisk->mycdevclassdevice);
release_class_create:
class_destroy(myvirtualdisk->mycdevclass);
release_mem_malloc:
kfree(myvirtualdisk);
release_chrdev:
unregister_chrdev_region(MKDEV(major, 0), 1);
return ret;
}
static void __exit cdevtest_exit(void){
int i = 0;
printk("*******%s****1****n", __FUNCTION__);
tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務(wù)
del_timer(&myvirtualdisk->mytimer);
printk("*******%s*****2***n", __FUNCTION__);
for(i = 0; i < 3; i++)free_irq(gpio_to_irq(myirq_descs[i].irq_id), &myirq_descs[i]);
printk("*******%s*****3***n", __FUNCTION__);
cdev_del(&myvirtualdisk->mycdev);
//刪除工作和工作隊列
flush_workqueue(myvirtualdisk->myworkqueue);
destroy_workqueue(myvirtualdisk->myworkqueue);
printk("*******%s*****4***n", __FUNCTION__);
class_device_unregister(myvirtualdisk->mycdevclassdevice);
printk("*******%s*****5***n", __FUNCTION__);
class_destroy(myvirtualdisk->mycdevclass);
printk("*******%s*****6***n", __FUNCTION__);
kfree(myvirtualdisk);
printk("*******%s********n", __FUNCTION__);
unregister_chrdev_region(MKDEV(major, 0), 1);
printk("*******%s*****7***n", __FUNCTION__);
}
module_init(cdevtest_init);
module_exit(cdevtest_exit);
MODULE_LICENSE("GPL");
評論