您的位置:首页 > 其它

字符设备实验之按键阻塞

2015-03-19 20:55 148 查看
目的:同一时刻,只能有1个app打开/dev/buttons

1.原子操作:指在执行过程中不会被别的代码路径所中断的操作

常用原子操作函数举例:

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,为0则返回true,否则返回false

2.信号量semaphore:用于保护临界区的一种常用方法,只有得到信号量的进程才能执行临界区代码。当获取不到信号量时,进程进入休眠等待状态

定义信号量

struct semaphore sem;

初始化信号liang

void sema_init(struct semaphore *sem, int val);

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);

3.阻塞:指在执行设备操作时若不能获得资源则挂起进程,直到满足可操作的条件后再进行操作

被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足

非阻塞操作:进程在不能进行设备操作时并不挂起,它或者被放弃,或者不停的查询,直至可以进行操作为止

fd = open("...", O_RDWR|O_NONBLOCK);

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/init.h>

#include <linux/fs.h>

#include <linux/interrupt.h>

#include <linux/irq.h>

#include <linux/sched.h>

#include <linux/pm.h>

#include <linux/slab.h>

#include <linux/sysctl.h>

#include <linux/proc_fs.h>

#include <linux/delay.h>

#include <linux/platform_device.h>

#include <linux/input.h>

#include <linux/gpio_keys.h>

#include <linux/workqueue.h>

#include <linux/gpio.h>

#include <linux/cdev.h>

#include <linux/device.h>

#include <linux/poll.h>

#include <asm/io.h>

#include <asm/irq.h>

#include <asm/uaccess.h>

#include <mach/map.h>

#include <mach/regs-clock.h>

#include <mach/regs-mem.h>

#include <mach/gpio.h>

#include <mach/gpio-smdkc110.h>

#include <mach/regs-gpio.h>

/********************************************************************************************

ioremap(),iounmap()

class_create(),class_destroy()

class_device_create(),class_device_unregister()

********************************************************************************************/

static dev_t devno;

static struct cdev *pCdev;

static struct class *sixthdrv_class;

static struct device *sixthdrv_class_dev;

#define DEVICE_NAME "sixth_drv"

int major,minor;

volatile unsigned long *gpgcon;

volatile unsigned long *gpgdat;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

/* 中断事件标志, 中断服务程序将它置1,sixth_drv_read将它清0 */

static volatile int ev_press = 0;

struct pin_desc{

unsigned int pin;

unsigned int key_val;

};

/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */

/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */

static unsigned char key_val;

struct pin_desc pins_desc[4] = {

{S5PV210_GPH0(0), 0x01},

{S5PV210_GPH0(1), 0x02},

{S5PV210_GPH0(2), 0x03},

{S5PV210_GPH0(3), 0x04},

};

/* 异步通知 :

为了使设备支持异步通知机制,驱动程序中涉及以下3项工作

1.支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID,不过此项工作已由内核完成,设备驱动无须处理

2.支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序中的fasync()函数得以执行

驱动中应该实现fasync()函数

3.在设备资源可以获得时,调用kill_fasync()函数激发相应的信号

*/

static struct fasync_struct *button_async;

/*互斥阻塞*/

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

/*

* 确定按键值

*/

static irqreturn_t sixth_drv_irq(int irq, void *dev_id)

{

struct pin_desc * pindesc = (struct pin_desc *)dev_id;

unsigned int pinval;

pinval = gpio_get_value(pindesc->pin);

if (pinval)

{

/* 松开 */

key_val = 0x80 | pindesc->key_val;

}

else

{

/* 按下 */

key_val = pindesc->key_val;

}

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

wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程, 在poll中用于唤醒等待的进程 */

kill_fasync (&button_async, SIGIO, POLL_IN);//kill_fasync通知应用程序

return IRQ_RETVAL(IRQ_HANDLED);

}

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

{

if (file->f_flags & O_NONBLOCK)

{

if (down_trylock(&button_lock)) //试图获取信号量,如果信号量已被其他进程获取,则立刻返回非零值

{

printk("down_trylock fail \n");

return -EBUSY;

}

}

else

{

/* 获取信号量 */

down(&button_lock);

}

//printk("do sixth_drv_open \n");

/*

* K1,K2,K3,K4对应GPH0,GPH1,GPH2,GPH3

*/

/* 配置K1,K2,K3,K4为输入引脚 */

request_irq(IRQ_EINT(0), sixth_drv_irq, IRQ_TYPE_EDGE_BOTH, "S2", &pins_desc[0]);

request_irq(IRQ_EINT(1), sixth_drv_irq, IRQ_TYPE_EDGE_BOTH, "S3", &pins_desc[1]);

request_irq(IRQ_EINT(2), sixth_drv_irq, IRQ_TYPE_EDGE_BOTH, "S4", &pins_desc[2]);

request_irq(IRQ_EINT(3), sixth_drv_irq, IRQ_TYPE_EDGE_BOTH, "S5", &pins_desc[3]);

return 0;

}

