Chinaunix首页 | 论坛 | 博客
  • 博客访问: 299002
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 715
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-20 20:38
文章分类
文章存档

2016年(20)

2015年(56)

分类: 嵌入式

2015-06-01 19:01:40

字符驱动中原子变量与信号量的使用
15年5月5日22:26:33
程序代码:
  1 #include
  2 #include
  3 #include
  4 #include
  5 #include
  6 #include
  7 #include
  8 #include
  9 #include
 10 #include
 11 #include
 12 #include
 13
 14 static struct class *sixth_drv_class;
 15 static struct class_device *sixth_drv_class_dev;
 16
 17 volatile unsigned long *gpfcon;
 18 volatile unsigned long *gpfdat;
 19
 20 volatile unsigned long *gpgcon;
 21 volatile unsigned long *gpgdat;
 22
 23 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
 24 static volatile int ev_press = 0;
 25
 26 static struct fasync_struct *button_async;
 27
 28 struct pin_desc{
 29     unsigned int pin;
 30     unsigned int key_val;
 31 };
 32
 33 static unsigned char key_val;
 34
 35 static struct pin_desc pins_desc[4] = {
 36     {S3C2410_GPF0, 0X01},
 37     {S3C2410_GPF2, 0X02},
 38     {S3C2410_GPG3, 0X03},
 39     {S3C2410_GPG11, 0X04},
 40 };
 41
 42 //static atomic_t canopen = ATOMIC_INIT(1);
 43 static DECLARE_MUTEX(button_lock);
 44
 45 static irqreturn_t buttons_irq(int irq, void *dev_id)
 46 {
 47     struct pin_desc * pindesc = (struct pin_desc *)dev_id;
 48     unsigned int pinval;
 49
 50     pinval = s3c2410_gpio_getpin(pindesc->pin);
 51
 52     if(pinval)
 53     {
 54         key_val = 0x80 | pindesc->key_val;
 55     }
 56     else
 57     {
 58         key_val = pindesc->key_val;
 59     }
 60
 61     ev_press = 1;
 62     wake_up_interruptible(&button_waitq);
 63
 64     kill_fasync(&button_async, SIGIO, POLL_IN);
 65
 66     return IRQ_RETVAL(IRQ_HANDLED);
 67 }
 68
 69 static int sixth_drv_open(struct inode *inode, struct file *file)
 70 {
 71 #if 0
 72     if (!atomic_dec_and_test(&canopen))
 73     {
 74         atomic_inc(&canopen);
 75         return -EBUSY;
 76     }
 77 #endif
 78
 79     down(&button_lock);
 80
 81     request_irq(IRQ_EINT0,   buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
 82     request_irq(IRQ_EINT2,   buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
 83     request_irq(IRQ_EINT11,  buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
 84     request_irq(IRQ_EINT19,  buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
 85     return 0;
 86 }
 87
 88 ssize_t sixth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos){
 89
 90     if(size != 1)
 91         return -EINVAL;
 92
 93     wait_event_interruptible(button_waitq, ev_press);
 94
 95     copy_to_user(buf, &key_val, 1);
 96
 97     ev_press = 0;
 98
 99     return 1;
100 }
101
102 int sixth_drv_close(struct inode *inode, struct file *file)
103 {
104     //atomic_inc(&canopen);
105     free_irq(IRQ_EINT0,  &pins_desc[0]);
106     free_irq(IRQ_EINT2,  &pins_desc[1]);
107     free_irq(IRQ_EINT11, &pins_desc[2]);
108     free_irq(IRQ_EINT19, &pins_desc[3]);
109     up(&button_lock);
110 }
111
112 static unsigned sixth_drv_poll (struct file *file, poll_table *wait)
113 {
114     unsigned int mask = 0;
115     poll_wait(file, &button_waitq, wait);
116
117     if (ev_press)
118         mask |= POLLIN | POLLRDNORM;
119
120     return mask;
121 }
122
123 static int sixth_drv_fasync(int fd, struct file *filp, int on)
124 {
125     printk("dirve : sixth_drv_fasync\n");
126     return fasync_helper(fd, filp, on, &button_async);
127 }
128
129 static struct file_operations sixth_drv_fops = {
130     .owner   =   THIS_MODULE,
131     .open    =   sixth_drv_open,
132     .read    =   sixth_drv_read,
133     .release =   sixth_drv_close,
134     .poll    =   sixth_drv_poll,
135     .fasync  =   sixth_drv_fasync,
136 };
137
138 int major;
139 static int __init sixth_drv_init(void){
140     major = register_chrdev(0, "sixth_drv", &sixth_drv_fops);
141
142     sixth_drv_class = class_create(THIS_MODULE, "sixth_drv");
143     sixth_drv_class_dev = class_device_create(sixth_drv_class, NULL, MKDEV(major, 0), NULL, "buttons");
144
145     gpfcon = (unsigned long *)ioremap(0x56000050, 16);
146     gpfdat = gpfcon + 1;
147
148     gpgcon = (unsigned long *)ioremap(0x56000060, 16);
149     gpgdat = gpgcon + 1;
150     return 0;
151 }
152
153 static void __exit sixth_drv_exit(void){
154     unregister_chrdev(major, "sixth_drv");
155
156     class_device_unregister(sixth_drv_class_dev);
157     class_destroy(sixth_drv_class);
158
159     iounmap(gpfcon);
160     iounmap(gpgcon);
161
162     return 0;
163 }
164
165 module_init(sixth_drv_init);
166 module_exit(sixth_drv_exit);
167
168 MODULE_LICENSE("GPL");

测试代码:
  1 #include
  2 #include
  3 #include
  4 #include
  5 #include
  6 #include
  7 #include
  8 #include
  9 #include
 10
 11 int fd;
 12
 13 void my_signal_fun(int signum)
 14 {
 15     unsigned char key_val;
 16     read(fd, &key_val, 1);
 17     printf("key_val: 0x%x\n", key_val);
 18 }
 19
 20 int main (int argc, char **argv)
 21 {
 22     unsigned char key_val;
 23     int ret;
 24     int Oflags;
 25
 26     signal(SIGIO, my_signal_fun);
 27
 28     fd = open("/dev/buttons",O_RDWR);
 29
 30     if (fd < 0)
 31     {
 32         printf("cannot open!\n");
 33         return -1;
 34     }
 35     fcntl(fd, F_SETOWN, getpid());
 36     Oflags = fcntl(fd, F_GETFL);
 37     fcntl(fd, F_SETFL, Oflags | FASYNC);
 38
 39     while (1)
 40     {
 41         sleep(1000);
 42     }
 43     return 0;
 44 }
************************************************************************************************
我们想达到这种目的:在同一时刻只能有一个应用程序打开我们的驱动程序。
最简单的办法是:
先定义一个变量:
static int canopen = 1;

然后在open函数中加入:
 static int sixth_drv_open(struct inode *inode, struct file *file)
 {
    if (--canopen != 0)
    {
        canopen++;
        return -EBUSY;
    }

      request_irq(IRQ_EINT0,   buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);
      request_irq(IRQ_EINT2,   buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);
      request_irq(IRQ_EINT11,  buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);
      request_irq(IRQ_EINT19,  buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);
      return 0;
 }

在close函数中
 int sixth_drv_close(struct inode *inode, struct file *file)
 {
     canopen++;
     free_irq(IRQ_EINT0,  &pins_desc[0]);
     free_irq(IRQ_EINT2,  &pins_desc[1]);
     free_irq(IRQ_EINT11, &pins_desc[2]);
     free_irq(IRQ_EINT19, &pins_desc[3]);
 }

我们开始定义了canopen = 1, 如果A设备先打开了这个驱动,在A设备的open函数中(--canopen)操作会使canopen为0,如果A设备正在使用,还没有释放,那么当B设备想要打开这个驱动的时候,--canopen = -1,open函数中if (--canopen != 0)这个语句就会执行,B设备暂时打不开驱动。同样,A设备在使用完驱动以后,需要在close函数中需要释放掉canopen。以供其他的设备继续使用这个驱动。

这个方法乍一看可行,但是(--canopen)这个操作需要3步来完成,在多任务的操作系统中,如下图所示,A和B两个设备都可以打开这个驱动:

那么如何不被打断呢?
第一种方法是采取原子操作,原子操作就是指在执行过程中不会被别的代码路径所中断的操作。原子操作在上面程序中为绿色注释掉的部分。
atomic_t v = ATOMIC_INIT(0);   //将原子变量v的值并初始化为整数值0 。
atomic_read(atomic_t *v);          //返回原子变量的值。
void atomic_inc(atomic_t *v);     //原子变量增加1。
void atomic_dec(atomic_t *v);    //原子变量减少1。
int atomic_dec_and_test(atomic_t *v);  //执行自减操作并测试结果,为0则返回true,否则返回false。

第二种方法是采取信号量的方法。
信号量是用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区的代码。当获取不到信号量的时候,进程进入休眠等状态。
struct semaphore sem;     //定义信号量
void sema_init (struct semaphore *sem, int val);  //初始化信号量,值为val。
void init_MUTEX(struct semaphore *sem);  //声明和初始化互斥体,初始化为1。
void init_MUTEX(struct semaphore *sem);  //声明和初始化互斥体,初始化为0。

static DECLARE_MUTEX(button_lock);  //定义互斥锁

void down(struct semaphore * sem);   //获得信号量,减小信号量的值,并在必要时一直等待。
int down_interruptible(struct semaphore * sem);  //完成上面相同的工作,但操作是可中断的。
int down_trylock(struct semaphore * sem);  //永远不会休眠,如果信号量在调用时不可获得,会立即返回一个非零值。

void up(struct semaphore * sem);  //释放信号量

信号量的方法原理是与上面两种方法相同的。
************************************************************************************************
试验第五个驱动程序,发现两个驱动程序是可以一起运行的:

试验第六个驱动:在第二次打开sixdrvtest测试程序的时候,系统提示cannot open!。


遇到的问题:
在第一次执行sixdrvtest测试程序的时候,并没有打开,而是直接提示cannot open!, 如图所示:

检查程序发现,在写程序的时候,在if 语句的后面不小心加了分号;造成的。

阅读(1354) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~