驱动开发之阻塞与按键驱动

驱动开发之阻塞与按键驱动:

内核源码分析:
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);
wait_demo
 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 }
wait_app.c

 

分析接口:

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     
key_int.c
 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 }
app.c

 

定时器接口:

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");
key_timer.c
 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 }
app.c

 



posted @ 2018-09-17 18:28  向往的生活ing  阅读(561)  评论(0编辑  收藏  举报