static int sixth_drv_release(struct inode *inode, struct file *file)

{

//printk("do sixth_drv_release \n");

free_irq(IRQ_EINT(0), &pins_desc[0]);

free_irq(IRQ_EINT(1), &pins_desc[1]);

free_irq(IRQ_EINT(2), &pins_desc[2]);

free_irq(IRQ_EINT(3), &pins_desc[3]);

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

return 0;

}

static int sixth_drv_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)

{

if (count != 1)

return -EINVAL;

if (filp->f_flags & O_NONBLOCK)

{

if (!ev_press) // 非阻塞方式判断是否有按键按下,没有则直接退出

return -EAGAIN;

}

else

{

/* 如果没有按键动作, 休眠 */

wait_event_interruptible(button_waitq, ev_press);

}

/* 如果有按键动作, 返回键值 */

copy_to_user(buff, &key_val, 1);

ev_press = 0;

return 1;

}

static ssize_t sixth_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)

{

//printk("do sixth_drv_write \n");

return 0;

}

static unsigned sixth_drv_poll(struct file *file, poll_table *wait)

{

unsigned int mask = 0;

poll_wait(file, &button_waitq, wait); // 此处将当前进程加入到等待队列中,但并不阻塞

if (ev_press)

mask |= POLLIN | POLLRDNORM; //表示设备可读

return mask;

}

static int sixth_drv_fasync (int fd, struct file *filp, int on)

{

printk("driver: sixth_drv_fasync, on=%d\n",on);

return fasync_helper (fd, filp, on, &button_async);//用于给字符设备建立异步通知队列

}

static struct file_operations sixth_drv_fops =

{

.owner = THIS_MODULE, /* 这是一个宏,指向编译模块时自动创建的__this_module 变量*/

.open = sixth_drv_open,

.release = sixth_drv_release,

.read = sixth_drv_read,

.write = sixth_drv_write,

.poll = sixth_drv_poll,

.fasync
= sixth_drv_fasync,

};

static int __init sixth_drv_init(void)

{

//major = register_chrdev(0, DEVICE_NAME, &sixth_drv_fops);

int ret;

//使用udev自动生成设备文件,并分配设备号

pCdev = cdev_alloc();

if (!pCdev) {

printk(KERN_WARNING "cdev_alloc failed\n");

//goto out;

}

pCdev->owner = THIS_MODULE;

cdev_init(pCdev, &sixth_drv_fops);

ret = alloc_chrdev_region(&devno, 5, 1, "buttons");

if(ret){

printk(KERN_ERR "alloc char device region faild!\n");

//return ret;

}

major = MAJOR(devno);

minor = MINOR(devno);

ret = cdev_add(pCdev, devno, 1); //cat /proc/devices可以查看到多了一个buttons设备

if(ret){

printk(KERN_ERR "add char device faild!\n");

//goto add_error;

}

#if 1

sixthdrv_class = class_create(THIS_MODULE, "buttonsClass");//ls /sys/class/buttonsClass/可以查看到多了一个buttonsClass类

if(IS_ERR(sixthdrv_class)){

printk(KERN_ERR "create class error!\n");

}

//ls /sys/class/buttonsClass/buttons5/可以查看到多了一个buttons5设备

sixthdrv_class_dev = device_create(sixthdrv_class, NULL, devno/*MKDEV(major, 0)*/, NULL, "buttons%d", MINOR(devno)); /* /dev/buttons */

if(IS_ERR(sixthdrv_class_dev)){

printk(KERN_ERR "create buttons device error!\n");

}

#endif

printk("sixth_drv_init \n");

printk("major = %d,minor = %d \n", major,minor);

gpgcon = (volatile unsigned long *)ioremap(0xE0200C00, 16);

gpgdat = gpgcon + 1;

return 0;

}

static void __exit sixth_drv_exit(void)

{

//unregister_chrdev(major, DEVICE_NAME);

#if 1

device_destroy(sixthdrv_class,devno);

class_destroy(sixthdrv_class);

#endif

cdev_del(pCdev);

unregister_chrdev_region(devno, 1);

iounmap(gpgcon);

printk("sixth_drv_exit \n");

}

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 <fcntl.h>

#include <signal.h>

int main(int argc, char **argv)

{

int fd;

int cnt = 0;

unsigned char key_val;

int ret;

fd = open("/dev/buttons5", O_RDWR | O_NONBLOCK);

if (fd < 0)

{

printf("can't open!\n");

return 0;

}

while (1)

{

ret = read(fd, &key_val, 1);

printf("key_val: 0x%x, ret = %d\n", key_val, ret);

sleep(5);

}

return 0;

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