驱动开发之阻塞与按键驱动
驱动开发之阻塞与按键驱动:
内核源码分析:
container_of((drv), struct platform_driver,driver)
ptr type member
#define container_of(ptr, type, member) ({
const typeof( ((type *)0)->member ) *__mptr = (ptr);
const typeof( ((struct platform_driver *)0)->driver) *__mptr =drv;// driver类型是struct device_driver
typeof( ((struct platform_driver *)0)->driver)//获取了struct device_driver类型
struct device_driver *__mptr = drv;
(type *)( (char *)__mptr - offsetof(type,member) );
})
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
TYPE:struct platform_driver
MEMBER:driver
(TYPE *)0 <==> (struct platform_driver *)0 假设platform_driver首地址为0
(TYPE *)0)->MEMBER 以0地址为标准引用了driver成员。
&((TYPE *)0)->MEMBER) 取出driver成员的地址(相对于0的地址)
(size_t) &((TYPE *)0)->MEMBER) 将driver成员的地址转成了一个整数
(char *)__mptr 是一个已知的成员地址
offsetof(type,member) 是一个偏移量
成员地址 - 偏移量 = 整个结构体的首地
同步和互斥:
当前的os是运行在多核cpu上。 宏观和微观都可以同时执行多个任务。
如何避免资源竞争?
1.中断屏蔽:只适用于单核cpu
1 关闭中断:local_irq_disable();
2 临界区
3 开启中断:local_irq_enable();
2.原子操作:不可拆分操作
1 atomic_set();//初始化原子变量
2 atomic_dec_and_test();//对原子变量-1并且和-进行比较
3 临界区
4 atomic_inc();//对原子变量+1
3.互斥锁
1 mutex_init();//初始化互斥锁
2 mutex_lock();//上锁,如果申请不到锁会阻塞
3 临界区
4 mutex_unlock();//解锁
4.信号量
1 sema_init();//初始化信号量
2 down();//申请资源,如果申请不到资源会阻塞
3 临界区
4 up();//释放资源
5.自旋锁
1 spin_lock_init();//初始化自旋锁
2 spin_lock();//上锁,如果申请不到锁会自旋(循环)
3 spin_unlock();//解锁
列如:
while(1);//一直执行,一直在占用cpu(此时cpu不可以分配时间片给其他任务)
printf("hello\n");
pthread_join();//等待,已经释放了cpu资源(此时cpu可以分配时间片给其他任务)
printf("hello\n");
自旋锁和互斥锁:
自旋锁和互斥锁都可以实现互斥,但是自旋锁不会阻塞,互斥锁会阻塞,自旋锁占用资源,互斥锁阻塞时不占用资源。互斥锁的临界区中可以有延时函数,自旋锁的临界区中不能有延时函数。
自旋锁:临界区中不能有睡眠,而且它的上锁函数本身可能会自旋不会阻塞。
互斥锁:临界区中能有睡眠,而且申请不到锁会阻塞。
注:中断处理函数:保证尽快完成,内部不能阻塞。
------->如果中断处理函数中需要使用同步互斥机制,应该选择自旋锁。
IO模型:
阻塞
非阻塞
多路复用
异步通知
阻塞:
内核实现阻塞使用了等待队列机制(双向循环链表)
等待队列头
struct __wait_queue_head {
spinlock_t lock;//自旋锁变量
struct list_head task_list;//构建双向循环链表
};
typedef struct __wait_queue_head wait_queue_head_t;
等待队列项
typedef struct __wait_queue wait_queue_t;
typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int flag
struct __wait_queue {
unsigned int flags;
void *private;//存放的是当前进程结构体的指针
wait_queue_func_t func;//函数指针
struct list_head task_list;//构建双向循环链表
};
等待队列的函数接口:
#define init_waitqueue_head(q)
do {
static struct lock_class_key __key;
__init_waitqueue_head((q), #q, &__key);
} while (0)
----->void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lo
{
spin_lock_init(&q->lock);//初始化自旋锁
INIT_LIST_HEAD(&q->task_list);//为等待队列头创建空的双向循环链表
}
阻塞接口:
wait_event(wait_queue_head_t,条件);//实现阻塞,不可被信号中断
wait_event_timeout();
wait_event_interruptible();//实现阻塞,可被信号中断
wait_event_interruptible_timeout();
sleep_on();//实现阻塞
唤醒接口:
wake_up(wait_queue_head_t *);
wake_up_interruptible();
1 #include <linux/init.h> 2 #include <linux/module.h> 3 #include <linux/fs.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include <linux/sched.h> 7 8 MODULE_LICENSE("GPL"); 9 10 int major; 11 struct class *cls; 12 struct device *devs; 13 14 char kbuf[1024]; 15 wait_queue_head_t rq; 16 wait_queue_head_t wq; 17 int flag = 0; 18 19 int demo_open(struct inode *inode,struct file *filp) 20 { 21 return 0; 22 } 23 24 int demo_close(struct inode *inode,struct file *filp) 25 { 26 return 0; 27 } 28 29 ssize_t demo_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off) 30 { 31 int ret; 32 ssize_t n; 33 34 wait_event(rq,flag != 0); 35 if(strlen(kbuf) + 1 > size) 36 n = size; 37 else 38 n = strlen(kbuf) + 1; 39 40 ret = copy_to_user(ubuf,kbuf,n); 41 if(ret != 0) 42 { 43 return -EFAULT; 44 } 45 wake_up(&wq); 46 flag = 0; 47 48 return n; 49 } 50 51 ssize_t demo_write(struct file *filp,const char __user *ubuf,size_t size,loff_t *off) 52 { 53 ssize_t n; 54 int ret; 55 56 wait_event(wq,flag == 0); 57 if(size > sizeof(kbuf)) 58 n = sizeof(kbuf); 59 else 60 n = size; 61 ret = copy_from_user(kbuf,ubuf,n); 62 if(ret != 0) 63 return -EFAULT; 64 printk("%s\n",kbuf); 65 wake_up(&rq); 66 flag = 1; 67 return n; 68 } 69 struct file_operations fops = { 70 .owner = THIS_MODULE, 71 .open = demo_open, 72 .read = demo_read, 73 .write = demo_write, 74 .release = demo_close, 75 }; 76 77 int demo_init(void) 78 { 79 major = register_chrdev(0,"demo",&fops); 80 81 cls = class_create(THIS_MODULE,"demo"); 82 83 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"demo"); 84 85 init_waitqueue_head(&rq); 86 init_waitqueue_head(&wq); 87 return 0; 88 } 89 module_init(demo_init); 90 91 void demo_exit(void) 92 { 93 device_destroy(cls,MKDEV(major,0)); 94 class_destroy(cls); 95 unregister_chrdev(major,"demo"); 96 return; 97 } 98 module_exit(demo_exit);
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd; 9 10 fd = open("/dev/demo",O_RDWR); 11 if(fd == -1) 12 { 13 perror("open"); 14 return -1; 15 } 16 17 pid_t pid; 18 19 char buf[64]; 20 21 pid = fork(); 22 if(pid == -1) 23 { 24 25 } 26 else if(pid == 0) 27 { 28 read(fd,buf,sizeof(buf)); 29 printf("user read:%s\n",buf); 30 } 31 else 32 { 33 sleep(5); 34 write(fd,"hello",6); 35 } 36 close(fd); 37 return 0; 38 }
分析接口:
vi -t wait_event
-->#define wait_event(wq, condition)
do {
if (condition) //一定要保证最初这个条件为假,而且还建议使用变量表达式。
break;
__wait_event(wq, condition);
} while (0)
-->#define __wait_event(wq, condition) (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0, schedule())
-->#define ___wait_event(wq, condition, state, exclusive, ret, cmd)
({
__label__ __out;
wait_queue_t __wait;//定义了一个等待队列项
long __ret = ret;
INIT_LIST_HEAD(&__wait.task_list);给等待队列项创建双向循环链表
if (exclusive) 条件为假
__wait.flags = WQ_FLAG_EXCLUSIVE;
else
__wait.flags = 0; //被执行
for (;;) { 无限循环
long __int = prepare_to_wait_event(&wq, &__wait, state);
if (condition)
break;
cmd;
}
finish_wait(&wq, &__wait);
__out: __ret;
})
进入prepare_to_wait_event(&wq, &__wait, state);
wait->private = current;//存放了当前进程指针
wait->func = autoremove_wake_function; //存放了一个函数接口,用来唤醒。
给等待队列项的成员初始化
if (list_empty(&wait->task_list)) {判断等待队列项是否为空链表
if (wait->flags & WQ_FLAG_EXCLUSIVE)//条件为假,因为上面代码将flags = 0
__add_wait_queue_tail(q, wait);
else
__add_wait_queue(q, wait);//将等待队列项加入到等待队列中
}
set_current_state(state);//将当前进程的属性设置为TASK_UNINTERRUPTIBLE
回到___wait_event(wq, condition, state, exclusive, ret, cmd)
继续执行
for(::)
{
if (condition)暂时还是假
break;
cmd;//进程调度器,调度器执行前进程已经存放在了等待队列中。
}
进程调度器被执行时会判断进程的属性,如果进程的属性为TASK_UNINTERRUPTIBLE,进程调度器会将运行队列中的进程删除。
cpu给进程分配时间片前,会遍历运行队列,只有这里的进程才具备拥有时间片的资格。
所以说:一旦进程从运行队列中删除后可能出现阻塞。
唤醒接口:
vi -t wake_up
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
--> __wake_up_common(q, mode, nr_exclusive, 0, key);
--> static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int wake_flags, void *key)
{
wait_queue_t *curr, *next;
list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
unsigned flags = curr->flags;
if (curr->func(curr, mode, wake_flags, key) &&
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
执行curr->func(curr, mode, wake_flags, key)时,本质上在调用prepare_to_wait_event(&wq, &__wait, state);中的autoremove_wake_function,这个函数将进程的状态设置为TASK_WAKING。
时刻记住wait_event内部的for循环是一直在执行的,一直在执行进程调度器。进程调度器会识别到
TASK_WAKING,将进程信息拷贝到运行队列中,但是此时等待队列中还包含进程信息。一旦重新将进程拿到运行队列后,那么需要指定条件为真。跳出循环后执行finish_wait(&wq, &__wait); 将进程从等待队列中删除
死锁:
线程1:
pthread_mutex_lock(&a);
pthread_mutex_lock(&b);
临界区
pthread_mutex_unlock(&a);
pthread_mutex_unlock(&b);
线程2:
pthread_mutex_lock(&b);
pthread_mutex_lock(&a);
临界区
pthread_mutex_unlock(&a);
pthread_mutex_unlock(&b);
出现死锁
避免的方式:保证多个线程申请锁的顺序一致。
按键驱动(使用中断的方式来实现按键驱动):
按键的硬件特性:
核心板:
XEINT9/KP_COL1/ALV_DBG5/GPX1_1 ---> UART_RING
说明UART_RING引脚使用外部中断9,控制引脚是GPX1_1
在厂家封装的设备树中对应的gpio控制器的引脚名称叫做gpx1
进入三星手册中:
第九章
25 57 – EINT[9] External Interrupt
说明外部中断9使用的共享中断的中断号为25
vi exynos4x12-pinctl.dtsi
gpx1: gpx1 {
gpio-controller;//说明这个节点是一个gpio控制器
#gpio-cells = <2>;
interrupt-controller;//同时也是一个中断控制器
interrupt-parent = <&gic>;//gpx1中断控制器的中断父节点是gic
interrupts = <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
索引值 0 1 2 3
<0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
4 5 6 7
#interrupt-cells = <2>;//当前节点的中断子节点中的interrupts属性有2个值
};
UART_RING按键连接到了gpx1中断控制器上
interrupts = <val1 val2 val3>;//一般出现在控制器节点中
val1:中断类型 0代表共享中断、1代表私有中断
val2:硬件中断号
val3:中断触发方式
1 上升沿触发
2 下降沿触发
4 高电平触发
8 低电平触发
interrupts = <val1 val2>;//用于一下普通节点
val1:硬件中断号的索引值
val2:代表中断触发方式
自己的设备树如何实现?
fs4412-key{
compatible = "fs4412,key";
interrupt-parent = <&gpx1>;
interrupts = <1 2>,<2 2>;
};
make dtbs
cp exynos4412-fs4412.dtb /tftpboot
注册中断接口:
1 int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
2 功能:给内核注册中断(给struct irqaction结构体初始化)
3 参数1:虚拟中断号
4 参数2:中断处理函数 :irqreturn_t (*)(int ,void *);
5 参数1:虚拟中断号
6 参数2:只有共享中断可以使用
7 返回值:如果中断被当前设备操作直接返回IRQ_HANDLED
8 参数3:指定中断触发方式,也可以指定中断类型
9 中断触发方式:
10 上升沿触发:IRQF_TRIGGER_RISING
11 下降沿触发:IRQF_TRIGGER_FALLING
12 高电平触发:IRQF_TRIGGER_HIGH
13 低电平触发:IRQF_TRIGGER_LOW
14 参数4:出现在/proc/interrupts文件中的一个名称,代表中断名称
15 参数5:共享中断使用,其他中断传递NULL
注销中断接口:
1 void free_irq(unsigned int irq,void *);
按键驱动实现过程:
1、模块三要素
2、注册platform驱动,注销platform驱动
3、定义并且初始化platform_driver结构体
4、如果和设备树匹配成功执行probe函数
在probe中需要搭建字符设备框架、创建设备类、创建设备文件、注册中断
5、如果有按键按下则执行中断处理函数
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/platform_device.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include <linux/irqreturn.h> 7 #include <linux/interrupt.h> 8 #include <linux/sched.h> 9 10 int major; 11 12 struct class *cls; 13 struct device *devs; 14 struct resource *res_key2; 15 struct resource *res_key3; 16 17 int key; 18 wait_queue_head_t keyq; 19 int flag = 0; 20 21 irqreturn_t fs4412_key_handler(int irqno,void *id) 22 { 23 if(irqno == res_key2->start) 24 key = 2; 25 if(irqno == res_key3->start) 26 key = 3; 27 // wake_up(&keyq); 28 wake_up_interruptible(&keyq); 29 flag = 1; 30 return IRQ_HANDLED; 31 } 32 33 int fs4412_key_open(struct inode *inode ,struct file *filp) 34 { 35 return 0; 36 } 37 38 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off) 39 { 40 int ret; 41 42 // wait_event(keyq,flag != 0); 43 wait_event_interruptible(keyq,flag != 0); 44 ret = copy_to_user(ubuf,&key,sizeof(key)); 45 46 flag = 0; 47 return sizeof(key); 48 } 49 50 struct file_operations fops = { 51 .owner = THIS_MODULE, 52 .open = fs4412_key_open, 53 .read = fs4412_key_read, 54 }; 55 56 int fs4412_key_probe(struct platform_device *pdev) 57 { 58 int ret; 59 printk("match ok\n"); 60 61 major = register_chrdev(0,"key",&fops); 62 cls = class_create(THIS_MODULE,"key"); 63 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key"); 64 65 res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<> 66 res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1); 67 ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL); 68 ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL); 69 70 init_waitqueue_head(&keyq); 71 return 0; 72 } 73 74 int fs4412_key_remove(struct platform_device *pdev) 75 { 76 free_irq(res_key3->start,NULL); 77 free_irq(res_key2->start,NULL); 78 device_destroy(cls,MKDEV(major,0)); 79 class_destroy(cls); 80 unregister_chrdev(major,"key"); 81 return 0; 82 } 83 84 struct of_device_id fs4412_dt_tbl[] = { 85 { 86 .compatible = "fs4412,key", 87 }, 88 { 89 90 },//必须要有个空大括号如果没有可能会发生段错误 91 }; 92 93 94 struct platform_driver pdrv = { 95 .driver = { 96 .name = "fs4412-key", 97 .of_match_table = fs4412_dt_tbl, 98 }, 99 .probe = fs4412_key_probe, 100 .remove = fs4412_key_remove, 101 }; 102 103 module_platform_driver(pdrv); 104 MODULE_LICENSE("GPL"); 105
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd; 9 10 fd = open("/dev/key",O_RDWR); 11 if(fd == -1) 12 { 13 perror("open"); 14 return -1; 15 } 16 17 int key; 18 while(1) 19 { 20 read(fd,&key,sizeof(key)); 21 printf("key = %d\n",key); 22 } 23 return 0; 24 }
定时器接口:
1 struct timer_list
2 {
3 void (*function)(unsigned long);//定时器中断处理函数
4 };
1 init_timer(struct timer_list *);//初始化定时器
1 add_timer(struct timer_list *);//给内核注册定时器
1 int mod_timer(struct timer_list *timer, unsigned long expires)
2 参数1:
3 参数2:定时的时间值
4 绝对时间
5 相对时间:jiffies代表从系统开机到mod_timer执行完成的时间差
6 jiffies + 30;定时30ms
7 jiffies + 3 * HZ / 100;定时30ms
1 void del_timer(struct timer_list *);
1 #include <linux/module.h> 2 #include <linux/fs.h> 3 #include <linux/platform_device.h> 4 #include <linux/device.h> 5 #include <asm/uaccess.h> 6 #include <linux/irqreturn.h> 7 #include <linux/interrupt.h> 8 #include <linux/sched.h> 9 10 int major; 11 12 struct class *cls; 13 struct device *devs; 14 struct resource *res_key2; 15 struct resource *res_key3; 16 17 int key; 18 wait_queue_head_t keyq; 19 int flag = 0; 20 struct timer_list t; 21 int glo_irqno; 22 23 irqreturn_t fs4412_key_handler(int irqno,void *id) 24 { 25 glo_irqno = irqno; 26 mod_timer(&t,jiffies + 30); 27 return IRQ_HANDLED; 28 } 29 30 void fs4412_key_timer_handler(unsigned long data) 31 { 32 if(glo_irqno == res_key2->start) 33 key = 2; 34 if(glo_irqno == res_key3->start) 35 key = 3; 36 // wake_up(&keyq); 37 wake_up_interruptible(&keyq); 38 flag = 1; 39 } 40 41 int fs4412_key_open(struct inode *inode ,struct file *filp) 42 { 43 return 0; 44 } 45 46 ssize_t fs4412_key_read(struct file *filp,char __user *ubuf,size_t size,loff_t *off) 47 { 48 int ret; 49 50 // wait_event(keyq,flag != 0); 51 wait_event_interruptible(keyq,flag != 0); 52 ret = copy_to_user(ubuf,&key,sizeof(key)); 53 54 flag = 0; 55 return sizeof(key); 56 } 57 58 struct file_operations fops = { 59 .owner = THIS_MODULE, 60 .open = fs4412_key_open, 61 .read = fs4412_key_read, 62 }; 63 64 int fs4412_key_probe(struct platform_device *pdev) 65 { 66 int ret; 67 printk("match ok\n"); 68 69 major = register_chrdev(0,"key",&fops); 70 cls = class_create(THIS_MODULE,"key"); 71 devs = device_create(cls,NULL,MKDEV(major,0),NULL,"key"); 72 73 res_key2 = platform_get_resource(pdev,IORESOURCE_IRQ,0);//索引interrupts = <>,<>第一个<> 74 res_key3 = platform_get_resource(pdev,IORESOURCE_IRQ,1); 75 ret = request_irq(res_key2->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key2",NULL); 76 ret = request_irq(res_key3->start,fs4412_key_handler,IRQF_TRIGGER_FALLING,"key3",NULL); 77 78 init_waitqueue_head(&keyq); 79 80 init_timer(&t); 81 t.function = fs4412_key_timer_handler;//定时器中断处理函数 82 add_timer(&t);//让内核认识定时器中断处理函数 83 return 0; 84 } 85 86 int fs4412_key_remove(struct platform_device *pdev) 87 { 88 del_timer(&t); 89 free_irq(res_key3->start,NULL); 90 free_irq(res_key2->start,NULL); 91 device_destroy(cls,MKDEV(major,0)); 92 class_destroy(cls); 93 unregister_chrdev(major,"key"); 94 return 0; 95 } 96 97 struct of_device_id fs4412_dt_tbl[] = { 98 { 99 .compatible = "fs4412,key", 100 }, 101 {}, 102 }; 103 104 105 struct platform_driver pdrv = { 106 .driver = { 107 .name = "fs4412-key", 108 .of_match_table = fs4412_dt_tbl, 109 }, 110 .probe = fs4412_key_probe, 111 .remove = fs4412_key_remove, 112 }; 113 114 module_platform_driver(pdrv); 115 MODULE_LICENSE("GPL");
1 #include <stdio.h> 2 #include <sys/types.h> 3 #include <sys/stat.h> 4 #include <fcntl.h> 5 6 int main(int argc, const char *argv[]) 7 { 8 int fd; 9 10 fd = open("/dev/key",O_RDWR); 11 if(fd == -1) 12 { 13 perror("open"); 14 return -1; 15 } 16 17 int key; 18 while(1) 19 { 20 read(fd,&key,sizeof(key)); 21 printf("key = %d\n",key); 22 } 23 return 0; 24 }