Chinaunix首页 | 论坛 | 博客
  • 博客访问: 845388
  • 博文数量: 155
  • 博客积分: 4004
  • 博客等级: 中校
  • 技术积分: 2070
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-19 11:37
文章分类

全部博文(155)

文章存档

2014年(3)

2013年(9)

2012年(28)

2011年(20)

2010年(29)

2009年(66)

我的朋友

分类: LINUX

2009-12-01 10:50:34

kpp.c
---------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include


#include
#include
#include
#include
#include
#include

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

//<<<<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
#include
#include
#include
#include
#include
#include
#include
#include

#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;
    }
 
阅读(1531) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~