您的位置:首页 > 其它

内核模块向用户进程发送消息

2009-09-02 23:05 357 查看
kpp.c
---------------------------------------------
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/ioport.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/jiffies.h>

#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/arch-magus/hardware.h>
#include <asm/arch-magus/irqs.h>
#include <asm/gpio.h>
#include <asm/param.h>

#define KPPMAJOR 240
#define MODULE_NAME "kpp"
#define HOLD_IRQ MAGUS_GPIO_HOLD
#define KPP_GET _IOR('f', 2, int)

//<<<<<Global Variable
struct kpp_dev {
struct cdev cdev;
struct fasync_struct *async_queue;
};
struct kpp_dev *kpp_devp;

//work queue struct
struct work_struct hold_wq;
void hold_do_work(void);

static int gvalue;
static struct class *hold_class;
static int number;

void hold_do_work(void)
{
int state;
struct kpp_dev *dev = kpp_devp;
state = gpio_get_value(HOLD_IRQ);
if (state == 1) {
gvalue = 10;
number++;
if (number == 1) {
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
schedule_work(&hold_wq);
} else {
gvalue = -10;
number = 0;
enable_irq(gpio_to_irq(HOLD_IRQ));
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
}
msleep(100);
}

static irqreturn_t hold_isr(int irq, void *dev_id)
{
int state;
mdelay(5);
state = gpio_get_value(HOLD_IRQ);
if (state == 1) {
disable_irq(irq);
schedule_work(&hold_wq);
}
return IRQ_HANDLED;
}

static int kpp_fasync(int fd, struct file *filp, int mode)
{
struct kpp_dev *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}

static int kpp_release(struct inode *inode, struct file *filp)
{
kpp_fasync(-1, filp, 0);
return 0;
}

static int kpp_open(struct inode *inode, struct file *filp)
{
filp->private_data = kpp_devp;
return 0;
}

static int kpp_ioctl(struct inode *inode, struct file *filp, unsigned
int cmd, unsigned long arg)
{
int value = gvalue;
switch (cmd) {
case KPP_GET:
put_user(value, (int __user *) arg);
break;
default:
return -ENOTTY;
}
return 0;
}

static struct file_operations g_kpp_fops = {
.owner = THIS_MODULE,
.ioctl = kpp_ioctl,
.fasync = kpp_fasync,
.open = kpp_open,
.release = kpp_release,
};

static void kpp_setup_cdev(struct kpp_dev *dev, int index)
{
int devno = MKDEV(KPPMAJOR, index);
cdev_init(&dev->cdev, &g_kpp_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &g_kpp_fops;
cdev_add(&dev->cdev, devno, 1);
}

static dev_t devno;

static void register_kpp(void)
{
devno = MKDEV(KPPMAJOR, 0);
register_chrdev_region(devno, 1, MODULE_NAME);
kpp_devp = kmalloc(sizeof (struct kpp_dev), GFP_KERNEL);
memset(kpp_devp, 0, sizeof (struct kpp_dev));
kpp_setup_cdev(kpp_devp, 0);

hold_class = class_create(THIS_MODULE, "hold_class");
if (IS_ERR(hold_class)) {
printk("<1> create class error\n");
return -1;
}
class_device_create(hold_class, NULL, devno, NULL, "hold");
}

static int __init kpp_init(void)
{
int hold_irq;
register_kpp();

/*register hold-key irq interrupt */
hold_irq = gpio_to_irq(HOLD_IRQ);
set_irq_type(hold_irq, IRQ_TYPE_EDGE_RISING);
if (request_irq(hold_irq, hold_isr, IRQF_SAMPLE_RANDOM, "hold-key", NULL)) {
printk(KERN_ERR "Request HOLD IRQ error !\n");
return -EINVAL;
}

/*init work queue for hold irq */
INIT_WORK(&hold_wq, (void (*)(void *)) hold_do_work);

printk("Solomon Systech Limited\n");
return 0;
}

static void __exit kpp_cleanup(void)
{
int hold_irq = gpio_to_irq(HOLD_IRQ);
free_irq(hold_irq, NULL);

cdev_del(&kpp_devp->cdev);
unregister_chrdev_region(devno, 1);
class_device_destroy(hold_class, devno);
class_destroy(hold_class);
}

module_init(kpp_init);
module_exit(kpp_cleanup);
MODULE_DESCRIPTION("Magus Keypad Driver");
MODULE_LICENSE("GPL");

kpptest.c
---------------------------------------------
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <linux/ioctl.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

#define KPP_GET _IOR('f', 2, int)
static int fd;

void gpiovol_handler(int sigum)
{
int value;
ioctl(fd, KPP_GET, &value);
printf("the value = %d.\n", value);
}

int main()
{
int oflags;
fd = open("/dev/hold", O_RDWR);
if (fd < 0) {
return -1;
}
// 当按下hold键后,hold driver将向该进程发送SIGIO信号
signal(SIGIO, gpiovol_handler);

// F_SETOWN设置接收SIGIO和SIGURG信号的进程ID或进程组ID.正的
// arg指定一个进程ID,负的arg表示等于arg绝对值的一个进程组ID
fcntl(fd, F_SETOWN, getpid());
oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, oflags | FASYNC); // (1)
while (1) ;
return 0;
}

(1) 当一个文件的FASYNC标志变化时(调用fcntl()函数,设置FASYNC文件标志时),该文件所对应的设备驱动的fasync()接口将被调用。

驱动程序向用户程序发信号
---------------------------------------------
当设备有IO事件发生,就有机制保证向应用进程发送信号,显然设备驱动程序扮演重要角色,实际终端tty、网络socket等的标准实现已经包括了实时信号驱动的支持,所以,在Linux中它们可以如上直接使用。但有些设备的驱动程序还并没有支持,所以需要定制设备驱动程序。以下两个API应该是可以屏蔽所有相关琐碎操作(类似send_sig())的标准接口:
int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
void kill_fasync(struct fasync_struct **fa, int sig, int band);

如果需要支持异步通知机制,如下设备结构中需要有异步事件通知队列(它应该与睡眠队列类似),并且增加fasync()接口的实现(该函数将本进程登记到async_queue上去)。 当一个打开的文件FASYNC标志变化时(调用fcntl()函数,设置FASYNC文件标志时),fasync()接口将被调用。

struct kpp_dev {
struct cdev cdev;
struct fasync_struct *async_queue;
};

static int kpp_fasync(int fd, struct file *filp, int mode)
{
struct kpp_dev *dev = filp->private_data;
return fasync_helper(fd, filp, mode, &dev->async_queue);
}

事件发生的时机,就是中断服务程序或相应的软中断中调用kill_fasync():
if (dev->async_queue)
kill_fasync(&dev->async_queue, SIGIO, POLL_IN);
如果是写操作,就是POLL_OUT。注意,无论用户进程设定了什么期望的信号,在这个环节,发送的一般就是SIGIO。注意在设备文件关闭(release方法)时,注意执行fasync(),使得本文件的操作从上述的设备异步事件等待链表中剥离。

static int kpp_release(struct inode *inode, struct file *filp)
{
kpp_fasync(-1, filp, 0);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: