1. 程式人生 > >linux字元驅動之poll機制GPIO狀態監測驅動

linux字元驅動之poll機制GPIO狀態監測驅動

應用背景:

1、A20全志方案;

2、Linux系統;

3、兩個GPIO口,一個用於檢測門的狀態(門開或門關),一個用於檢測開門按鈕是否按下;

4、生成兩個字元裝置:/dev/doorstate和/dev/doorstate,上層C++通過select函式監測;

#include <linux/kernel.h>  
#include <linux/fs.h>  
#include <linux/init.h>  
#include <linux/delay.h>  
#include <linux/irq.h>  
#include <asm/uaccess.h>  
#include <asm/irq.h>  
#include <asm/io.h>  
#include <linux/module.h>  
#include <linux/device.h>
#include <mach/hardware.h> 
#include <linux/interrupt.h>
#include <linux/poll.h>
#include <linux/gpio.h>
#include <linux/wait.h>
#include <linux/sched.h>
  
/* 定義並初始化等待佇列頭 */  
static DECLARE_WAIT_QUEUE_HEAD(state_waitq);  
static DECLARE_WAIT_QUEUE_HEAD(open_waitq);    

#define GPIO_PH19 186
#define GPIO_PH20 187

static struct class *door_drv_class;  
static struct device *door_drv_device;
static struct device *door_drv_device_2;

static int state_pressed = 0; 
static int open_pressed = 0;
static unsigned char door_state_val;
static unsigned char door_open_val;

int door_state_dev_id; 
int door_open_dev_id;

static u32 int_handle = 0;
static u32 int2_handle = 0;

struct door_data_struct {
	int unused;
};

struct door_data_struct door_data = {
	0
};
  
/* 使用者中斷處理函式 */  
static u32 door_state_irq(struct door_data_struct *data)
{
    unsigned int pinval;

    pinval = __gpio_get_value(GPIO_PH19);  
    printk("door_state_irq val=%d\n", pinval);
  
    if (pinval)   // 門開了
    {   
        door_state_val = 0x01;
    }  
    else   // 門關了
    {  
        door_state_val = 0x00;
    }  
  
    state_pressed = 1;                            /* 表示中斷已經發生 */  
    wake_up_interruptible(&state_waitq);   /* 喚醒休眠的程序 */  
	
    return 0;
}

static int door_drv_open(struct inode * inode, struct file * filp)  
{  
	int_handle = sw_gpio_irq_request(GPIO_PH19, TRIG_EDGE_DOUBLE, (peint_handle)door_state_irq, &door_data);
	if (!int_handle) {
		printk("open ph19 failed\n");
		goto exit_irq_request_failed;
	}

	printk("open ph19 ok\n");
	return 0;
exit_irq_request_failed:
       sw_gpio_irq_free(int_handle);

	return -1;
}  
  
static ssize_t door_drv_read(struct file *file, char __user *user, size_t size, loff_t *ppos)  
{  
    if (size != 1)  
            return -EINVAL;  
      
    /* 當沒有按鍵按下時,休眠。 
     * 即state_pressed = 0; 
     * 當有按鍵按下時,發生中斷,在中斷處理函式會喚醒 
     * 即state_pressed = 1;  
     * 喚醒後,接著繼續將資料通過copy_to_user函式傳遞給應用程式 
     */  
    wait_event_interruptible(state_waitq, state_pressed);  
    copy_to_user(user, &door_state_val, 1);  
      
    /* 將state_pressed清零 */  
    state_pressed = 0;  
    return 1;     
}  
  
static int door_drv_close(struct inode *inode, struct file *file)  
{  
    sw_gpio_irq_free(int_handle);

    return 0;  
}  
  
static unsigned int door_drv_poll(struct file *file, poll_table *wait)  
{  
    unsigned int mask = 0;  
  
    /* 該函式,只是將程序掛在state_waitq佇列上,而不是立即休眠 */  
    poll_wait(file, &state_waitq, wait);  
  
    /* 當沒有按鍵按下時,即不會進入按鍵中斷處理函式,此時state_pressed = 0  
     * 當按鍵按下時,就會進入按鍵中斷處理函式,此時state_pressed被設定為1 
     */  
    if(state_pressed)  
    {  
        mask |= POLLIN | POLLRDNORM;  /* 表示有資料可讀 */  
    }  
  
    /* 如果有按鍵按下時,mask |= POLLIN | POLLRDNORM,否則mask = 0 */  
    return mask;    
}  


/******************************************************************************/  
/**********************監測門內開門按鈕*********************************/
static u32 door_open_irq(struct door_data_struct *data)
{   
    unsigned int pinval;  
	
    pinval = __gpio_get_value(GPIO_PH20);  
    printk("door_open_irq val=%d\n", pinval);
  
    if (pinval)  // 開門
    {  
	door_open_val = 0x01;
	open_pressed = 1;                            /* 表示中斷已經發生 */  
	wake_up_interruptible(&open_waitq);   /* 喚醒休眠的程序 */  
    }  
    else  // 開門不成功
    {  
	door_open_val = 0x00;
	open_pressed = 0;
    }  
	
    return 0; 
}  

