您的位置:首页 > 其它

tiny6410按键驱动(五)---同步互斥阻塞

2014-12-24 20:55 363 查看
驱动代码如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>

#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>

#include<linux/timer.h>

#include <linux/semaphore.h>

static unsigned button_major;
static dev_t button_id;  /*设备号*/

static struct cdev button_cdev;
static struct class *button_class;
static struct device *button_device;

static volatile char ev_press;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  /*生成一个等待队列头wait_queue_head_t,名字为button_waitq*/

static DEFINE_SEMAPHORE(button_lock) ; /*这个宏定义一个互斥体,并将其初始化为1*/

struct buttons_irq_decs{
int irq;
char *name;

};
static struct buttons_irq_decs button_irqs[] = {
{IRQ_EINT( 0),"K1"},
{IRQ_EINT( 1),"K2"},
{IRQ_EINT( 2),"K3"},
{IRQ_EINT( 3),"K4"},

};

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct buttons_irq_decs *irq_desc = (struct buttons_irq_decs*)dev_id ;
printk("%s\n" ,irq_desc->name);
ev_press = 1;
wake_up_interruptible(&button_waitq);/*唤醒睡眠的进程*/
return IRQ_HANDLED;
}

static int myButton_open(struct inode * inode, struct file * file)
{
/*获取信号量,如果成功,程序继续执行,否则程序进入睡眠,直到信号量被占有它的那个进程释放。释放信号量的地方在myButton_release里*/

/*获取信号量的三种方式*/
/*方式一 : */
//down(&button_lock);
/*这种获取信号量的方式是不可中断的,当获取不到信号量时,进程会进入不可中断的休眠,即使执行kill也无法杀死这个休眠的进程*/
/*方式二 :*/
if(down_interruptible(&button_lock))
return -ERESTARTSYS;
/*这种获取信号量的方式是可中断的,获取到信号是返回值是0,无法获取信号量时返回非零值,这种获取信号量的方式一定要判断返回值*/

/*方式三 :*/
/*if(down_trylock(&button_lock))
{
return -ERESTARTSYS;
}                             */
/*这种方式永远不会进入休眠,如果信号量不能获得时,立即返回一个非0值,但我在用这种方式时,当进程无法获得信号量时,进程一直在
这里休眠,即使后来信号量被其他的进程释放,依旧还是在此处休眠,除非杀死进程,重新运行进程,才可以获得信号量*/

int i;
char err;
for(i=0 ; i<sizeof(button_irqs)/sizeof(button_irqs[0]) ;i++)
{

err = request_irq(button_irqs[i].irq , &buttons_irq ,IRQ_TYPE_EDGE_BOTH ,button_irqs[i].name ,&button_irqs[i] );
/*IRQ_TYPE_EDGE_BOTH表示双边沿触发,即按键按下和松开都会触发中断*/

if(err)
{
printk("button_irqs[i] request faild\n");
continue;
}
}
printk("myButton_open end\n");

return 0;
}

static int myButton_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_pos)
{
return 0;

}

static int myButton_read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off)
{
if(bytes != 1)
{
printk("you can read just one char from the decive\n");
return -1;
}
//printk("myButton_read start\n");

wait_event_interruptible(button_waitq ,ev_press);

char value;

value = readl(S3C64XX_GPNDAT)&(0xf);   /*GPN0~3对应于按键K1~K4*/

if(copy_to_user(userbuf, &value, bytes))    /*成功时返回0,失败时返回没有成功拷贝的字符个数*/
{
printk("copy_to_user error\n");
return -1;
}
ev_press =0 ;
//printk("myButton_read end\n");
return 0;
}

static int myButton_release(struct inode *inode, struct file *file)
{
int i;
for(i=0 ; i<sizeof(button_irqs)/sizeof(button_irqs[0]) ;i++)
{
free_irq(button_irqs[i].irq, &button_irqs[i]);
}

printk("myButton_release\n");

up(&button_lock); /*释放信号量*/
return 0;

}

/*(应用程序)poll->sys_poll->do_sys_poll->do_poll->do_pollfd->f_op->poll(驱动)*/
static unsigned myButton_poll(struct file *file, struct poll_table_struct *wait)
{
unsigned mask = 0;
poll_wait(file, &button_waitq, wait); /*将当前的设备加入到等待队列中,不会立即休眠,poll_wait的作用,
poll_wait的作用只是为了让驱动程序能找到要唤醒的进程*/
if (ev_press)
mask |= POLLIN | POLLRDNORM;     /*POLLIN :设备可以无阻塞地读取;POLLRDNORM:数据已就绪,可以读取*/
return mask;

}

struct file_operations button_fiops=
{
.owner    = THIS_MODULE,
.open     = myButton_open,
.write    = myButton_write,
.read     = myButton_read,
.release  = myButton_release,
.poll     = myButton_poll,

};

static int myButton_init(void)
{

if(button_major)
{
button_id = MKDEV(button_major,0);
register_chrdev_region(button_id, 2, "myButton_drv");
}
else
{
alloc_chrdev_region(&button_id, 0, 2,"myButton_drv" );
button_major = MAJOR(button_id);
}
cdev_init(&button_cdev, &button_fiops);
cdev_add(&button_cdev, button_id, 2);       /*添加2个设备*/

button_class = class_create(THIS_MODULE, "myButton");
button_device = device_create(button_class, NULL, MKDEV(button_major , 0), NULL, "myButton0");  /*创建设备节点/dev/myButton0*/
button_device = device_create(button_class, NULL, MKDEV(button_major , 1), NULL, "myButton1");  /*创建设备节点/dev/myButton1*/

printk("myButton init succeed\n");
return 0;
}
static void myButton_exit(void)
{
device_destroy(button_class, MKDEV(button_major , 0));
device_destroy(button_class, MKDEV(button_major , 1));
class_destroy(button_class);
cdev_del(&button_cdev);
unregister_chrdev_region(button_id, 2);

printk("myButton exit succeed\n");

}

module_init(myButton_init);
module_exit(myButton_exit);
MODULE_LICENSE("GPL");


Makefile文件如下:

obj-m := mybutton_drv5.o
KDIR :=/home/lijunliang/windows-files/linux-tiny6410
all :
make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean :
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order


测试程序如下:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>

int main(void)
{
int fd;

int ret;

char keyValue ;

struct pollfd fds[1];

fd = open("/dev/myButton0" , O_RDWR);
if(fd < 0)
{printf("open file error\n");}

fds[0].fd = fd ;
fds[0].events = POLLIN ;

while(1)
{
ret =  poll(fds , 1 ,5000 );
/*函数原型 :int poll(struct pollfd    *fds ,nfds_t    nfds ,int    timeout);
Poll机制会判断fds中的文件是否可读,如果可读则会立即返回,返回的值就是可读fd的数量,
如果不可读,那么就进程就会休眠timeout这么长的时间,然后再来判断是否有文件可读,如果有,返回fd的数量,如果没有,则返回0.  */
if(0 == ret)
{
printf("timeOut ...\n");

}
else
{

read(fd ,&keyValue ,1);

switch(keyValue)
{
case 0xe: printf("K1 按下\n");
break;

case 0xd: printf("K2 按下\n");
break;

case 0xb: printf("K3 按下\n");
break;

case 0x7: printf("K4 按下\n");
break;

default : break;

}

}

}
//printf("%x\n",userbuffer0[0]);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: