2416开发记录十一:按键驱动(platform/中断)
2016-01-27 10:37
393 查看
在前面几章的基础上编写了一个按键中断的驱动,并验证成功。
这里用到了字符设备驱动,platform驱动,并有资源的获取,算是比较全面的platform驱动了。
对应的makefile和以前的差不多,就不写出来了。
这里用到了字符设备驱动,platform驱动,并有资源的获取,算是比较全面的platform驱动了。
首先是设备模块代码
//my2416PlatformKeyDev.c #include <linux/miscdevice.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/string.h> #include <linux/list.h> #include <linux/pci.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include <asm/unistd.h> #include <linux/gpio.h> //2.6.32.2内核版本要求 #include <linux/poll.h> #include <linux/platform_device.h> #include <linux/gpio.h> //2.6.32.2内核版本要求 /* 参考arch/arm/plat-s3c24xx/devs.c */ /*1. 根据芯片手册来获取资源*/ static struct resource key_resource[] = { /*EINT0*/ [0]= { .flags = IORESOURCE_IRQ,//flags可以为IORESOURCE_IO, IORESOURCE_MEM, IORESOURCE_IRQ, IORESOURCE_DMA等如当flags为IORESOURCE_MEM时,start、end分别表示该platform_device占据的内存的开始地址和结束地址;当flags为IORESOURCE_IRQ时,start、end分别表示该platform_device使用的中断号的开始值和结束值,如果只使用了1个中断号,开始和结束值相同。 .start = IRQ_EINT0,//资源的起始地址,结束地址 .end = IRQ_EINT0, .name = "S3C24XX_EINT0", }, /*EINT1*/ [1]= { .flags = IORESOURCE_IRQ, .start = IRQ_EINT1, .end = IRQ_EINT1, .name = "S3C24xx_EINT1", }, /*EINT2*/ [2]= { .flags = IORESOURCE_IRQ, .start = IRQ_EINT2, .end = IRQ_EINT2, .name = "S3C24xx_EINT2", }, /*EINT3*/ [3]= { .flags = IORESOURCE_IRQ, .start = IRQ_EINT3, .end = IRQ_EINT3, .name = "S3C24xx_EINT3", }, [4]= { .flags = IORESOURCE_IRQ, .start = IRQ_EINT4, .end = IRQ_EINT4, .name = "S3C24xx_EINT4", }, }; void key_release(struct device *dev) { } /*1.构建平台设备结构体,将平台资源加入进来,需要注意的是platform_device 实质上是经过处理过的设备,在platform_device结构体中存在一个设备结构体,与之前的设备存在差别的是引入了设备资源。这些设备资源就能实现对设备寄存器,中断等资源的访问。*/ struct platform_device key_device = { .name = "myplatformkey", /* 设备名,使用名为"myplatformkey"的平台驱动 ,注册后,会在/sys/device/platform目录下创建一个以name命名的目录,并且创建软连接到/sys/bus/platform/device下。*/ .id = -1,/*设备id,一般为-1,如果是-1,表示同样名字的设备只有一个举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1 如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。 */ .dev = {//结构体中内嵌的device结构体。 .release = key_release, }, .num_resources = ARRAY_SIZE(key_resource),/* 设备所使用各类资源数量 */ .resource = key_resource,//定义平台设备的资源 }; /*2。把我们的设备资源挂在到虚拟总线的设备连表中去, 如果没有定义上面的struct platform_device led_device,那么需要下面的init函数*/ int key_dev_init(void) { platform_device_register(&key_device); //platform设备的初注册 return 0; } /*如果没有定义上面的struct platform_device led_device,那么需要使用platform_device_alloc()函数分配一个platform_device结构体,然后使用platform_device_add_resources函数添加资源,最后使用platform_device_add函数 struct platform_device *my_buttons_dev; static int __init platform_dev_init(void) { int ret; my_buttons_dev = platform_device_alloc("my_buttons", -1); platform_device_add_resources(my_buttons_dev,key_resource,6);//添加资源 ret = platform_device_add(my_buttons_dev); //platform设备的注册 if(ret) platform_device_put(my_buttons_dev); return ret; } */ void key_dev_exit(void) { platform_device_unregister(&key_device); } module_init(key_dev_init); module_exit(key_dev_exit); MODULE_AUTHOR("Zhao Yidong <zmurder@outlook.com>"); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("A sample key module dev int"); MODULE_ALIAS("key module dev int");
对应的makefile和以前的差不多,就不写出来了。
驱动模块代码
//my2416PlatformKeyDriver.c #include <linux/miscdevice.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include<mach/irqs.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/string.h> #include <linux/list.h> #include <linux/pci.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include <asm/unistd.h> #include <linux/gpio.h> //2.6.32.2内核版本要求 #include <linux/platform_device.h> #include <linux/cdev.h> #include <linux/interrupt.h> #include <linux/poll.h> #include <linux/irq.h> #include<linux/sched.h> #define DEVICE_NAME "mykeys" #define DRIVER_NAME "my2416keys"//加载驱动之后会在/dev/目录下发现my2416keys,应用程序可以使用 #define MYKEY_SIZE 0x1000//全局内存最大4K #define MEM_CLEAR 0x1//清零全局内存 #define MYKEY_MAJOR 251 //预设的mykeys的主设备号 #define NUM_RESOURCE 5//资源数 static int mykey_major = MYKEY_MAJOR; struct class *mykey_class; static struct device *mykeyDevice=NULL; /*中断结构体定义*/ struct irqs { int pirqs[NUM_RESOURCE]; char *names[NUM_RESOURCE]; }irqs; struct mykey_dev { struct cdev cdev;//cdev结构体 //unsigned char mem[MYKEY_SIZE];//全局内存 }; struct mykey_dev *mykey_devp;//设备结构体指针 //定义并初始化等待队列头 static DECLARE_WAIT_QUEUE_HEAD(key_waitq); static volatile int ev_press = 0; static volatile int key_values; //定义GPIO管脚 static unsigned long key_table [] = { S3C2410_GPF(0), S3C2410_GPF(1), S3C2410_GPF(2), S3C2410_GPF(3), S3C2410_GPF(4), }; //设置管脚模式 static unsigned int key_cfg_table [] = { S3C2410_GPF0_EINT0, //随内核版本中定义类型的变化,在arch/arm/mach-sc2410/include/mach/Regs-gpio.h文件中定义 S3C2410_GPF1_EINT1, S3C2410_GPF2_EINT2, S3C2410_GPF3_EINT3, S3C2410_GPF4_EINT4, }; /* static int my2416_keys_ioctl(struct file* filp, unsigned int cmd,unsigned long arg) { switch(cmd) { case KEY_ON: s3c2410_gpio_setpin(S3C2410_GPB(1), KEY_ON); break; case KEY_OFF: //s3c2410_gpio_setpin(key_table[arg], !cmd); s3c2410_gpio_setpin(S3C2410_GPB(1), KEY_OFF); break; default: printk("KEY control:no cmd\n"); printk("KEY control are KEY_ON or KEY_OFF\n"); return(-EINVAL); } return 0; }*/ static irqreturn_t keys_interrupt(int irq, void *dev_id) { int i; for(i=0; i<NUM_RESOURCE; i++){ if(irq == irqs.pirqs[i]){ key_values = i; ev_press = 1; wake_up_interruptible(&key_waitq); } } return IRQ_RETVAL(IRQ_HANDLED); } //打开函数 static int my2416_key_open(struct inode *inode, struct file *file) { int i=0; int err = 0; ////这里只定义了一个io口GPB1配置GPIO for (i = 0; i < NUM_RESOURCE; i++) { s3c2410_gpio_cfgpin(key_table[i], key_cfg_table[i]); //s3c2410_gpio_pullup(key_table[i], 1);//拉高引脚 根据原理图 按下为低电平 } //申请中断 for (i = 0; i < NUM_RESOURCE; i++) { // |上升沿下降沿触发 err = request_irq(irqs.pirqs[i], keys_interrupt, IRQ_TYPE_EDGE_BOTH , irqs.names[i], NULL); if (err) break; } if (err) { i--; for (; i >= 0; i--) { if (irqs.pirqs[i] < 0) { continue; } disable_irq(irqs.pirqs[i]); free_irq(irqs.pirqs[i], NULL); } return -EBUSY; } return 0; } static int my2416_key_close(struct inode *inode, struct file *file) { int i; for (i = 0; i < NUM_RESOURCE; i++) { free_irq(irqs.pirqs[i], NULL); } return 0; } static int my2416_key_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err; if (!ev_press) { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; else //添加等待队列,条件是ev_press wait_event_interruptible(key_waitq, ev_press); } ev_press = 0; err = copy_to_user(buff, (const void *)&key_values, min(sizeof(key_values), count)); return err ? -EFAULT : min(sizeof(key_values), count); } static unsigned int my2416_key_poll( struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; poll_wait(file, &key_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM;//POLLIN|POLLRDNORM表示有数据可读 return mask; } //dev_fops操作指令集 static struct file_operations my2416Led_fops = { .owner =THIS_MODULE, .open = my2416_key_open, .release = my2416_key_close, .read = my2416_key_read, .poll = my2416_key_poll, //.unlocked_ioctl = my2416_keys_ioctl,//这里必须是unlocked_ioctl而不是ioctl。 }; /*//第三步:混杂设备定义 static struct miscdevice my2416Ledmisc = { .minor = MISC_DYNAMIC_MINOR, .name = DEVICE_NAME,//加载驱动之后会在/dev/目录下发现mykeys,应用程序可以使用 .fops = &my2416Led_fops, }; */ static void Key_setup_cdev(struct mykey_dev *dev,int index) { int err, devno = MKDEV(mykey_major,index); /*初始化cdev,并将相关的文件操作添加进来*/ cdev_init(&dev->cdev, &my2416Led_fops); dev->cdev.owner = THIS_MODULE; //dev->cdev.ops = &my2416Led_fops; /*注册字符设备*/ err = cdev_add(&dev->cdev, devno, 1); if (err) printk("Error %d\n", err); else printk("have finish add\n"); } /*3。实现probe函数*/ static int key_probe(struct platform_device *pdev) { int result; unsigned char i=0; struct resource * irq_resource; printk("key_probe\n"); /*创建一个设备号*/ dev_t devno=MKDEV(mykey_major,0); /*注册一个设备号*/ /*如果定义了主设备号采用静态申请的方式*/ if(mykey_major) { result=register_chrdev_region(devno,1,DEVICE_NAME); } else//动态申请设备号 { result= alloc_chrdev_region(&devno,0,1,DEVICE_NAME); mykey_major=MAJOR(devno); } if(result<0) { printk (DEVICE_NAME " can't register\n"); return result; } printk("key devno\n"); //动态申请设备结构体内存 mykey_devp=kmalloc(sizeof(struct mykey_dev), GFP_KERNEL); if(!mykey_devp)//申请失败 { printk("kmalloc faile\n"); result=-ENOMEM; goto fail_malloc; } printk("kmalloc succeed\n"); /*清除空间*/ memset(mykey_devp,0,sizeof(struct mykey_dev)); /*创建一个设备*/ Key_setup_cdev(mykey_devp,0); //class_create和device_create函数是为了自动在/dev下创建DRIVER_NAME设备文件。 //创建一个类,这个类存放于sysfs下面 mykey_class=class_create(THIS_MODULE,DRIVER_NAME); if(IS_ERR(mykey_class)) { result = PTR_ERR(mykey_class); printk("class create faikey\n"); goto class_create_fail; } //在/dev目录下创建相应的设备节点 mykeyDevice = device_create(mykey_class,NULL,devno,NULL,DRIVER_NAME); if(IS_ERR(mykeyDevice)) { result = PTR_ERR(mykeyDevice); printk("device_create faile\n"); goto device_create_faile; } //获得资源 for(i=0; i < NUM_RESOURCE; ++ i) { /*获得设备的资源*/ irq_resource = platform_get_resource(pdev,IORESOURCE_IRQ,i); if(NULL == irq_resource) { return -ENOENT; } irqs.pirqs[i] = irq_resource->start; /*实现名字的复制操作*/ //strcpy(tq2440_irqs.name[i],irq_resource->name); /*这一句是将指针的地址指向一个具体的地址*/ irqs.names[i] = irq_resource->name; printk("irqs name are %s\n",irq_resource->name); } /* cdev_init(&(mykey_dev.cdev),&my2416Led_fops); mykey_dev.cdev.owner = THIS_MODULE; ret = cdev_add(&(mykey_dev.cdev),devno,1); if(ret) { printk("Add device error\n"); return ret; } printk (DEVICE_NAME " Initialized \n"); return 0; */ fail_malloc: unregister_chrdev_region(devno,1);//释放设备号 class_create_fail: unregister_chrdev_region(MKDEV(mykey_major, 0), 1);//释放设备号 device_create_faile: class_destroy(mykey_class);/*注销创建的设备类*/ return result; } int key_remove(struct platform_device *dev) { /*注销设备*/ device_destroy(mykey_class,MKDEV(mykey_major, 0)); /*注销创建的设备类*/ class_destroy(mykey_class); /*字符设备注销*/ cdev_del(&mykey_devp->cdev);//注销cdev kfree(mykey_devp);/*释放设备结构体内存*/ unregister_chrdev_region(MKDEV(mykey_major, 0), 1);//释放设备号 printk(DEVICE_NAME " exit\n"); return 0; } /*1。平台驱动定义*/ static struct platform_driver key_driver = { .probe = key_probe, /* 平台总线下增加一个平台设备时,调用枚举函数 */ .remove = key_remove, /* 平台总线下去掉一个平台设备时,调用remove函数 */ .driver = { .name = "myplatformkey", /* 能支持名为"myplatformkey"的平台设备 */ .owner = THIS_MODULE, }, }; /*2。注册,把我们的驱动加入到平台设备驱动连表中去*/ static int key_drv_init(void) { int ret; /*平台驱动注册*/ ret=platform_driver_register(&key_driver); return ret; } static void __exit key_drv_exit(void) { /*平台驱动注销*/ platform_driver_unregister(&key_driver); } module_init(key_drv_init); module_exit(key_drv_exit); MODULE_LICENSE("GPL");
测试的应用程序
#include <stdio.h> #include <stdlib.h> #include<fcntl.h> //open() close() #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> //read() wrie() #include<sys/ioctl.h> #include<sys/select.h> #include<sys/time.h> #include<errno.h> #define DEVICE_NAME "/dev/my2416keys" #define LED_ON 0 #define LED_OFF 1 int main() { int buttons_fd; int key_value = 0; /*open函数测试*/ printf("My2416 keys dev test!\n"); buttons_fd=open(DEVICE_NAME,O_RDWR); printf("fd=%d\n",buttons_fd); if(buttons_fd < 0) { perror("open device buttons\n"); exit(1); } while(1) { fd_set rds; int ret; FD_ZERO(&rds); FD_SET(buttons_fd,&rds); /*poll函数测试*/ ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL); if(ret < 0) { perror("select"); exit(1); } if(ret == 0) { printf("Timeout.\n"); } else if(FD_ISSET(buttons_fd,&rds)) { /*read函数测试*/ int ret = read(buttons_fd,&key_value,sizeof key_value); if(ret != sizeof key_value) { if(errno != EAGAIN) perror("read buttons\n"); continue; } else { printf("buttons_value:%d\n",key_value+1); } } } /*release函数测试*/ close(buttons_fd); return 0; }
相关文章推荐
- RegExp类型
- 表格功能之--前台筛选
- LOG_DEBUG
- 自定义NSLog宏
- 数学之路(机器学习实践指南)-文本挖掘与NLP(1)
- 20160127--Jquery的ajax与post使用
- 怀念油麻地
- 动态调试Android笔记
- 国安广视或将成为DVB+OTT重磅玩家
- HashMap遍历
- CSS中的Position、Float属性详解
- 保护继承
- HDU 2089 不要62
- 基于JavaScript实现瀑布流效果(循环渐近)
- Android基础知识学习记录
- Hack5-1 TextSwitcher实现平滑过渡
- SuperMap超图——使用SQLite数据库中的点位信息创建数据源并在图层中显示数据
- 【QNX】从不同地方执行程序,调度策略不一样
- PHP的几种缓存方式
- [JS]document.body.clientHeight的取值(转)