static int door_drv_open_2(struct inode * inode, struct file * filp)  
{
	int2_handle = sw_gpio_irq_request(GPIO_PH20, TRIG_EDGE_POSITIVE, (peint_handle)door_open_irq, &door_data);
	if (!int2_handle) {
		printk("open ph20 failed\n");
		goto exit_irq_request_failed;
	}

	printk("open ph20 ok\n");
	return 0;
	
exit_irq_request_failed:
       sw_gpio_irq_free(int2_handle);

	return -1;
}

static ssize_t door_drv_read_2(struct file *file, char __user *user, size_t size, loff_t *ppos) 
{
    if (size != 1)  
            return -EINVAL;  
      
    /* 當沒有按鍵按下時,休眠。 
     * 即state_pressed = 0; 
     * 當有按鍵按下時,發生中斷,在中斷處理函式會喚醒 
     * 即state_pressed = 1;  
     * 喚醒後,接著繼續將資料通過copy_to_user函式傳遞給應用程式 
     */  
    wait_event_interruptible(open_waitq, open_pressed);  
    copy_to_user(user, &door_open_val, 1);  
      
    /* 將state_pressed清零 */  
    open_pressed = 0;  
    return 1;     

}

static int door_drv_close_2(struct inode *inode, struct file *file)  
{
    sw_gpio_irq_free(int2_handle);
	
    return 0;  
}

static unsigned int door_drv_poll_2(struct file *file, poll_table *wait)
{
    unsigned int mask = 0;  
  
    /* 該函式,只是將程序掛在open_pressed佇列上,而不是立即休眠 */  
    poll_wait(file, &open_waitq, wait);  
  
    /* 當沒有按鍵按下時,即不會進入按鍵中斷處理函式,此時open_pressed = 0  
     * 當按鍵按下時,就會進入按鍵中斷處理函式,此時open_pressed被設定為1 
     */  
    if(open_pressed)  
    {  
        mask |= POLLIN | POLLRDNORM;  /* 表示有資料可讀 */  
    }  
  
    /* 如果有按鍵按下時,mask |= POLLIN | POLLRDNORM,否則mask = 0 */  
    return mask;   
}

/* File operations struct for character device */  
static const struct file_operations door_drv_fops = {  
    .owner      = THIS_MODULE,  
    .open       = door_drv_open,  
    .read       = door_drv_read,  
    .release    = door_drv_close,  
    .poll       = door_drv_poll,  
};  

static const struct file_operations door_drv_fops_2 = {  
    .owner      = THIS_MODULE,  
    .open       = door_drv_open_2,  
    .read       = door_drv_read_2,  
    .release    = door_drv_close_2,  
    .poll       = door_drv_poll_2,  
};
  
  
/* 驅動入口函式 */  
static int door_drv_init(void)  
{  
    /* 主裝置號設定為0表示由系統自動分配主裝置號 */  
    door_state_dev_id = register_chrdev(0, "doorstate", &door_drv_fops);
    door_open_dev_id = register_chrdev(0, "dooropen", &door_drv_fops_2);
  
    /* 建立fourthdrv類 */  
    door_drv_class = class_create(THIS_MODULE, "door");  
  
    /* 在fourthdrv類下建立buttons裝置,供應用程式開啟裝置*/  
    door_drv_device = device_create(door_drv_class, NULL, MKDEV(door_state_dev_id, 0), NULL, "doorstate");
    door_drv_device_2 = device_create(door_drv_class, NULL, MKDEV(door_open_dev_id, 0), NULL, "dooropen");

    printk("door_drv_init\n");
	
    return 0;  
}  

/* 驅動出口函式 */  
static void door_drv_exit(void)  
{  
    sw_gpio_irq_free(int_handle);
    sw_gpio_irq_free(int2_handle);
    unregister_chrdev(door_state_dev_id, "doorstate");  
    unregister_chrdev(door_open_dev_id, "dooropen");  
    device_unregister(door_drv_device);  //解除安裝類下的裝置  
    device_unregister(door_drv_device_2);  //解除安裝類下的裝置 
    class_destroy(door_drv_class);     //解除安裝類  
} 

module_init(door_drv_init);  //用於修飾入口函式  
module_exit(door_drv_exit);  //用於修飾出口函式     
  
MODULE_AUTHOR("ldh");  
MODULE_DESCRIPTION("door gpio driver");  
MODULE_LICENSE("GPL");  //遵循GPL協議 

參考文章:

http://blog.csdn.net/lwj103862095/article/details/17536069