只能有一个应用程序打开/dev/buttons驱动程序(使用阻塞与非阻塞)

来源:互联网 发布:非负矩阵分解图片 编辑:程序博客网 时间:2024/06/06 04:09
/*功能:同一时刻,只能有一个应用程序打开/dev/buttons驱动程序。
使用阻塞与非阻塞
*2016年5月9日20:44:54
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>




static struct class *sixthdrv_class;
static struct class_device *sixthdrv_class_devs[4];


volatile unsigned long * gpfcon;
volatile unsigned long * gpfdat;


volatile unsigned long * gpgcon;
volatile unsigned long * gpgdat;




/*
*
确定按键值 s3c2410_gpio_getpin
*/


struct pin_desc{
unsigned int pin;
unsigned int key_val;
};


/*键值:按下时:0x01,0x02,0x03x0x04*/
/*键值:松开时:0x81,0x82,0x83,0x84*/




struct pin_desc pins_desc[4] = {
{S3C2410_GPF0,0x01},
{S3C2410_GPF2,0x01},
{S3C2410_GPG3,0x01},
{S3C2410_GPG11,0x01},


};


static unsigned char key_val;


//static int canopen = 1;  对应open函数中if方法
//atomic_t canopen = ATOMIC_INIT(1);     //定义原子变量v并初始化为0


static DECLARE_MUTEX(button_lock);     // 定义一个名称为button_lock的信号量,并将信号量初始化为1 。定义互斥锁 






static struct fasync_struct *button_async;


static DECLARE_WAIT_QUEUE_HEAD(button_waitq);


/* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */
static volatile int ev_press = 0;




static irqreturn_t buttons_irq(int irq, void *dev_id)
{
/*利用设备号来传递引脚号和值*/
struct pin_desc * pindesc = (struct pin_desc *) dev_id;
unsigned int pinval ;
pinval = s3c2410_gpio_getpin(pindesc->pin);//系统提供的读取引脚状态的函数


if(pinval)
{
/*按键松开*/
key_val = 0x80 | pindesc->key_val;

}
else
{
/*按键按下*/

key_val = pindesc->key_val;


}


ev_press = 1;                  /* 表示中断发生了 */


/*由kill_fasync函数发出SIGIO信号,发给谁,是在button_async这个结构中定义的
button_async这个结构要在sixth_drv_fasync函数中进行初始化
在内核里面一般向应用程序发出的是SIGIO信号,表示有数据可供读写*/
*/
kill_fasync(&button_async, SIGIO, POLL_IN);

    wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
return IRQ_HANDLED;
}


static int sixth_drv_open(struct inode * inode, struct file * file)
{


#if  0
/*第一次执行open函数时,--canopen!=0不成立,所以执行if后面的函数
从第二次以后 --canopen!=0 都成立,所以会返回错误,即表示不能第二次打开
该方法有BUG,因为linux是多任务操作系统,在 执行--canopen != 0期间的取值时,
cpu可能会被切换出去,去执行另一个程序打开open函数后,再返回继续执行--canopen != 0
接下来的程序,结果是两个应用程序都成功打开了设备*/.

if(--canopen != 0)
{
canopen ++;
return -EBUSY;


}
#endif


/*获取信号量,如果是第一次执行open函数的话,就可以获得信号量,当第二个程序在执行open函数时,就无法获得信号量,
就会进入不可中断的休眠状态,只有第一个应用程序把button_lock这个信号量释放的时候,第二个应用程序才会被唤醒
继续执行。
down系列函数会使信号量的值减1
up会使信号量的值加1
调用down的进程不允许被中断,*/


/*对open函数传入的O_NONBLOCK标志位的处理,在file这个结构体中,这个结构是内核给我们提供的*/
if(file->f_flags & O_NONBLOCK)
{
/*如果打不开这个设备就会立刻返回错误*/
if(down_trylock(&button_lock))
return -EBUSY;


}
else
{

down(&button_lock);
}


#if 0
/*
int atomic_dec_and_test(atomic_t *v); //自减操作后测试其是否为0,为0则返回true,否则返回false。
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
*/


if(!atomic_dec_and_test(&canopen))   /*被执行过一次之后,canopen变为0*/
{
atomic_inc(&canopen);
return -EBUSY;

}


#endif


/*配置GPF0,2  位输入引脚 */
/*配置GPG3,11为输入引脚 */
/*
IRQ_EINT0是在irqs.h中定义的
IRQT_BOTHEDGE是在s3c_irqext_type函数中找到,在irq.h中定义
*/


//向内核注册中断处理函数
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;
}










/*函数的参数是用户空间的函数的参数*/
static ssize_t sixth_drv_read(struct file *file, const __user char *buffer,size_t count, loff_t *ppos)
{
if(count != 1)
return -EINVAL;//返回错误值




if (file->f_flags & O_NONBLOCK)
{
/*判断有没有按键按下,没有按键按下的话*/
if (!ev_press)
return -EAGAIN;/*如果你连续做read操作而没有数据可读。此时程序不会阻塞起来等待数据准备就绪返回,
read函数会返回一个错误EAGAIN,提示你的应用程序现在没有数据可读请稍后再试,再次执行*/
}
else
{

/*如果没有按键按下:进入休眠(让出CPU,不返回)*/
wait_event_interruptible(button_waitq,ev_press);

}





/*如果休眠的程序被唤醒,则会从wait_event_interruptible继续往下执行*/




/*如果有按键动作发生,则直接返回键值*/
copy_to_user(buffer ,&key_val,1);
ev_press = 0;
return 1;





}




/*入口函数*/
int major;
static int sixth_drv_init (void)
{
major = register_chrdev(0,"secod_drv",&sixth_drv_fops);


sixthdrv_class = class_create(THIS_MODULE, "secod_drv");//创建个类

sixthdrv_class_devs = class_device_create(sixthdrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */



gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
gpfdat = gpfcon + 1;//指向0x56000054


gpgcon = (volatile unsigned long *)ioremap(0x56000060,16);
gpgdat = gpgcon + 1;//指向0x56000064


return 0;


}






/*出口函数*/
int major;
static int sixth_drv_exit (void)
{
unregister_chrdev(major,"secod_drv");

class_device_unregister(sixthdrv_class_devs);
 
class_destroy(sixthdrv_class);



iounmap(gpfcon);
iounmap(gpgcon);
return 0;


}


int sixth_drv_close (struct inode * inode, struct file * file)
{
/* canopen ++;是配合只能一次打开open函数?当open函数真正被打开后,
canopen的值为0,此时不能在被任何应用程序打开,当打开的open函数被关闭时canopen
的值变为1,以便下次再被应用程序打开*/


//canopen ++;  对应于open函数中的if语句


//atomic_inc(&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]);
up(&button_lock);/*第一个程序运行完后,释放掉信号量*/
return 0;


}




/*如果sixth_drv_poll函数返回的是0,则当前进程会进入休眠状态,直到超时被唤醒,返回到用户态,
用户态的poll函数返回0,或者是sixth_drv_poll函数返回非0,返回到用户态*/
static unsigned int sixth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = 0;
/*poll_wait函数是将当前进程挂到button_waitq队列中,不会使当前进程立即进入休眠
使当前进程进入休眠是在其他的内核中的函数中实现的*/
poll_wait(file, &button_waitq, wait); // 不会立即休眠


if (ev_press)
mask |= POLLIN | POLLRDNORM;


return mask;


}
f_owner
static int sixth_drv_fasync (int fd, struct file *filp, int on)
{
printk("driver: sixth_drv_fasync\n");
/*
fasync_helper函数初始化button_async这个结构体,初始化后,
kill_fasync(&button_async, SIGIO, POLL_IN);这个函数就能给应用程序发送信号了


*/
return fasync_helper (fd, filp, on, &button_async);
}




static struct file_operations sixth_drv_fops = {
    .owner   =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open    =   sixth_drv_open,    //表示将first_drv_open函数的地址赋给file_operations结构体中open函数,
.read =sixth_drv_read, //以后执行 first_drv_open函数时会自动调用file_operations结构体中的open函数
.release =  sixth_drv_close,//关闭设备的时候,释放open函数中注册中断处理函数的irq
.poll    =  sixth_drv_poll,
.fasync  =  sixth_drv_fasync,
};


/*修饰*/
module_init(sixth_drv_init);
module_exit(sixth_drv_exit);


MODULE_LICENSE("GPL");




//测试函数


/*功能:*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>




/*
阻塞操作
是指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作
被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足
非阻塞操作
进程在不能进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作.
*/




/* sixthdrvtest 
  */
int fd;


void my_signal_fun(int signum)
{
unsigned char key_val;
read(fd, &key_val, 1);
printf("key_val: 0x%x\n", key_val);
}


int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags;


/*使用signal系统调用给SIGIO这个信号挂接处理函数
在内核里面一般向应用程序发出的是SIGIO信号*/
signal(SIGIO, my_signal_fun);


/*open 会传入O_NONBLOCK的参数 */
fd = open("/dev/buttons", O_RDWR | O_NONBLOCK);
if (fd < 0)
{
printf("can't open!\n");
return -1;
}

#if 0


/*buttons这个设备就是驱动程序*/
/*F_GETFL :读取文件状态标志。*/
/*可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志*/
/*F_SETFL 将文件状态标志设置为第三个参数的值 (取为整型值)。*/
/* fcntl的F_SETOWN指令设置当前进程为设备文件owner*/




/* fcntl(fd, F_SETOWN, getpid());告诉驱动程序当前PID进程号
*/
fcntl(fd, F_SETOWN, getpid());


/*读取fd的文件状态标志位*/
Oflags = fcntl(fd, F_GETFL); 


/* 
设置fd的文件状态标志位为Oflags | FASYNC  即异步通知
调用fcntl(fd, F_SETFL, Oflags | FASYNC);函数时,会进入驱动程序的file_operations结构体执行fasync函数


*/


/*
每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
驱动中应该实现fasync()函数。


*/
fcntl(fd, F_SETFL, Oflags | FASYNC);


#endif


while (1)
{
/*判断返回值,非阻塞的话一读就会返回一个值,*/
ret = read(fd, &key_val, 1);
printf("key_val: 0x%x, ret = %d\n", key_val, ret);
sleep(5);//单位为秒
}

return 0;
}




/*
fcnt1函数返回值说明
返回说明:   
成功执行时,对于不同的操作,有不同的返回值 
F_DUPFD: 新文件描述词 
F_GETFD:  标志值 
F_GETFL:  标志值 
F_GETOWN: 文件描述词属主 
F_GETSIG: 读写变得可行时将要发送的通告信号,或者0对于传统的SIGIO行为 


*/


/*
getpid()用来取得目前进程的进程识别码,许多程序利用取到的
此值来建立临时文件,以避免临时文件相同带来的问题。*/





0 0
原创粉丝点击