一步一步学习 Linux 驱动之 platform 机制(tiny210 按键驱动)
2015-12-29 14:48
495 查看
1、platform device
/** * @Author: ZP1015 * * @Copyright:SCUT. * * @Function:Platform device driver for button * * @Creat:2015-06-10 * * @Modify:2015-12-29 **/ #include <linux/kernel.h> #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/device.h> #include <linux/platform_device.h> #include <mach/map.h> #include <mach/gpio.h> #include <mach/regs-clock.h> #include <mach/regs-gpio.h> #include <linux/gpio_keys.h> /** gpio_keys_button gpio_keys_platform_data 头文件 **/ static struct gpio_keys_button mini210_button[] = { [0] = { .gpio = S5PV210_GPH2(0), .type = 0, .desc = "KEY0", }, [1] = { .gpio = S5PV210_GPH2(1), .type = 1, .desc = "KEY1", }, [2] = { .gpio = S5PV210_GPH2(2), .type = 2, .desc = "KEY2", }, [3] = { .gpio = S5PV210_GPH2(3), .type = 3, .desc = "KEY3", }, [4] = { .gpio = S5PV210_GPH3(0), .type = 4, .desc = "KEY4", }, [5] = { .gpio = S5PV210_GPH3(1), .type = 5, .desc = "KEY5", }, [6] = { .gpio = S5PV210_GPH3(2), .type = 6, .desc = "KEY6", }, [7] = { .gpio = S5PV210_GPH3(3), .type = 7, .desc = "KEY7", } }; static struct gpio_keys_platform_data mini210_button_data = { .buttons = mini210_button, .nbuttons = ARRAY_SIZE(mini210_button), }; static void button_platform_device_release(struct device * dev) { return ; } static struct platform_device button_platform_device = { .name = "button_platform_device_driver", .id = -1, .dev = { .release = button_platform_device_release, .platform_data = &mini210_button_data, } }; static int __init button_platform_device_init(void) { printk("button_platform_device add ok!\n"); return platform_device_register(&button_platform_device); } static void __exit button_platform_device_exit(void) { printk("button_platform_device remove ok!\n"); platform_device_unregister(&button_platform_device); } MODULE_AUTHOR("ZP1015"); MODULE_LICENSE("GPL"); module_init(button_platform_device_init); module_exit(button_platform_device_exit);
2、platform driver
/** * @Author: ZP1015 * * @Copyright:SCUT. * * @Function:platform driver for button * * @Creat:2015-6-12 * * @Modify:2015-12-29 **/ #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/poll.h> #include <linux/irq.h> #include <asm/irq.h> #include <asm/io.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include <linux/platform_device.h> #include <linux/cdev.h> #include <mach/map.h> #include <mach/gpio.h> #include <mach/regs-clock.h> #include <mach/regs-gpio.h> #include <linux/gpio_keys.h> /*传给用户态空间的数据*/ struct buttons_status_copy_to_user { int keynum;/*按键编号*/ int buttons_cur_press;/*该按键被按了多少次*/ }; static volatile int key_values[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; #define DEVICE_NAME "mybuttons_platfor_driver" #define GLOBAL_LED_MAJOR 250 static unsigned int global_led_major = GLOBAL_LED_MAJOR; static struct cdev *button_cdev = NULL; static struct class *buttons_class = NULL; struct buttons_status_copy_to_user buttons_status_statistics; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); static volatile int ev_press = 0; static void mini210_buttons_status(struct gpio_keys_button *_data) { struct gpio_keys_button *button_data = (struct gpio_keys_button *)_data; /*内核打印当前按键状态*/ printk("KEY %d: %d\n", button_data->type,key_values[button_data->type]); /*TO DO:For IRQ_TYPE_EDGE_FALLING*/ key_values[button_data->type] = key_values[button_data->type] + 1; buttons_status_statistics.buttons_cur_press = key_values[button_data->type]; buttons_status_statistics.keynum = button_data->type; ev_press = 1; wake_up_interruptible(&button_waitq); } static irqreturn_t button_interrupt(int irq, void *dev_id) { struct gpio_keys_button *button_data = (struct gpio_keys_button *)dev_id; mini210_buttons_status(button_data); return IRQ_HANDLED; } static int mini210_buttons_open(struct inode *inode, struct file *file) { return 0; } static int mini210_buttons_close(struct inode *inode, struct file *file) { return 0; } /*mini210_buttons_read()函数的申明*/ /*read调用的具体函数*/ /*由它读取键盘输入的结果*/ /*实质上就是读取自定义结构体的值*/ /*它完成了键盘作为输入设备的核心功能*/ /*数组是否可读,要根据标志位ev_press来判断*/ /*如果数组可读,则读取数据到用户buffer中*/ /*如果数组不可读,则进程进入等待队列,等待到数组可读为止*/ /*等待队列机制,是中断管理中常用到的机制*/ /*因为有些进程经常需要等待某一事件的发生*/ static int mini210_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp) { unsigned long err; /*靠ev_press标志位来判断的*/ /*进程等待队列的机制,是进程调度的一种方法*/ if (!ev_press) { if (filp->f_flags & O_NONBLOCK) return -EAGAIN; /*进程休眠,放进button_waitq等待队列*/ /*这里,把ev_press标志位设成了休眠进程的标志位了*/ /*这是为了便于利用poll_wait函数*/ /*也就是利于select函数*/ else wait_event_interruptible(button_waitq, ev_press); /*在中断处理函数中,此进程会被唤醒*/ /*唤醒前,ev_press 已被置1了*/ /*唤醒后的执行点从这里开始*/ } ev_press = 0; /*TO DO:For IRQ_TYPE_EDGE_FALLING*/ #if 1 err = copy_to_user((struct buttons_status_copy_to_user *)buff, (struct buttons_status_copy_to_user *)(&buttons_status_statistics), min(sizeof(buttons_status_statistics), count)); /*TO DO:For IRQ_TYPE_EDGE_BOTH*/ #else err = copy_to_user((int*)buff, (const int *)(&key_values), min(sizeof(key_values), count)); memset((int *)key_values,0,sizeof(key_values)); #endif return err ? -EFAULT : min(sizeof(key_values), count); } static unsigned int mini210_buttons_poll( struct file *file, struct poll_table_struct *wait) { unsigned int mask = 0; /*poll_wait()函数*/ /*会监测进程队列button_waitq里的进程*/ /*例如,如果mini210_button_read所在的进程的标志位ev_press置为1了*/ /*那么就不会再等待了*/ /*这实质上就是select函数的运行机制*/ poll_wait(file, &button_waitq, wait); if (ev_press) mask |= POLLIN | POLLRDNORM; return mask; } static struct file_operations button_fops = { .owner = THIS_MODULE, .open = mini210_buttons_open, .release = mini210_buttons_close, .read = mini210_buttons_read, .poll = mini210_buttons_poll, }; static int __devinit button_probe(struct platform_device *pdev) { int ret; int err,irq; dev_t devno; int i = 0; struct gpio_keys_platform_data *pdata = pdev->dev.platform_data; printk("button probe start!\n"); /*1.初始化cdev*/ button_cdev = cdev_alloc();//分配cdev cdev_init(button_cdev,&button_fops);//初始化cdev button_cdev->owner = THIS_MODULE; /*2.获取字符设备号*/ devno = MKDEV(global_led_major,0);//将主设备号和次设备号转换成dev_t类型 if (devno) { ret = register_chrdev_region(devno,1,DEVICE_NAME);//注册一系列设备号 } else { ret = alloc_chrdev_region(&devno,0,1,DEVICE_NAME);//未知主设备号注册设备号 global_led_major = MAJOR(devno); } if (ret < 0) { return ret; } /*3.注册字符设备*/ err = cdev_add(button_cdev,devno,1);//注册cdev if (err) { printk(KERN_NOTICE"Error %d adding button_cdev",err); goto cdev_add_fail; } printk(KERN_NOTICE"button_platform_driver init ok!\n"); /*4.自动创建设备节点,在/sys/class/下创建类目录*/ buttons_class = class_create(THIS_MODULE,"mybuttons_class");//自动创建设备节点 device_create(buttons_class,NULL,devno,NULL,"mybuttons"); /*5.注册按键中断服务函数*/ for (i = 0; i < pdata->nbuttons; i++) { if (!pdata->buttons[i].gpio) continue; /*寄存器与中断引脚的联结*/ irq = gpio_to_irq(pdata->buttons[i].gpio); /*注册中断函数*/ err = request_irq(irq, button_interrupt, IRQ_TYPE_EDGE_FALLING, pdata->buttons[i].desc, (void *)&(pdata->buttons[i])); if (err) goto request_irq_fail; } ev_press = 1; return 0; request_irq_fail: cdev_del(button_cdev); unregister_chrdev_region(devno,1); device_destroy(buttons_class,devno); class_destroy(buttons_class); i--; /*当前没注册成功不需要清除注册的中断函数*/ for (; i >= 0; i--) { if (!pdata->buttons[i].gpio) continue; irq = gpio_to_irq(pdata->buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&(pdata->buttons[i])); } return -EBUSY; cdev_add_fail: unregister_chrdev_region(devno,1); return -1; } static int __devexit button_remove(struct platform_device *pdev) { struct gpio_keys_platform_data *pdata = NULL; dev_t devno; int i,irq= 0; devno = MKDEV(global_led_major,0);//将主设备号和次设备号转换成dev_t类型 printk("button_remove!\n"); cdev_del(button_cdev); unregister_chrdev_region(devno,1); device_destroy(buttons_class,devno); class_destroy(buttons_class); if(!pdev) return 0; pdata = pdev->dev.platform_data; for (i=0; i < pdata->nbuttons; i++) { if (!pdata->buttons[i].gpio) continue; irq = gpio_to_irq(pdata->buttons[i].gpio); disable_irq(irq); free_irq(irq, (void *)&(pdata->buttons[i])); } return 0; } static struct platform_driver button_platform_driver = { .probe = button_probe, .remove = __devexit_p(button_remove), .driver = { .name = "button_platform_device_driver", .owner = THIS_MODULE, } }; static int __init button_platform_driver_init(void) { printk("button_platform_driver_init!\n"); return platform_driver_register(&button_platform_driver); } static void __exit button_platform_driver_exit(void) { printk("button_platform_driver_exit!\n"); platform_driver_unregister(&button_platform_driver); } module_init(button_platform_driver_init); module_exit(button_platform_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("ZP1015");
3、Makefile
ifneq ($(KERNELRELEASE),) obj-m :=button_platform.o else KERNELDIR :=/home/ZP1015/Desktop/linux ARM_LINUX_DIR =-I $(KERNELDIR)/arch/arm/mach-s5pv210/include/mach/ \ -I $(KERNELDIR)/arch/arm/include/asm all: make -C $(KERNELDIR) $(ARM_LINUX_DIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux- clean: rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul* *.*~ endif
4、测试程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> struct buttons_status_copy_to_user { int keynum; int buttons_cur_press; }; int main() { int fd,ret; struct buttons_status_copy_to_user buttons_status_statistics; fd = open("/dev/mybuttons", 0); if(fd < 0) { printf("open error!"); return -1; } while(1) { ret = read(fd, &buttons_status_statistics,\ sizeof(struct buttons_status_copy_to_user)); printf("you press the key %d ,%d\n", buttons_status_statistics.keynum+1,\ buttons_status_statistics.buttons_cur_press); } close(fd); return 0; }
相关文章推荐
- MINI2440按键驱动详解
- 向Tiny210移植Debian Linux
- mini2440按键驱动
- Linux设备驱动——字符驱动各种结构体
- 并发与竞态控制
- 阻塞与非阻塞I/O
- PCI总线驱动
- 总线,设备,驱动——(1)
- s3c2440 LCD ——驱动程序
- USB驱动——键盘,U盘
- 在 tiny210 下显示 字母 ,汉字,方框, 宋体,图片。
- 移植tiny210的 触摸屏驱动
- 移植 tiny210 的按键驱动
- 基于友善的superboot 移植linux内核到tiny210(s5pv210)上
- jz2440 的中断中的底半部机制
- 飞凌ok6410开发板的按键驱动改程序
- 第二个驱动程序-tq2440
- 第六个驱动程序-tq2440
- 第7个驱动程序
- tiny210 环境搭建