• 嵌入式(驱动开发)(中断处理)


    一、什么是中断

    一种硬件上的通知机制,用来通知CPU发生了某种需要立即处理的事件

    分为:

    1. 内部中断 CPU执行程序的过程中,发生的一些硬件出错、运算出错事件(如分母为0、溢出等等),不可屏蔽
    2. 外部中断 外设发生某种情况,通过一个引脚的高、低电平变化来通知CPU (如外设产生了数据、某种处理完毕等等)

    二、中断处理原理

    任何一种中断产生,CPU都会暂停当前执行的程序,跳转到内存固定位置执行一段程序,该程序被称为总的中断服务程序,在该程序中区分中断源,然后进一步调用该中断源对应的处理函数。

    中断源对应的处理函数被称为分中断处理程序,一般每一个分中断处理程序对应一个外设产生的中断

    写驱动时,如果外设有中断,则需要编写一个函数(分中断处理程序)来处理这种中断

    三、中断接口

    3.1 中断申请

    int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
    /*
    参数:
    	irq:所申请的中断号
    	handler:该中断号对应的中断处理函数
    	flags:中断触发方式或处理方式 
    		触发方式:IRQF_TRIGGER_NONE 		//无触发
    		 	 	IRQF_TRIGGER_RISING 	//上升沿触发
    			 	IRQF_TRIGGER_FALLING  //下降沿触发
    				IRQF_TRIGGER_HIGH  	//高电平触发
    				IRQF_TRIGGER_LOW 		//低电平触发
    		处理方式:
    			   	IRQF_DISABLED		//用于快速中断,处理中屏蔽所有中断
    				IRQF_SHARED		  //共享中断
    		name:中断名 /proc/interrupts
    		dev:传递给中断例程的参数,共享中断时用于区分那个设备,一般为对应设备的结构体地址,无共享中断时写NULL
    返回值:成功:0 失败:错误码
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.2 中断释放

    void free_irq(unsigned int irq, void *dev_id)/*
    功能:释放中断号
    参数:
    	irq:设备号
    	dev_id:共享中断时用于区分那个设备一般强转成设备号,无共享中断时写NULL
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3.3 中断处理函数原型

    typedef irqreturn_t (*irq_handler_t)(int, void *);
    /*
    参数:
    	int:中断号
    	void*:对应的申请中断时的dev_id
    返回值:
    	typedef enum irqreturn irqreturn_t;	//中断返回值类型
    	enum irqreturn {
    		IRQ_NONE	= (0 << 0),
    		IRQ_HANDLED	= (1 << 0),
    		IRQ_WAKE_THREAD	= (1 << 1),
    	};
    	返回IRQ_HANDLED表示处理完了,返回IRQ_NONE在共享中断表示不处理
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    四、按键驱动

    按键原理图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y4QNx41B-1668921574240)(.\按键原理图.jpg)]
    exynos4412-fs4412.dts中增加节点

    mykey2_node {
    	compatible = "mykey2,key2";
    	key2-gpio = <&gpx1 1 0>;
    	interrupt-parent = <&gpx1>;
    	interrupts = <1 3>;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    按键驱动函数

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "fs4412_key.h"   //自己写的.h用“”引用,库用<>引用
    #include 
    #include 
    #include 
    
    int major = 11;				//主设备号
    int minor = 0;				//次设备号
    int fs4412key2_num = 1;			//设备数量
    
    struct fs4412key2_dev			//led设备结构体
    {
    	struct cdev mydev;		//设备结构体
    
    	int gpio;				//设备gpio成员变量
    	int irqno;				//中断
    	
    	struct keyvalue data;	//按键存储的数据
    	int newflag;			//新数据到来的标志位
    	spinlock_t lock;		//自旋锁
    	
    	wait_queue_head_t rq;	//读忙等待队列
    };
    
    struct fs4412key2_dev *pgmydev = NULL;     //定义一个设备结构体变量,用于调用结构体成员
    
    int fs4412key2_open(struct inode *pnode,struct file *pfile)	//打开文件函数
    {																					//inode类型结构体中i_cdev是mydev的地址														
    	pfile->private_data = (void *) (container_of(pnode->i_cdev,struct fs4412key2_dev,mydev));//知道成员地址可以得出结构体地址
        return 0;																		
    }
    
    int fs4412key2_close(struct inode *pnode,struct file *pfile)
    {
    	return 0;
    }
    
    ssize_t fs4412key2_read(struct file *pfile,char __user *puser,size_t count,loff_t *p_pos)
    {
    	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
    	int size = 0;
    	int ret = 0;
    
    	if(count < sizeof(struct keyvalue))
    	{
    		printk("expect read size is invalde\n");
    		return -1;
    	}
    
    	spin_lock(&pmydev->lock);		//上锁
    	if(!pmydev->newflag)		//无数据时进入
    	{
    		if(pfile->f_flags & O_NONBLOCK)	
    		{	//非阻塞
    			spin_unlock(&pmydev->lock);
    			printk("O_NONBLOCK NO Data Read\n");
    			return -1;
    		}
    		else
    		{	//阻塞
    			spin_unlock(&pmydev->lock);
    			ret = wait_event_interruptible(pmydev->rq,pmydev->newflag == 1);	//等待条件是pmydev->newflag == 1也就是有数据到来
    			if(ret)
    			{
    				printk("Wake up by signal\n");
    				return -ERESTARTSYS;
    			}
    			spin_lock(&pmydev->lock);
    		}
    	}
    	
    	if(count > sizeof(struct keyvalue))				//对读取数据的长度做一个限制
    	{
    		size = sizeof(struct keyvalue);
    	}
    	else
    	{
    		size = count;
    	}
    
    	ret = copy_to_user(puser,&pmydev->data,size);	//将内核数据拷贝到用户
    	if(ret)
    	{
    		spin_unlock(&pmydev->lock);
    		printk("copy_to_user failed\n");
    		return -1;
    	}
    
    	pmydev->newflag = 0;			//将数据标志物清零,为下次进入做准备
    	
    	spin_unlock(&pmydev->lock);		//开锁
    	
    	return size;
    }
    
    unsigned int fs4412key2_poll(struct file *pfile,poll_table *ptb)		//决定什么时候能读数据,这儿好像没用着。app上没些poll函数
    {			
    	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)pfile->private_data;
    	unsigned int mask = 0;
    	
    	poll_wait(pfile,&pmydev->rq,ptb);					//将读队列加入到表里,但是未休眠
    
    	spin_lock(&pmydev->lock);							//上锁
    	if(pmydev->newflag)									//当newflag为真是表示有数据,可读打开
    	{
    		mask |= POLLIN | POLLRDNORM;					//读打开
    	}
    	spin_unlock(&pmydev->lock);
    	
    	return mask;
    }
    
    struct file_operations myops = {					//设备的操作函数,自己写的子函数必须在这儿与内核函数关联起来才能被调用
            .owner = THIS_MODULE,
            .open = fs4412key2_open,
            .release = fs4412key2_close,
    		.read = fs4412key2_read,
    		.poll = fs4412key2_poll,
    };
    
    irqreturn_t key2_irq_handle(int no,void *arg)			//按键中断服务函数
    {
    	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
    	int status1 = 0;
    	int status2 = 0;
    	int status = 0;
    
    	status1 = gpio_get_value(pmydev->gpio);		//消抖
    	mdelay(1);
    	status2 = gpio_get_value(pmydev->gpio);
    
    	if(status1 != status2)
    	{
    		return IRQ_NONE;
    	}
    
    	status = status1;
    
    	spin_lock(&pmydev->lock);					//上锁
    	if(status == pmydev->data.status)			
    	{
    		spin_unlock(&pmydev->lock);
    		return IRQ_NONE;
    	}
    
    	pmydev->data.code = KEY2;
    	pmydev->data.status = status;
    	pmydev->newflag = 1;
    	
    	spin_unlock(&pmydev->lock);
    	wake_up(&pmydev->rq);
    
    	return IRQ_HANDLED;
    }
     
    int __init fs4412key2_init(void)				
    {
            int ret = 0;
            dev_t devno = MKDEV(major,minor);				//将主次设备号合成一个32位的设备号
            
            struct device_node *pnode = NULL;				//定义一个变量用于存储设备树中的一个节点
    
    		pnode = of_find_node_by_path ("/mykey2_node");		//从设备树获得key2节点  这个名称必须与设备树一致
    		if(NULL == pnode)		
    		{
    			printk("fialed of_find_node_by_path\n");
    			return -1;
    		}
    
    		pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);//给设备结构体申请一块内存,kmalloc是申请小内存效率高。GFP_KERNEL是可以进行忙等待,因为这个是任务上下文
    		if(NULL == pgmydev)			//申请失败
    		{
    			printk("kmalloc failed\n");
    			return -1;
    		}
    		pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);	//从设备树中提取gpio口
    		
    		pgmydev->irqno = irq_of_parse_and_map(pnode,0);			//获得设备树中的中断号并进行映射
    		
            /*申请设备号*/
            ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");	
            if(ret)
            {
                    ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");		//手动申请失败时自动申请
                    if(ret)
                    {
                            printk("get devno failed\n");
                            kfree(pgmydev);							//申请失败时释放掉设备结构体
                            return -1;
                    }
                    major = MAJOR(devno);
            }
    	
    	    /*给struct cdev对象指定操作函数集*/
    	    cdev_init(&pgmydev->mydev,&myops);				
    	
    	    /*将struct cdev对象添加到内核对应的数据结构里*/
    	    pgmydev->mydev.owner = THIS_MODULE;
            cdev_add(&pgmydev->mydev,devno,fs4412key2_num);
            
    		init_waitqueue_head(&pgmydev->rq);		//对等待队列头做初始化
         	spin_lock_init(&pgmydev->lock);			//对自旋锁做初始化,因为是异常上下文所以用自旋锁,他可以忙等待
         	
         	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);//中断申请函数,初始化要放在锁初始化后面,要不然进入中断服务程序锁没初始化会出问题,
    		if(ret)
    		{
    			printk("request_irq failed\n");
    			kfree(pgmydev);
    			pgmydev = NULL;
    			return -1;
    		}	
         
            return 0;
    }
    
    void __exit fs4412key2_exit(void)
    {
            dev_t devno = MKDEV(major,minor);
    		free_irq(pgmydev->irqno,pgmydev);
            cdev_del(&pgmydev->mydev);
            unregister_chrdev_region(devno,fs4412key2_num);
            kfree(pgmydev);
            pgmydev = NULL;
    }
    
    MODULE_LICENSE("GPL");
    module_init(fs4412key2_init);
    module_exit(fs4412key2_exit);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242

    fs4412_key.h

    #ifndef FS4412_KEY_H
    #define FS4412_KEY_H
    
    enum KEYCODE
    {
    	KEY2 = 1002,
    	KEY3,
    	KEY4,
    };
    
    enum KEY_STATUS
    {
    	KEY_DOWN = 0,
    	KEY_UP,
    };
    
    struct keyvalue
    {
    	int code;
    	int status;
    };
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    app

    #include 
    #include 
    #include 
    #include 
    #include "leddrv.h"
    #include 
    #include 
    
    int main(int argc,char *argv[])
    {
    	int fd = -1;
    	
    	if(argc < 2)
    	{
    		printf("The argument is too few\n");
    		return -1;
    	}
    	
    	fd = open(argv[1],O_RDONLY);		//
    	if(fd < 0)
    	{
    		printf("open %s failed\n",argv[1]);
    		return 3;
    	}
    	
    	while((ret = read(fd,&keydata,sizeof(keydata))) == sizeof(keydata))
    	{
    		if(keydata.status == KEY_DOWN)
    		{
    			printf("Key2 is down!\n");
    		}
    		else
    		{
    			printf("Key2 is up!\n");
    		}
    	}
    	
    	close(fd);
    	fd = -1;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    一、上半部与下半部

    起源:

    1. 中断处理程序执行时间过长引起的问题
    2. 有些设备的中断处理程序必须要处理一些耗时操作

    二、下半部机制之tasklet ---- 基于软中断(异常上下文)

    6.1 结构体

    struct tasklet_struct

    {

    ​ struct tasklet_struct *next;

    ​ unsigned long state;

    ​ atomic_t count;

    ​ void (*func)(unsigned long); //重点关注

    ​ unsigned long data; //重点关注

    };

    6.2 定义tasklet的中断底半部处理函数

    void tasklet_func(unsigned long data);

    6.3 初始化tasklet

    DECLARE_TASKLET(name, func, data);
    /*
    定义变量并初始化
    参数:name:中断底半部tasklet的名称
    	 Func:中断底半部处理函数的名字
    	 data:给中断底半部处理函数传递的参数
    */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long),unsigned long data)
    
    • 1

    6.4 调度tasklet(上半部运行完后调用下面这个函数,运行上面那个*func函数指针指向的函数 )

    void tasklet_schedule(struct tasklet_struct *t)
    //参数:t:tasklet的结构体
    
    • 1
    • 2

    三、按键驱动之tasklet版

    在这里插入图片描述

    struct fs4412key2_dev			//led设备结构体
    {
    	struct cdev mydev;		//设备结构体
    
    	int gpio;				//设备gpio成员变量
    	int irqno;				//中断
    	
    	struct keyvalue data;	//按键存储的数据
    	int newflag;			//新数据到来的标志位
    	spinlock_t lock;		//自旋锁
    	
    	wait_queue_head_t rq;	//读忙等待队列
    	struct tasklet_struct tsk;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在init函添加

    在这里插入图片描述

    int __init fs4412key2_init(void)				
    {
            int ret = 0;
            dev_t devno = MKDEV(major,minor);				//将主次设备号合成一个32位的设备号
    		int ret = 0;
            struct device_node *pnode = NULL;				//定义一个变量用于存储设备树中的一个节点
    
    		pnode = of_find_node_by_path ("/mykey2_node");		//从设备树获得key2节点
    		if(NULL == pnode)		
    		{
    			printk("fialed of_find_node_by_path\n");
    			return -1;
    		}
    
    		pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);//给设备结构体申请一块内存,kmalloc是申请小内存效率高。GFP_KERNEL是可以进行忙等待,因为这个是任务上下文
    		if(NULL == pgmydev)			//申请失败
    		{
    			printk("kmalloc failed\n");
    			return -1;
    		}
    		pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);	//从设备树中提取gpio口
    		pgmydev->irqno = irq_of_parse_and_map(pnode,0);			//获得设备树中的中断号并进行映射
    
            /*申请设备号*/
            ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");	
            if(ret)
            {
                    ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");		//手动申请失败时自动申请
                    if(ret)
                    {
                            printk("get devno failed\n");
                            kfree(pgmydev);							//申请失败时释放掉设备结构体
                            return -1;
                    }
                    major = MAJOR(devno);
            }
    	
    	    /*给struct cdev对象指定操作函数集*/
    	    cdev_init(&pgmydev->mydev,&myops);				
    	
    	    /*将struct cdev对象添加到内核对应的数据结构里*/
    	    pgmydev->mydev.owner = THIS_MODULE;
            cdev_add(&pgmydev->mydev,devno,fs4412key2_num);
            
    		init_waitqueue_head(&pgmydev->rq);		//对等待队列头做初始化
         	spin_lock_init(&pgmydev->lock);			//对自旋锁做初始化,因为是异常上下文所以用自旋锁,他可以忙等待
         
         	tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);//
         
         	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);//中断申请函数,初始化要放在锁初始化后面,要不然进入中断服务程序锁没初始化会出问题,
    		if(ret)
    		{
    			printk("request_irq failed\n");
    			kfree(pgmydev);
    			pgmaydev = NULL;
    			return -1;
    		}		
            return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    上下中断函数

    irqreturn_t key2_irq_handle(int no,void *arg)	//上
    {
    	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
    	
    	tasklet_schedule(&pmydev->tsk);//进入下半部
    }
    
    void bottom_irq_func(unsigned long *arg)			//下半部:一般处理一些耗时操作
    {
    	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
    	int status1 = 0;
    	int status2 = 0;
    	int status = 0;
    
    	status1 = gpio_get_value(pmydev->gpio);		//消抖
    	mdelay(1);
    	status2 = gpio_get_value(pmydev->gpio);
    
    	if(status1 != status2)
    	{
    		return;
    	}
    
    	status = status1;
    
    	spin_lock(&pmydev->lock);					//上锁
    	if(status == pmydev->data.status)			
    	{
    		spin_unlock(&pmydev->lock);
    		return;
    	}
    
    	pmydev->data.code = KEY2;
    	pmydev->data.status = status;
    	pmydev->newflag = 1;
    	
    	spin_unlock(&pmydev->lock);
    	wake_up(&pmydev->rq);
    
    	return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    四、下半部机制之workqueue ----- 基于内核线程(任务上下文)

    8.1 工作队列结构体:

    typedef void (*work_func_t)(struct work_struct *work)

    struct work_struct {

    ​ atomic_long_t data;

    ​ struct list_head entry;

    ​ work_func_t func;

    #ifdef CONFIG_LOCKDEP

    ​ struct lockdep_map lockdep_map;

    #endif

    };

    8.2 定义工作队列底半部处理函数

    void work_queue_func(struct work_struct *work);

    8.3 初始化工作队列

    struct work_struct work_queue;

    初始化:绑定工作队列及工作队列的底半部处理函数

    INIT_WORK(struct work_struct * pwork, _func) ;

    参数:pwork:工作队列

    ​ func:工作队列的底半部处理函数

    8.4 工作队列的调度函数

    bool schedule_work(struct work_struct *work);

    五、按键驱动之workqueue版

    struct fs4412key2_dev			//led设备结构体
    {
    	struct cdev mydev;		//设备结构体
    
    	int gpio;				//设备gpio成员变量
    	int irqno;				//中断
    	
    	struct keyvalue data;	//按键存储的数据
    	int newflag;			//新数据到来的标志位
    	spinlock_t lock;		//自旋锁
    	
    	wait_queue_head_t rq;	//读忙等待队列
    //	struct tasklet_struct tsk;
    	struct work_struct wk;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    int __init fs4412key2_init(void)				
    {
            int ret = 0;
            dev_t devno = MKDEV(major,minor);				//将主次设备号合成一个32位的设备号
    		int ret = 0;
            struct device_node *pnode = NULL;				//定义一个变量用于存储设备树中的一个节点
    
    		pnode = of_find_node_by_path ("/mykey2_node");		//从设备树获得key2节点
    		if(NULL == pnode)		
    		{
    			printk("fialed of_find_node_by_path\n");
    			return -1;
    		}
    
    		pgmydev = (struct fs4412key2_dev *)kmalloc(sizeof(struct fs4412key2_dev),GFP_KERNEL);//给设备结构体申请一块内存,kmalloc是申请小内存效率高。GFP_KERNEL是可以进行忙等待,因为这个是任务上下文
    		if(NULL == pgmydev)			//申请失败
    		{
    			printk("kmalloc failed\n");
    			return -1;
    		}
    		pgmydev->gpio = of_get_named_gpio(pnode,"key2-gpio",0);	//从设备树中提取gpio口
    		pgmydev->irqno = irq_of_parse_and_map(pnode,0);			//获得设备树中的中断号并进行映射
    
            /*申请设备号*/
            ret = register_chrdev_region(devno,fs4412key2_num,"fs4412key2");	
            if(ret)
            {
                    ret = alloc_chrdev_region(&devno,minor,fs4412key2_num,"fs4412key2");		//手动申请失败时自动申请
                    if(ret)
                    {
                            printk("get devno failed\n");
                            kfree(pgmydev);							//申请失败时释放掉设备结构体
                            return -1;
                    }
                    major = MAJOR(devno);
            }
    	
    	    /*给struct cdev对象指定操作函数集*/
    	    cdev_init(&pgmydev->mydev,&myops);				
    	
    	    /*将struct cdev对象添加到内核对应的数据结构里*/
    	    pgmydev->mydev.owner = THIS_MODULE;
            cdev_add(&pgmydev->mydev,devno,fs4412key2_num);
            
    		init_waitqueue_head(&pgmydev->rq);		//对等待队列头做初始化
         	spin_lock_init(&pgmydev->lock);			//对自旋锁做初始化,因为是异常上下文所以用自旋锁,他可以忙等待
         
     //    	tasklet_init(&pgmydev->tsk,bottom_irq_func,(unsigned long)pgmydev);//tasklet的
         	INIT_WORK(&pgmydev->wk,);
         	
         	ret = request_irq(pgmydev->irqno,key2_irq_handle,IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,"fs4412key2",pgmydev);//中断申请函数,初始化要放在锁初始化后面,要不然进入中断服务程序锁没初始化会出问题,
    		if(ret)
    		{
    			printk("request_irq failed\n");
    			kfree(pgmydev);
    			pgmaydev = NULL;
    			return -1;
    		}		
            return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    irqreturn_t key2_irq_handle(int no,void *arg)	//上
    {
    	struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;
    	
    //	tasklet_schedule(&pmydev->tsk);//进入下半部
    	schedule_work(&pmydev->wk);
    
    	return IRQ_HANDLED;
    }
    
    //void bottom_irq_func(unsigned long *arg)				//tasklet的
    void bottom_irq_func(struct work_struct *pwk)			//下半部:一般处理一些耗时操作 ,参数改
    {
    	//struct fs4412key2_dev *pmydev = (struct fs4412key2_dev *)arg;   //tasklet的
    	struct fs4412key2_dev *pmydev = container_of(pwk,struct fs4412key2_dev,wk);  //改
    	int status1 = 0;
    	int status2 = 0;
    	int status = 0;
    
    	status1 = gpio_get_value(pmydev->gpio);		//消抖
    	mdelay(1);
    	status2 = gpio_get_value(pmydev->gpio);
    
    	if(status1 != status2)
    	{
    		return;
    	}
    
    	status = status1;
    
    	spin_lock(&pmydev->lock);					//上锁
    	if(status == pmydev->data.status)			
    	{
    		spin_unlock(&pmydev->lock);
    		return;
    	}
    
    	pmydev->data.code = KEY2;
    	pmydev->data.status = status;
    	pmydev->newflag = 1;
    	
    	spin_unlock(&pmydev->lock);
    	wake_up(&pmydev->rq);
    
    	return;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    六、下半部机制比较

    任务机制

    ​ workqueue ----- 内核线程 能睡眠 运行时间无限制

    异常机制 ------- 不能睡眠 下半部执行时间不宜太长( < 1s)

    ​ 软中断 ---- 接口不方便

    ​ tasklet ----- 无具体延后时间要求时

    ​ 定时器 -----有具体延后时间要求时

  • 相关阅读:
    Oracle归档日志暴增排查优化
    冒泡排序概览(java+R_优化以及双向冒泡代码)
    一个22届被裁前端思想上得转变
    【会议资源】2022年第三届自动化科学与工程国际会议(JCASE 2022)
    .NET性能优化-你应该为集合类型设置初始大小
    Linux常用命令——chown命令
    红队隧道应用篇之Netsh端口转发
    【亚马逊云+阿里万网】| 实现网站证书配置和域名解析
    【图像分割】基于PCA结合模糊聚类算法FCM实现SAR图像分割附matlab代码
    3.51 什么是平坦式原理图?什么是层次式电路设计?它的优点有哪些?
  • 原文地址:https://blog.csdn.net/afddasfa/article/details/127947987