按键驱动程序
驱动程序采用Linux平台总线(Platform Bus)模型,通过 platform_driver 结构体注册驱动,利用设备树实现设备和驱动的自动匹配。驱动同时注册了一个混杂设备(miscdevice),主设备号为10,为用户空间提供 /dev/pio_button 设备节点。
这个驱动我们不采用编译进内核的方式,而是当作一个可以动态加载的模块。这个驱动模块由源文件和makefile组成。
驱动源文件pio_interrupt.c:
//platform driver #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/clk.h> #include <linux/uaccess.h> #include <linux/io.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/unistd.h> #include <linux/device.h> #include <linux/of.h> #include <linux/slab.h> #include <linux/sched.h> // define the interrupt registers, refer to embedded ip user guide for infomation #define PIO_REGISTER_INTERRUPTMSK 2 #define PIO_REGISTER_EDGECAPTUE 3 struct irq_des { int button_irq; void __iomem *pio_base; }; static volatile int key_values; static volatile int ev_press = 0; //define the wait quene head static DECLARE_WAIT_QUEUE_HEAD(button_waitq); struct irq_des pio_irq; static irqreturn_t buttons_interrupt(int irq, void *dev_id) { if(irq ==pio_irq.button_irq){ key_values = readl(pio_irq.pio_base+PIO_REGISTER_EDGECAPTUE*4); ev_press = 1; wake_up_interruptible(&button_waitq); } //clear all edge caputre registers writel(0x0f,pio_irq.pio_base+PIO_REGISTER_EDGECAPTUE*4); return IRQ_RETVAL(IRQ_HANDLED); } static int pio_buttons_open(struct inode *inode, struct file *file) { int err = 0; err = request_irq(pio_irq.button_irq, buttons_interrupt, 0, "PIO_BUTTON", NULL); if (err) { disable_irq(pio_irq.button_irq); free_irq(pio_irq.button_irq, NULL); return -EBUSY; } ev_press = 0; //enable pio interrupt writel(0x0f,pio_irq.pio_base+PIO_REGISTER_INTERRUPTMSK*4); return 0; } static int pio_buttons_close(struct inode *inode, struct file *file) { free_irq(pio_irq.button_irq, NULL); return 0; } static ssize_t pio_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err; wait_event_interruptible(button_waitq, ev_press); ev_press = 0; err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count)); return err ? -EFAULT : (ssize_t)min(sizeof(key_values), count); } static unsigned int pio_buttons_poll( struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = pio_buttons_open, .release = pio_buttons_close, .read = pio_buttons_read, .poll = pio_buttons_poll, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = "pio_button", .fops = &dev_fops, }; static int my_plat_probe(struct platform_device *pdev) { int ret; struct resource *res; int irq; int error; res = platform_get_resource(pdev,IORESOURCE_MEM,0); if(res == NULL) return -ENOENT; if(!request_mem_region(res->start,resource_size(res),pdev->name)){ error=-EBUSY; return error; } pio_irq.pio_base=ioremap(res->start,resource_size(res)); irq=platform_get_irq(pdev,0); if(irq<0) { return irq; } pio_irq.button_irq=irq; ret = misc_register(&misc); if(ret) return ret; return 0; } static void my_plat_remove(struct platform_device *pdev) { struct resource *res; printk("my platfrom device has removed.\n"); res=platform_get_resource(pdev,IORESOURCE_MEM,0); release_mem_region(res->start,resource_size(res)); misc_deregister(&misc); //return 0; } static struct of_device_id pio_button_match[]={ {.compatible="terasic,pio-button",.data=NULL}, {} }; MODULE_DEVICE_TABLE(of,pio_button_match); struct platform_driver my_buttons_drv = { .probe = my_plat_probe, .remove = my_plat_remove, .driver = { .owner = THIS_MODULE, .name = "my_buttons", .of_match_table=pio_button_match, }, }; static int __init platform_drv_init(void) { int ret; ret = platform_driver_register(&my_buttons_drv); return ret; } static void __exit platform_drv_exit(void) { platform_driver_unregister(&my_buttons_drv); } module_init(platform_drv_init); module_exit(platform_drv_exit); MODULE_AUTHOR("Terasic"); MODULE_LICENSE("GPL");
驱动对应的Makefile文件如下:
KDIR := /home/doreen/de25_nan0_sdmmc/linux-socfpga PWD := $(shell pwd) default: make -C $(KDIR) M=$(PWD) modules clean: make -C $(KDIR) M=$(PWD) clean obj-m += pio_interrupt.o
注意上面Makefile文件当中 KDIR := /home/doreen/de25_nan0_sdmmc/linux-socfpga 是指定Linux Kernel 源码路径,读者要改成自己当前开发路径。
设置好编译链环境变量,执行make编译出pio_interrupt.ko文件:
export CROSS_COMPILE=aarch64-linux-gnu- export ARCH=arm64 export PATH=/usr/bin:$PATH make

本文源码参考:https://github.com/hbwangjinwu/PIO_Interrupt/tree/master
往期阅读
无
浙公网安备 33010602011771号