linux字元驅動之poll機制GPIO狀態監測驅動
阿新 • • 發佈:2019-01-06
應用背景:
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