做了做这个信号量的实验,不过用到了异步通知,也就是通过内核来通知应用程序(具体就是按键按下后,内核通知应用程序可以进行读操作)!
在《Linux Device Driver》这本书里我查过了,在下章节会介绍到,但是也懒得改代码了,所以就这样贴出来吧,马上也就要写一下异步通知这个很实用的动动。。
这里我用的是互斥锁,还可以设置 O_NONBLOCK 位。
1、驱动程序
①、tiny6410_button_sem.c
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/cdev.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-n.h>
#include <mach/gpio-bank-l.h>
#include <mach/map.h>
MODULE_LICENSE("GPL");
static int major;
static dev_t devno;
static struct cdev cdev;
static struct class *tiny6410_class;
static struct device *tiny6410_device;
static struct fasync_struct *tiny6410_button_fasync;
static volatile char key_value;
static DEFINE_SEMAPHORE(button_semlock);
typedef struct {
int irq; /* 中断号 */
int num; /* 对应的按键编码 */
char *name; /* 对应的按键名称 */
} button_irq;
button_irq button_irqs[] = {
{IRQ_EINT(0), 0, "KEY1"},
{IRQ_EINT(1), 1, "KEY2"},
{IRQ_EINT(2), 2, "KEY3"},
{IRQ_EINT(3), 3, "KEY4"},
{IRQ_EINT(4), 4, "KEY5"},
{IRQ_EINT(5), 5, "KEY6"},
{IRQ_EINT(19), 6, "KEY7"},
{IRQ_EINT(20), 7, "KEY8"},
};
static irqreturn_t button_interrupt(int irq,void *dev_id)
{
button_irq *button_irqs_tmp = (struct button_irq *)dev_id;
int down;
int number;
unsigned tmp;
number = button_irqs_tmp->num;
switch(number) {
case 0: case 1: case 2: case 3: case 4: case 5:
tmp = readl(S3C64XX_GPNDAT);
down = !(tmp & (1<<number));
break;
case 6: case 7:
tmp = readl(S3C64XX_GPLDAT);
down = !(tmp & (1 << (number + 5)));
break;
default:
down = 0;
}
if (down == !(key_value & (1<<number))) {
key_value = down ? (key_value | (1<<number)) : (key_value & ~(1<<number));
kill_fasync (&tiny6410_button_fasync, SIGIO, POLL_IN);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static void button_request_irq(void)
{
int i;
for(i=0; i<8; i++) {
request_irq(button_irqs[i].irq, button_interrupt,IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)(&button_irqs[i]));
}
}
static void button_free_irq(void)
{
int i;
for(i=0; i<8; i++) {
free_irq(button_irqs[i].irq, (void *)(&button_irqs[i]));
}
}
static int button_open(struct inode *inode, struct file *filp)
{
if(filp->f_flags & O_NONBLOCK) {
if(down_trylock(&button_semlock)) {
return -EBUSY;
}
}
else {
down(&button_semlock);
}
button_request_irq();
return 0;
}
static int button_release(struct inode *inode, struct file *filp)
{
up(&button_semlock);
button_free_irq();
return 0;
}
static ssize_t button_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
unsigned long err;
err = copy_to_user(buf, &key_value, sizeof(key_value));
return err ? -EFAULT : min(sizeof(key_value), size);
}
static int button_fasync(int fd, struct file *filp, int on)
{
printk("Call button_fasync!\n");
return fasync_helper(fd, filp, on, &tiny6410_button_fasync);
}
static const struct file_operations button_fops =
{
.owner = THIS_MODULE,
.open = button_open,
.release = button_release,
.read = button_read,
.fasync = button_fasync,
};
static int __init button_sem_init(void)
{
alloc_chrdev_region(&devno, 0, 1, "tiny6410_button_sem");
major = MAJOR(devno);
/* 初始化cdev */
cdev_init(&cdev, &button_fops);
cdev.owner = THIS_MODULE;
/* 注册cdev */
cdev_add(&cdev, MKDEV(major, 0), 1);
tiny6410_class = class_create(THIS_MODULE, "tiny6410_sem_class");
tiny6410_device = device_create(tiny6410_class, NULL, MKDEV(major, 0), NULL, "tiny6410_button_sem");
return 0;
}
static void __exit button_sem_exit(void)
{
device_destroy(tiny6410_class, devno);
class_destroy(tiny6410_class);
/* 注销cdev */
cdev_del(&cdev);
unregister_chrdev_region(MKDEV(major, 0), 1);
;
}
module_init(button_sem_init);
moudle_exit(button_sem_exit);
2、测试程序
①、tiny6410_button_sem_app.c
#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>
/*
tiny6410_button_sem_app.c
*/
int fd;
void button_fasync_signal(int signum)
{
int i;
int key_value, last_key_value;
read(fd, &key_value, sizeof(key_value));
for (i=0; i<8; i++) {
if ((key_value & (1<<i)) != (last_key_value & (1<<i))) {
printf("KEY%d: %s (key_value = 0x%x)\n",i+1, (key_value & (1<<i)) ? "DOWN" : "UP", key_value);
}
}
last_key_value = key_value;
}
int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int oflags;
signal(SIGIO, button_fasync_signal);
fd = open("/dev/tiny6410_button_sem", O_RDWR | O_NONBLOCK);
if (fd < 0) {
printf("can't open!\n");
}
fcntl(fd, F_SETOWN, getpid()); /* Save process ID in filp -> f_owner,告诉内核我要发给谁 */
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, (oflags | FASYNC)); /* 改变fasync 标志,最终会调用到驱动的fasync > fasync_helper;用来初始化/ 释放fasync_struct */
while (1) {
sleep(1000);
}
return 0;
}
3、测试结果
当时由于没有记录。。。。所以就这样吧。。呵呵。。