新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > arm 驅動linux內核驅動之中斷下半部編程

        arm 驅動linux內核驅動之中斷下半部編程

        作者: 時間:2016-11-19 來源:網絡 收藏
        本文部分參考華清遠見文檔

        中斷上半部要求執行時間間隔段,所以往往將處理時間較長的代碼放在中斷下半部來處理

        本文引用地址:http://www.104case.com/article/201611/318223.htm

        中斷下半部的應用:網卡驅動上半部初始化網卡驅動等短時間的事件,下半部收發數據

        中斷下半部:

        a, 下半部產生的原因:

        1,中斷上下文中不能阻塞,這也限制了中斷上下文中能干的事

        2,中斷處理函數執行過程中仍有可能被其他中斷打斷,都希望中斷處理函數執行得越快越好。

        基于上面的原因,內核將整個的中斷處理流程分為了上半部和下半部。上半部就是之前所說的中斷處理函數,它能最快的響應中斷,并且做一些必須在中斷響應之后馬上要做的事情。而一些需要在中斷處理函數后繼續執行的操作,內核建議把它放在下半部執行。

        比如:在linux內核中,當網卡一旦接受到數據,網卡會通過中斷告訴內核處理數據,內核會在網卡中斷處理函數(上半部)執行一些網卡硬件的必要設置,因為這是在中斷響應后急切要干的事情。接著,內核調用對應的下半部函數來處理網卡接收到的數據,因為數據處理沒必要在中斷處理函數里面馬上執行,可以將中斷讓出來做更緊迫的事情

        b,中斷下半部實現的機制分類

        tasklet:

        workqueue:工作隊列

        timer:定時器

        其實還有一種,叫softirq,但要寫代碼的話,就必須修改原來的內核框架代碼,在實際開發中用的比較少,tasklet內部實現就是用softeirq

        c, 中斷下半部實現方法

        1, tasklet的編程方式

        1.1 : 定義并初始化結構體tasklet_struct(一般在哪里初始化:是在模塊卸載方法中)

        struct tasklet_struct

        {

        struct tasklet_struct *next; // l鏈表

        unsigned long state;

        atomic_t count;

        void (*func)(unsigned long); // 下半部處理方法的實現

        unsigned long data;//給處理函數的傳參

        };

        初始化方式:

        靜態:DECLARE_TASKLET(name, func, data);

        DCLARE_TASKLET_DISABLED初始化后的處于禁止狀態,暫時不能被使用(不是中斷),除非被激活

        參數1:tasklet_struct 的變量名字,自定義

        參數2:中斷下半部執行的處理函數.類型為函數指針

        參數3:處理函數帶的參數

        動態:void tasklet_init(struct tasklet_struct * t, void(* func)(unsigned long), unsigned long data);

        參數1:tasklet_struct 對象

        參數2:中斷下半部執行的處理函數

        參數3:處理函數帶的參數

        1.2: 在中斷上半部中調度下半部

        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對象加到鏈表中,內核稍后會去調度這個tasklet對象

        1.5: 特點:優先級高,調度快,運行在中斷上下文中,所以在處理方法中,不能執行阻塞/睡眠的操作

        2,workqueque編程方式:

        2.1 :定義并初始化workqueue_struct(一個隊列)和work_struct(隊列中的一項工作)對象

        work_struct對象的初始化

        struct work_struct {

        atomic_long_t data; // 傳遞給work的參數

        struct list_head entry; // 所在隊列的鏈表

        work_func_t func; // work對應的處理方法

        };

        靜態:DECLARE_WORK(n, f)

        參數1: 變量名,自定義

        參數2:work對應的處理方法,類型為函數指針

        動態:INIT_WORK(_work, _func)

        參數1: 指針,先聲明一個struct work_struct變量,將變量地址填入

        參數2:work對應的處理方法,類型為函數指針

        返回值: 返回值為void

        workqueue_struct對象的初始化:(其實就是一個內核線程)

        1, 重新創建一個隊列

        create_workqueue(name)//這個本身就是一個宏

        參數:名字,自定義,用于識別

        返回值:struct workqueue_struct *

        2, 系統在開機的時候自動創建一個隊列

        2.2 將工作對象加入工作隊列中,并參與調度(注意不是馬上調度,該步驟也是中斷上半部中調用)

        int queue_work(struct workqueue_struct *wq, struct work_struct *work)

        參數1:工作隊列

        參數2: 工作對象

        返回值: 0表示已經放到隊列上了(也即時說本次屬于重復操作),其實一般我們都不會去做出錯處理

        2.3 在模塊注銷的時候,銷毀工作隊列和工作對象

        void flush_workqueue(struct workqueue_struct * wq)

        該函數會一直等待,知道指定的等待隊列中所有的任務都執行完畢并從等待隊列中移除。

        void destroy_workqueue(struct workqueue_struct * wq);

        該函數是是創建等待隊列的反操作,注銷掉指定的等待隊列。

        2.4: 對于使用內核自帶的工作隊列events, 操作步驟如下:

        2.4.1 初始化工作對象,無需創建隊列了

        靜態:DECLARE_WORK(n, f)

        動態:INIT_WORK(_work, _func)

        2.4.2將工作加入隊列并調度(在中斷上半部中調度)

        int schedule_work(struct work_struct * work)

        只要兩步驟就完成,也不需要刷新,也不要銷毀,因為這個工作隊列是系統管理的,我們不用管

        2.5:原理梳理:在工作隊列中,有專門的工作者線程來處理加入到工作對列中的任務。工作對列對應的工作者線程可能不止一個,每個處理器有且僅有一個工作隊列 對應的工作者線程,在內核中有一個默認的工作隊列events,對于單處理器只有一個對應的工作者線程

        3, 定時器timer編程方式:(以上兩個下半部處理都是內核在一個特定的時候進行調度,時間不定,而timer可以指定某個時間點執行)

        3.1, jiffies,表示從系統啟動到當前的時間值,一般做加法(+5HZ(5s),)

        3.2, 定義并初始化 timer_list對象

        struct timer_list {

        struct list_head entry; // 鏈表

        unsigned long expires; // 過期時間。也及時在什么時候執行處理方法

        struct tvec_base *base;

        void (*function)(unsigned long); // 處理方法

        unsigned long data; // 處理方法可以傳遞的參數

        int slack;

        };

        靜態初始化:TIMER_INITIALIZER(_function, _expires, _data)

        動態初始化:void init_timer(timer)

        參數:為一個指針,需要傳遞一個struct timer_list對象的地址

        該函數只是初始化了timer_list對象的部分成員,還有以下成員是需要編程的:

        struct timer_list mytimer;

        init_timer(&mytimer);

        mytimer.expires = jiffies + 2HZ

        mytimer.fuction = my_timer_func; // 自己去實現

        mytimer.data = (unsigned long)99; // 可以傳遞參數

        3.3, 激活timer,開始計時 (一般也是放在中斷上半部完成)

        void add_timer(&mytimer);

        3.4 計時結束是,也就是要執行處理函數時,執行函數中要下一次計時的話,必須修改timer

        mod_timer(&my_timer, jiffies + 2*HZ);

        // 2s之后再來,相當于如下:

        my_timer.expires = jiffies + 2*HZ; //重新設定時間,在兩秒后再執行

        add_timer(&my_timer); //再次激活定時器

        3.5 定時器的銷毀

        int del_timer(struct timer_list *timer) // 該函數用來刪除還沒超時的定時器

        timer定時器的中斷上下半部代碼

        #include
        #include
        #include
        #include
        #include
        #include
        #include
        #include interrupt.h>
        #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創建類
        struct class_device *mycdevclassdevice;//在/dev下創建設備
        struct cdev mycdev;//給設備添加相關的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相關的放在irq前面,不然會有bug出現(當在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任務中斷上下半部代碼

        #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創建類
        struct class_device *mycdevclassdevice;//在/dev下創建設備
        struct cdev mycdev;//給設備添加相關的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);//激發任務,將mytasklet_func加入系統任務
        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 任務調度
        tasklet_init(&myvirtualdisk->mytasklet, mytasklet_func, (unsigned long) 90);

        //這里把timer相關的放在irq前面,不然會有bug出現(當在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);//刪除任務
        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);//刪除任務
        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創建類
        struct class_device *mycdevclassdevice;//在/dev下創建設備
        struct cdev mycdev;//給設備添加相關的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);//激發任務,將mytasklet_func加入系統任務
        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 任務調度
        tasklet_init(&myvirtualdisk->mytasklet, mytasklet_func, (unsigned long) 90);

        //這里把timer相關的放在irq前面,不然會有bug出現(當在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)
        該函數會一直等待,知道指定的等待隊列中所有的任務都執行完畢并從等待隊列中移除。
        void destroy_workqueue(struct workqueue_struct * wq);
        該函數是是創建等待隊列的反操作,注銷掉指定的等待隊列。*/

        return 0;
        release_cdevandtimer:
        tasklet_kill(&myvirtualdisk->mytasklet);//刪除任務
        del_timer(&myvirtualdisk->mytimer);
        cdev_del(&myvirtualdisk->mycdev);
        /*在模塊注銷的時候,銷毀工作隊列和工作對象
        void flush_workqueue(struct workqueue_struct * wq)
        該函數會一直等待,知道指定的等待隊列中所有的任務都執行完畢并從等待隊列中移除。
        void destroy_workqueue(struct workqueue_struct * wq);
        該函數是是創建等待隊列的反操作,注銷掉指定的等待隊列。*/
        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);//刪除任務
        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");



        評論


        技術專區

        關閉
        主站蜘蛛池模板: 瓦房店市| 金山区| 太白县| 旅游| 宜州市| 淄博市| 百色市| 青海省| 宁国市| 固始县| 瑞昌市| 衡南县| 固镇县| 松江区| 历史| 青铜峡市| 井研县| 桃园县| 新津县| 绥化市| 绥德县| 商丘市| 介休市| 安乡县| 乌拉特后旗| 兴仁县| 托克托县| 涞水县| 阳谷县| 水富县| 桦南县| 洛阳市| 镇雄县| 深圳市| 若羌县| 晋中市| 杭州市| 乐安县| 文成县| 巴彦淖尔市| 晋宁县|