Linux驱动开发———混杂设备驱动模型

来源:互联网 发布:如何制作淘宝数据包 编辑:程序博客网 时间:2024/05/19 15:23

1、混杂设备概念:

        在Linux系统中,存在一类字符设备,它们拥有相同的主设备号(10),但次设备号不同,我们称这类设备为混杂设备(miscdevice)。所有的混杂设备形成一个链表,对设备访问时内核根据次设备号查找到相应的混杂设备。

2、混杂设备描述:

        Linux中使用struct miscdevice来描述一个混杂设备:


注意:minor是这个混杂设备的次设备号,若由系统自动配置,则可以设置为MISC_DYNAMIC_MINOR,name是设备名。

3、混杂设备注册:

        Linux中使用misc_register函数来注册一个混杂设备驱动。

        int misc_register(struct miscdevice *misc)

4、混杂设备驱动的思维导图:


5、代码:

#include <linux/module.h>#include <linux/init.h>#include <linux/miscdevice.h>#include <linux/interrupt.h>#include <linux/fs.h>#include <linux/io.h>#include <mach/gpio.h>  #include <mach/regs-gpio.h>  #include <linux/irq.h>#include <linux/slab.h>#include <asm/uaccess.h>/*copy_to_user依赖的头文件*/#include <linux/uaccess.h>/*copy_to_user依赖的头文件*/#include <linux/cdev.h>#include <linux/types.h>#include <linux/slab.h>//#include <linux/wait.h>#include <linux/sched.h>//wait_event(queue,condition)中TASK_UNINTERRUPTIBLE依赖的头文件。#include <linux/platform_device.h>#define GPHCON 0xE0200C40/*tiny210v2开发板:按键K1->引脚GPH2_0->寄存器0xE0200C40*/#define GPHDAT 0xE0200C44/*定义一个工作指针*/struct work_struct *work1;/*定义一个定时器变量*/struct timer_list key_timer;unsigned int *gpio_data;unsigned int key_num = 0;/*用来存放按键值*///定义等待队列:wait_queue_head_t key_q;/*定义一个变量用来存放从平台驱动对应的平台设备那里取来的中断号*/struct resource *res_irq,*res_mem;unsigned int *key_base;/*定义这项工作要执行的函数*/void work1_fuc(struct work_struct *work){//printk("this is work1->\n");/*启动定时器;*//*jiffies:linux系统当时时间,滴答计时,1秒计1000个*//*HZ/10:一个HZ就是一秒,HZ/10=100ms*//*jiffies+HZ/10:计时时间*/mod_timer(&key_timer,jiffies+HZ/10);}/*定时器的超时函数*/void key_timer_fuc(unsigned long data){unsigned int key_val;//key_val = readl(gpio_data)&0x01;key_val = readl(key_base+1)&0x01;if(key_val == 0){//printk("key1 down ---- timer\n");key_num = 1;}//key_val = readl(gpio_data)&0x02;key_val = readl(key_base+1)&0x02;if(key_val == 0){//printk("key2 down ---- timer\n");key_num = 2;}//有数据的时候将阻塞唤醒:wake_up(&key_q);}/*实现中断处理函数*/irqreturn_t key_int(int irq, void *dev_id){    //1. 检测是否发生了按键中断            //2. 清除已经发生的按键中断            //3. 打印按键值    //printk("key down!\n");    /*提交下半部*//*将这些与硬件无关的代码程序提交给工作,有内核自动创建线程来执行,  这样就极大的减少了中断中执行程序的时间,中断中代码越少越好*///3.挂载(提交)工作:/*这个挂载和提交只是将工作提交到工作队列中,工作并没有运行*//*这个被挂载进队列的工作的运行是由系统内核自动创建的线程来给你运行的*//*至于什么时候运行也是系统自己决定的,你无需管*//*入口参数:工作队列指针;工作指针。指将这个工作挂载到这个工作队列中*///queue_work(my_wq,work1);schedule_work(work1);    //return 0;    return IRQ_HANDLED;}/*按键硬件初始化函数*/void key_hw_init(){    //unsigned int data;     //unsigned int *gpio_config;    /*将物理地址转化为虚拟地址,长度为4个字节*//*将一个IO地址空间映射到内核的虚拟地址空间*//*从起始地址GPHCON开始的4个字节的长度映射为虚拟地址,存放在指针变量gpio_cofig中*/    //gpio_config = ioremap(GPHCON,4);//gpio_data = ioremap(GPHDAT,4);/*将地址(虚拟地址)gpio_config中的值读出来,放在变量data中。*/    //data = readl(gpio_config);    //data &= ~0b11;    //data |= 0b1111;    //writel(data,gpio_config);//writel(0x000000ff,gpio_config);writel(0x000000ff,key_base);//创建工作:/*给指针分配空间*/work1 = kmalloc(sizeof(struct work_struct),GFP_KERNEL);/*对相应的工作进行初始化:*//*INIT_WORK函数的入口参数:要初始化的工作的指针;工作要执行的函数。*/INIT_WORK(work1,work1_fuc);}static int key_open(struct inode *node,struct file *filp){    /*程序调试使用*///printk("in kernel : at key_open fuc!\n");return 0;}/*加上这个函数之后,应用程序才可以读取到驱动程序中的数据*/static ssize_t key_read(struct file *filp, char __user *buf, size_t size, loff_t *pos){    /*程序调试使用*///printk("in kernel :key num is %d\n",key_num);    //如果没有数据可供读,进入等待队列休眠//入口参数:等待队列   条件wait_event(key_q,key_num);/*将驱动程序空间的数据传递到应用程序空间*//*入口参数:应用程序接收数据的地址;驱动程序被传送的数据的地址;传送的数据量*/copy_to_user(buf, &key_num, 4);    key_num = 0;    return 4;}static const struct file_operations key_fops = {    .open = key_open,.read = key_read,};/*初始化混杂设备*/static const struct miscdevice key_miscdev = {    .minor = 250,/*注意这个混杂设备驱动模型的次设备号,如果这个号被占用,那么驱动程序将不能和用户程序通信*/    .name = "s5pv210key",    .fops = &key_fops,};/*定义一个函数,这个函数要被用在平台驱动初始化中*//*当这个驱动找到与之对应的设备之后要调用的函数*//*平台设备驱动只是找到平台总线上与驱动对应的设备或者与设备对应的驱动*//*原则:驱动和设备是要独立开的,硬件信息都是保存在设备里面的*//*在驱动中不保存硬件信息,以增加驱动的可移植性*/int __devinit key_probe(struct platform_device *pdev){int size,ret;/*对按键驱动程序混杂设备的注册*//*注册混杂设备*/ret = misc_register(&key_miscdev);     /*从与该驱动对应的平台设备中拿到中断号*/res_irq = platform_get_resource(pdev,IORESOURCE_IRQ,0);    //注册中断处理程序/*注意:一个设备可以注册多个中断*//*IRQF_TRIGGER_FALLING:下降沿产生中断*//*IRQ_EINT16_31(中断号)查找文件路径: /arch/arm/mach-s5pv210/include/mach/irqs.h 中*/    //request_irq(gpio_to_irq(S5PV210_GPH2(0)),key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);//request_irq(gpio_to_irq(S5PV210_GPH2(1)),key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);    request_irq(res_irq->start,key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);request_irq(res_irq->end,key_int,IRQF_TRIGGER_FALLING,"s5pv210key",0);res_mem = platform_get_resource(pdev,IORESOURCE_MEM,0);size = res_mem->end - res_mem->start +1;key_base = ioremap(res_mem->start,size);//按键硬件初始化    key_hw_init();/*初始化定时器:*/init_timer(&key_timer);/*设置超时函数:*/key_timer.function = key_timer_fuc;/*向内核注册定时器:*/add_timer(&key_timer);//初始化等待队列:init_waitqueue_head(&key_q);return ret;}/*定义一个函数,这个函数要被用在平台函数初始化中*//*当与该驱动对应的设备移除的时候要调用的函数*/static int __devexit key_remove(struct platform_device *pdev){misc_deregister(&key_miscdev);}/*定义一个平台变量*/struct platform_driver key_driver ={.probe = key_probe,.remove = key_remove,.driver = {.name = "my-key",},};/*模块初始化函数*/static int button_init(){/*注册平台驱动*/return platform_driver_register(&key_driver);/*对于平台驱动程序的初始化,一定是要找到设备的时候再做初始化*/}static void button_exit(){    /*卸载平台驱动*/platform_driver_unregister(&key_driver);}MODULE_LICENSE("GPL");module_init(button_init);module_exit(button_exit);


0 0