arm 驱动linux内核驱动之中断下半部编程

发布者:彩虹微笑最新更新时间:2016-06-06 来源: eefocus关键字:arm驱动  linux  内核驱动  中断下半部编程 手机看文章 扫描二维码
随时随地手机看文章
本文部分参考华清远见文档

中断上半部要求执行时间间隔段,所以往往将处理时间较长的代码放在中断下半部来处理

中断下半部的应用:网卡驱动上半部初始化网卡驱动等短时间的事件,下半部收发数据

中断下半部:

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
#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_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 = %c\n", 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 = %ld\n", __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 = %c\n", 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 = %ld\n", __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 = %c\n", 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");

关键字:arm驱动  linux  内核驱动  中断下半部编程 引用地址:arm 驱动linux内核驱动之中断下半部编程

上一篇:arm驱动linux内核链表
下一篇:arm驱动linux等待队列阻塞中断IO的应用

推荐阅读最新更新时间:2024-03-16 14:56

STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库
前面介绍了在Windows的Keil5环境下使用FwLib_STC8, 以下介绍在Linux(本文使用Ubuntu20.04)的VSCode下的环境搭建 配置VSCode开发环境运行演示用例 前提 已经安装完VSCode + PlatformIO环境, 并配置好MCS-51 Platform, 如果未完成, 请自行搜索网上的教程和说明 本机已安装git 频率设定和参数准备 因为当前在Linux下不能通过烧录工具调整芯片的内置RC时钟频率, 所以芯片的时钟调整要用其它方法 最简单直接的方式, 是在Windows下用STC-ISP将芯片设置到目标频率, 这种方式将来要修改频率时, 需要再次用STC-ISP进行修改 如果希望将来
[单片机]
STC8H开发(二): 在<font color='red'>Linux</font> VSCode中配置和使用FwLib_STC8封装库
2005年Linux市场:竞争结构发生调整 中科红旗抢占第一
   2005年,中国Linux市场竞争结构因为两大国外厂商强势进入而发生巨大调整,同时市场规模激增,客观上促进了市场竞争的完全化,加速了用户采购应用的成熟。   如果说 Linux市场的发展,每年都伴随着新厂商的进入和老厂商的退出,那么2005年是新旧厂商交替变化最大的一年。Redhat改变了只在国内设置分销渠道的做法,正式在中国成立分公司;Novell收购SUSE后在中国市场主推Linux;而重点提供商“冲浪”则彻底退出Linux操作系统市场;“共创开源”的飞速发展和“麒麟”的诞生也给市场带来了新的活力。竞争参与者的新旧更替是市场竞争结构变化的特点之一。   2006年的Linux市场还有一个特点就是市场竞争完
[焦点新闻]
Linux内核服务例程与系统调用接口
  1.用宏生成系统调用例程   高级语言应用程序一般不能直接访问内核函数。但是,总还是有一些高级用户需要访问内核函数,如果让用户自己编写汇编语言程序来实现内核的陷入,显然是不合适的,因为它既不安全也不符合设置操作系统的初衷___为用户提供一个友好的程序设汁平台。   由于用于封装内核服务例程的系统调用例程有一个固定的框架,所以为了简化对内核服务例程的封装工作,Linux定义了从_sysca110~_sysca115的6个宏,高级用户可使用这些宏把所需的内核服务例程封装为系统调用例程。   上述这6个宏的名称后的数字表示被封装的内核服务例程可使用的参数个数(系统调用号除外)。在上述宏对内核服务例程进行封装时,Linux规定
[嵌入式]
[ARM笔记]驱动对设备的识别过程及实例——NAND Flash
驱动程序识别设备时,有以下两种方法: (1)驱动程序本身带有设备信息,比如开始地址、中断号等;加载驱动程序时,就可以根据这些信息来识别设备。 (2)驱动程序本身没有设备信息,但是内核中已经(或以后)根据其他方式确定了很多设备的信息;加载驱动程序时,将驱动程序与这些设备逐个比较,确定两者是否匹配(math)。如果驱动程序与某个设备匹配,就可以通过该驱动程序来操作这个设备了。 内核常使用第二种方法来识别设备,这可以将各种设备集中在一个文件中管理,当开发板的配置改变时,便于修改代码。在内核文件include/linux/platform_device.h中,定义了两个数据结构来表示这些设备和驱动程序:platform_device结
[单片机]
Linux将成为21世纪汽车主流操作系统
    据科技博客ZDNet报道,开源软件组织Linux基金会日前表示,Linux操作系统将成为21世纪汽车产品的主流操作系统。Linux系统不仅支持电 脑服务器产品,还能通过Android运行在用户的智能手机上;此外,在汽车上也能找到它的踪影。当然,没有人在购买汽车产品时考虑它使用了何种操作系 统,但是包括丰田、尼桑、捷豹路虎、福特、马自达、三菱和斯巴鲁等主流汽车品牌在内,它们的车载信息娱乐、显示、车载4G网络、Wi-Fi系统,都离不开 Linux操作系统的支持。 各大软件厂商也都纷纷加入了面向汽车行业的这场移动物联网盛宴当中。Movimento、甲骨文、高通、德仪、UIEvolution和VeriSilicon等软件厂商,
[手机便携]
AT91SAM9X5EK移植linux4sam6.1包
AT91Bootstrap 3.8.13 (Thu Oct 17 17:21:19 CST 2019) 1-Wire: Loading 1-Wire information … 1-Wire: ROM Searching … Done, 0 1-Wire chips found WARNING: 1-Wire: No 1-Wire chip found 1-Wire: Using default information 1-Wire: Board sn: 0x4010425 revision: 0x8421 NAND: ONFI flash detected chip- numblocks = 0x800 ch
[单片机]
AT91SAM9X5EK移植<font color='red'>linux</font>4sam6.1包
ARM-LINUX-GCC交叉编译工具链必知必会
一、一些需要知道的概念 在正式谈论交叉编译工具ARM-LINUX-GCC前,我想有必要明确两个非常基本的概念。 1、什么是交叉编译,什么是交叉编译工具链:https://www.crifan.com/files/doc/docbook/cross_compile/release/html/cross_compile.html#what_is_crosscompile,只需要关注此文章的第一章与第二章。2、GCC与ARM-LINUX-GCC的关系:GCC是一套编译工具链,一般来说,其用于将代码编译成在X86架构电脑上运行的可执行文件,而ARM-LINUX-GCC可以看成经过特殊配置的GCC,其编译出的程序并不是跑在X86架构电脑
[单片机]
<font color='red'>ARM</font>-<font color='red'>LINUX</font>-GCC交叉编译工具链必知必会
基于FA526处理器SoC平台的Linux操作系统实现
引言   智原科技的FIE8100 SoC平台是一种低功耗、便携式视频相关应用开发SoC平台,也可用于基于FA526 CPU的SoC设计验证。   基于FA526的Linux软件开发套件,开发人员可将Linux一2.4.19软件环境在FIE8100平台上安装实现,并完成对平台上所有IP的驱动程序安装和对FA526的内部调试。 FA526介绍   FA526是一颗有着广泛用途的32位RISC处理器。它包括一个同步CPU内核(core)、独立的指令/数据缓存(cache)、独立的指令/数据暂存器(scratchpads)、一个写缓存(write buffer)、一个内存管理单元(memory management unit)和J
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved