使用信号量
*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的进程不允许被中断,*/
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;//返回错误值
/*如果没有按键按下:进入休眠(让出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,
};
/*修饰*/
modules_init(sixth_drv_init);
modules_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);
fd = open("/dev/buttons", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
return -1;
}
/*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);
while (1)
{
sleep(1000);
}
return 0;
}
/*
fcnt1函数返回值说明
返回说明:
成功执行时,对于不同的操作,有不同的返回值
F_DUPFD: 新文件描述词
F_GETFD: 标志值
F_GETFL: 标志值
F_GETOWN: 文件描述词属主
F_GETSIG: 读写变得可行时将要发送的通告信号,或者0对于传统的SIGIO行为
*/
/*
getpid()用来取得目前进程的进程识别码,许多程序利用取到的
此值来建立临时文件,以避免临时文件相同带来的问题。*/