【Tiny6410 And Linux】—(2.3)—使用工作佇列處理按鍵中斷——程式碼
阿新 • • 發佈:2019-02-06
做了做用工作佇列處理按鍵中斷的實驗,對中斷開始明白~~
呵呵~~其實今天就是加了個全域性變數(雖然這在驅動程式中不是很合適吧),還有就是加了個消抖(就是通過延時等待而已)!
1、驅動程式
①、plat_btn_dev.c
#include <linux/module.h> #include <linux/types.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/device.h> #define DEVICE_NAME "tiny6410_buttons" /* 平臺資源的定義 */ static struct resource tiny6410_buttons_resource[] = { [0] = { .start = IRQ_EINT(0), .end = IRQ_EINT(0), .flags = IORESOURCE_IRQ, }, [1] = { .start = IRQ_EINT(1), .end = IRQ_EINT(1), .flags = IORESOURCE_IRQ, }, [2] = { .start = IRQ_EINT(2), .end = IRQ_EINT(2), .flags = IORESOURCE_IRQ, }, [3] = { .start = IRQ_EINT(3), .end = IRQ_EINT(3), .flags = IORESOURCE_IRQ, }, [4] = { .start = IRQ_EINT(4), .end = IRQ_EINT(4), .flags = IORESOURCE_IRQ, }, [5] = { .start = IRQ_EINT(5), .end = IRQ_EINT(5), .flags = IORESOURCE_IRQ, }, [6] = { .start = IRQ_EINT(19), .end = IRQ_EINT(19), .flags = IORESOURCE_IRQ, }, [7] = { .start = IRQ_EINT(20), .end = IRQ_EINT(20), .flags = IORESOURCE_IRQ, } /* 這裡不需要加逗號 */ }; static struct platform_device *tiny6410_buttons_dev; static int __init platform_init(void) { printk("[Call platform_init!]\n"); /* 分配一個 platform_device 結構 */ tiny6410_buttons_dev = platform_device_alloc(DEVICE_NAME, -1); /* 為平臺裝置新增平臺裝置資源 */ platform_device_add_resources(tiny6410_buttons_dev, tiny6410_buttons_resource, 8); /* 註冊平臺裝置 */ platform_device_add(tiny6410_buttons_dev); return 0; } static void __exit platform_exit(void) { printk("[Call platform_exit!]\n"); platform_device_unregister(tiny6410_buttons_dev); } module_init(platform_init); module_exit(platform_exit); MODULE_AUTHOR("_Justin"); MODULE_LICENSE("GPL");
②、plat_but_drv.c
#include <linux/module.h> #include <linux/types.h> #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/uaccess.h> #include <linux/io.h> #include <mach/map.h> #include <mach/regs-gpio.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/unistd.h> #include <linux/device.h> #include <linux/workqueue.h> #include <mach/gpio-bank-n.h> #include <mach/gpio-bank-l.h> #define DRIVER_NAME "tiny6410_buttons" #define DEVICE_NAME "tiny6410_buttons" /* 注意: * 在一個較規範的驅動程式中,通常不會將裝置資源在平臺驅動程式中 * 以全域性變數的形式給出(這裡是為了簡化操作,將部分裝置資源以全域性變數形式 * 給出),一個更好的辦法是,將這些資源都新增到對應的平臺裝置中,以平臺 * 裝置資源或者平臺裝置似有資料的形式管理。 */ /* 記錄按鍵中斷號 */ typedef struct { int irq; /* 中斷號,初始化為 0,將在 * probe 獲取資源 */ int num; /* 對應的按鍵編號 */ char *name; /* 中斷所屬的名字 */ } button_irq_t; /* 按鍵資料結構 */ typedef struct { wait_queue_head_t bwq; /* 按鍵等待佇列 */ struct delayed_work dwork; /* 用於按鍵中斷下半部分的延遲工作 */ button_irq_t *birqs; /* 指向記錄按鍵中斷資源陣列 */ int nirqs; /* 裝置支援的按鍵中斷數 */ int val; /* 記錄鍵值 */ volatile int cnum; /* 記錄產生中斷的按鍵編號 */ volatile int press; /* 等待佇列條件 */ } buttons_data_t; button_irq_t button_irqs[] = { {0, 0, "KEY0"}, {0, 1, "KEY1"}, {0, 2, "KEY2"}, {0, 3, "KEY3"}, {0, 4, "KEY4"}, {0, 5, "KEY5"}, {0, 6, "KEY6"}, {0, 7, "KEY7"}, }; /* 按鍵驅動資料全域性變數 g_bd */ static buttons_data_t g_bd = { .birqs = button_irqs, .nirqs = sizeof(button_irqs) / sizeof(button_irqs[0]), .press = 0, }; /* * buttons_work_func 函式 * 延遲工作的回撥函式則負責完成中斷處理程式中未完成的工作 */ static void buttons_work_func(struct work_struct *w) { int down; unsigned tmp; int num = g_bd.cnum; switch(num) { case 0: case 1: case 2: case 3: case 4: case 5: tmp = readl(S3C64XX_GPNDAT); down = !(tmp & (1 << num)); break; case 6: case 7: tmp = readl(S3C64XX_GPLDAT); down = !(tmp & (1 << (num + 5))); break; default: down = 0; } if(down == !(g_bd.val & (1 << num))) { g_bd.val = down ? g_bd.val | (1<<num) : g_bd.val & ~(1 << num); g_bd.press = 1; wake_up_interruptible(&g_bd.bwq); } } /* * buttons_interrupt * 使用一個整形變數 key_value 的 0~7 位來記錄鍵值,0~7 * 位分別對應 KEY0~KEY7 的擡起或者按下情況( 1 表示按下)。 */ static irqreturn_t buttons_interrupt(int irq,void *dev_id) { /* 定義一個指標,指向所對應的中斷號 */ button_irq_t *birq = (button_irq_t *)dev_id; g_bd.cnum = birq->num; /* 延遲 20ms,用作按鍵消抖 */ schedule_delayed_work(&g_bd.dwork,HZ/50); return IRQ_RETVAL(IRQ_HANDLED); } /* * tiny6410_buttons_open */ static int tiny6410_buttons_open(struct inode *inode,struct file *file) { int i; int err = 0; for(i = 0;i < sizeof(button_irqs) / sizeof(button_irqs[0]);i ++ ) { if (button_irqs[i].irq < 0) continue; /* 申請中斷 */ err = request_irq(button_irqs[i].irq,buttons_interrupt, IRQ_TYPE_EDGE_BOTH, button_irqs[i].name, (void *)&button_irqs[i]); if (err) break; } if(err) { i--; for(;i >= 0;i -- ) { if(button_irqs[i].irq < 0) continue; disable_irq(button_irqs[i].irq); free_irq(button_irqs[i].irq,(void *)&button_irqs[i]); } return -EBUSY; } // ev_press = 1; return 0; } /* * tiny6410_buttons_close */ static int tiny6410_buttons_close(struct inode *inode,struct file *file) { int i; for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i ++ ) { if(button_irqs[i].irq < 0) continue; free_irq(button_irqs[i].irq,(void *)&button_irqs[i]); } return 0; } /* * tiny6410_buttons_read */ static int tiny6410_buttons_read(struct file *filp,char __user *buff, size_t count,loff_t *offp) { unsigned long err; if(!g_bd.press) { if(filp->f_flags & O_NONBLOCK) return -EAGAIN; else wait_event_interruptible(g_bd.bwq,g_bd.press); } g_bd.press = 0; err = copy_to_user((void *)buff,(const void *)(&g_bd.val), min(sizeof(&g_bd.val),count)); return err ? -EFAULT : min(sizeof(&g_bd.val),count); } /* * poll 實現函式 */ static unsigned int tiny6410_buttons_poll(struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; poll_wait(file,&g_bd.bwq,wait); if(g_bd.press) mask |= POLLIN | POLLRDNORM; return mask; } /* * file_operation */ static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = tiny6410_buttons_open, .release = tiny6410_buttons_close, .read = tiny6410_buttons_read, .poll = tiny6410_buttons_poll, }; /* * 混合裝置結構體 */ static struct miscdevice tiny6410_buttons_misc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME, .fops = &dev_fops, }; /* * probe 實現函式 */ static int tiny6410_buttons_probe(struct platform_device *pdev) { int ret; int i; static struct resource *buttons_irq; printk("[call %s]\n", __func__); /* 初始化等待佇列 */ init_waitqueue_head(&g_bd.bwq); /* 初始化 delay_work 結構,來使用延遲排程工作 */ INIT_DELAYED_WORK(&g_bd.dwork,buttons_work_func); /* 獲取裝置資源 */ for(i = 0;i < 8;i ++ ) { buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i); button_irqs[i].irq = buttons_irq->start; } ret = misc_register(&tiny6410_buttons_misc); return 0; } /* * remove 實現函式 */ static int tiny6410_buttons_remove(struct platform_device *dev) { misc_deregister(&tiny6410_buttons_misc); return 0; } /* 平臺裝置驅動結構 */ static struct platform_driver tiny6410_buttons_driver = { .probe = tiny6410_buttons_probe, .remove = tiny6410_buttons_remove, .driver = { .owner = THIS_MODULE, .name = DRIVER_NAME, }, }; static int __init buttons_init(void) { printk("[Call buttons_init!]\n"); /* 註冊驅動 */ platform_driver_register(&tiny6410_buttons_driver); return 0; } static void __exit buttons_exit(void) { printk("[Call buttons_exit!]\n"); platform_driver_unregister(&tiny6410_buttons_driver); } module_init(buttons_init); module_exit(buttons_exit); MODULE_AUTHOR("_Justin"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Tiny6410 Buttons Driver");
③、Makefile
ifneq ($(KERNELRELEASE),)
obj-m := plat_btn_dev.o plat_btn_drv.o
else
KDIR := /home/_Jana/linux-2.6.38
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.order
endif
2、測試程式
①、app_btn.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/select.h> #include <time.h> #define DEVICE_NAME "/dev/tiny6410_buttons" static int key_value = 0; int main(void) { int fd, ret; fd_set rfds; int last_kval = key_value; if(-1 == (fd = open(DEVICE_NAME,O_RDONLY))) { printf("open %s error\n",DEVICE_NAME); _exit(EXIT_FAILURE); } /* 先清空集合 */ FD_ZERO(&rfds); /* 設定要監控的檔案描述符 */ FD_SET(fd,&rfds); printf("Test for tiny6410_buttons: ...\n"); while(1) { if(-1 == (ret = select(fd + 1,&rfds,NULL,NULL,NULL))) { printf("select error\n"); _exit(EXIT_FAILURE); } if(FD_ISSET(fd,&rfds)) { read(fd, &key_value, sizeof(key_value)); int i; for(i = 0;i < 8;i ++ ) { if((key_value & (1 << i)) != (last_kval & (1 << i))) { printf("KEY%d: %s (key_value=0x%x)\n", i+1, (key_value& (1<<i))? "DOWN": "UP", key_value); } } last_kval = key_value; } } _exit(EXIT_SUCCESS); }
3、測試結果