Platform总线按键驱动分析(精华版)

platform总线按键驱动分析(精华版) 驱动做了三个多星期了,从helloworld到LED再到PlatformLED,现在终于到了按键驱动,对于驱动的理解深刻了不少,从完全看不懂到现在能够独立分析,进步很大,今天完成了按键驱动,做一个总结性的分享,给后面的自己看!加油!按键驱动:与之前不同,这次将按 阅读详情

驱动做了三个多星期了,从helloworld到LED再到PlatformLED,现在终于到了按键驱动,对于驱动的理解深刻了不少,从完全看不懂到现在能够独立分析,进步很大,今天完成了按键驱动,做一个总结性的分享,给后面的自己看!加油!


按键驱动:

与之前不同,这次将按键驱动按照platform总线的理解,设备链表和驱动链表,做成了两个模块,一个kbd_driver.c   一个kbd_device.c    侧重点在理解按键的消抖和中断

中断:我个人理解为你正在看电影,有人叫你出去,你就得暂停出去处理,然后才能回来继续看电影,这就是一个生活中的中断。在开发板上,你也可以带入的理解。


上代码分析代码:

kbd_driver.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <mach/hardware.h>
#include <asm/gpio.h>
#include <asm/irq.h>
#include <linux/slab.h>
#include <mach/regs-gpio.h>

#include "kbd_driver.h"

/* 1HZ=100*jiffies 1*jiffies=10ms => 1HZ=100*10ms = 1s 	这是在设置时钟/
#define CANCEL_DITHERING_DELAY          (HZ/50)   /* Remove button push down dithering timer delay 20ms 	延时 */ 

typedef struct s3c_kbd_s 
{
    struct timer_list           *timers; /* every key get a cancel dithering timer 消抖时间*/
    struct input_dev            *input_dev;
    s3c_kbd_platform_data_t     *pdata;
} s3c_kbd_t;  /*---  end of struct s3c_kbd_s  ---*/


s3c_kbd_t                       *s3c_kbd = NULL;

static irqreturn_t s3c_kbd_intterupt(int irq, void *dev_id) 	//见139行,一旦发生中断后,将中断号传过来
{
    int                         i;
    int                         found = 0;
    struct platform_device      *pdev =  dev_id;
    s3c_kbd_t                   *s3c_kbd = NULL;
    
    s3c_kbd = platform_get_drvdata(pdev);
	
    for(i=0; i<s3c_kbd->pdata->nkeys; i++)	//寻找中断号
    {
        if(irq == s3c_kbd->pdata->keys[i].nIRQ)
        {
            found = 1;
            break;
        }
    }

    if(!found) /*  An ERROR interrupt */
        return IRQ_NONE;

    mod_timer(&s3c_kbd->timers[i], jiffies+CANCEL_DITHERING_DELAY);	//消抖定时器,延时		jiffies是当前时间由内核维护
	//中断的处理涉及到上半部和下半部,上半部进行响应然后离开,下半部例如定时器会在这里执行,提高效率。
    return IRQ_HANDLED;  
}

static void  cancel_dithering_timer_handler(unsigned long data)//消抖定时器处理方式:当62行延时结束后就来到这里调用这个函数,看看是否按键按下,这样就利用timer(定时器)来消抖
{
    int                      which =(int)data;
    unsigned int             pinval;  

    pinval = s3c2410_gpio_getpin(s3c_kbd->pdata->keys[which].gpio);	//获取按键引脚电平

    if( pinval ) 
    {
        //printk("s3c_kbd key[%d] code[%d] released\n", which, s3c_kbd->pdata->keys[which].code);
        input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 0);  
    }
    else
    {
        //printk("s3c_kbd key[%d] code[%d] pressed\n", which, s3c_kbd->pdata->keys[which].code);
        input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 1);  
    }
    
    input_sync(s3c_kbd->input_dev);
}

static int s3c_kbd_probe(struct platform_device *pdev)		//probe()函数传参调用platform_device相应的设备信息,在总线上device和driver name匹配时调用
{
    int                         i = 0;
    int                         rv = -ENOMEM;//还记得后面的if语句吗?这里是负值,如果不能正确执行就返回这个值哦!
    struct input_dev            *input_dev = NULL;
    s3c_kbd_platform_data_t     *pdata = pdev->dev.platform_data;//*pdata指向设备信息	从这里看出,驱动和设备的信息被隔离开

    /* malloc s3c_kbd struct	给按键分配结构体  */
    s3c_kbd = kmalloc(sizeof(s3c_kbd_t), GFP_KERNEL);
    if( !s3c_kbd )
    {
        printk("error: s3c_kbd_probe kmalloc() for s3c_kbd failure\n");
        goto fail;
    }
    memset(s3c_kbd, 0, sizeof(s3c_kbd_t));//void *memset(void *s,int c,size_t n)   总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。内存空间初始化

    /* malloc cancel dithering timer for every key */
    s3c_kbd->timers = (struct timer_list *) kmalloc(pdata->nkeys*sizeof(struct timer_list), GFP_KERNEL);//在linux/gfp.h中定义的一个宏,是分配内核空间的内存时的一个标志位。
    if( !s3c_kbd->timers )
    {
        printk("error: s3c_kbd_probe kmalloc() for s3c_kbd timers failure\n");
        goto fail;
    }
    memset(s3c_kbd->timers, 0, pdata->nkeys*sizeof(struct timer_list));

    /* malloc input_dev for keyboard */
    input_dev=input_allocate_device();//分配结构体,填充设备名等
    if( !input_dev )
    {
        printk("error: s3c_kbd_probe input_allocate_device() failure\n");
        goto fail;
    }

    /* setup input_dev  */
    input_dev->name = pdev->name;
    input_dev->dev.parent = &pdev->dev;
    input_dev->id.bustype = BUS_HOST;
    input_dev->id.vendor = 0x0001;
    input_dev->id.product = 0x0001;
    input_dev->id.version = 0x0100;

    set_bit(EV_KEY,input_dev->evbit);  //extern __inline__ int set_bit(int nr,long * addr);   将addr的第nr位置为1
    set_bit(EV_REP,input_dev->evbit); 

    /* Initialize all the keys and interrupt  初始化所有按键、中断 */
    for(i=0; i<pdata->nkeys; i++)
    {
        set_bit(pdata->keys[i].code, input_dev->keybit);
        s3c2410_gpio_cfgpin(pdata->keys[i].gpio, pdata->keys[i].setting);//将按键设为中断模式 
        irq_set_irq_type(pdata->keys[i].nIRQ, IRQ_TYPE_EDGE_BOTH);/*IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)中断的触发类型:上下沿都触发
																	更多的触发方式参考:http://blog.chinaunix.net/uid-25445243-id-4052877.html	或 http://blog.sina.com.cn/s/blog_640029b30100uw2f.html*/
        
        rv = request_irq(pdata->keys[i].nIRQ/*中断号*/, s3c_kbd_intterupt, IRQF_DISABLED/*表示处理这个中断时禁止其他中断,即这是一个快速中断*/, pdev->name, pdev);//上面设置完中断后这里申请(安装)中断,一旦有中断发生,就调用这个中断程序(也叫中断服务处理程序)
        if( rv )
        {
            printk("error: request IRQ[%d] for key<%d> failure\n", pdata->keys[i].nIRQ, i);
            rv = -EBUSY;
            goto fail;
        }
        
        //printk("s3c_kbd request IRQ[%d] for key<%d> ok\n", pdata->keys[i].nIRQ, i);

         /* Initialize all the keys cancel dithering timer 	初始化所有按键消抖定时器*/
        setup_timer(&s3c_kbd->timers[i], cancel_dithering_timer_handler, i);
    }

    /* register input device 在内核中注册设备 */
    rv = input_register_device(input_dev);
    if( rv )
    {
        printk("error: s3c_kbd_probe input_register_device error!\n");
        goto fail;
    }

    /* set s3c_kbd as private data in pdev */
    s3c_kbd->input_dev = input_dev;
    s3c_kbd->pdata = pdata;
    platform_set_drvdata(pdev, s3c_kbd);

    printk("s3c_kbd_probe ok\n");
    return 0;

fail:
    while(i--)
    {
        disable_irq(pdata->keys[i].nIRQ);
        free_irq(pdata->keys[i].nIRQ, pdev);
        del_timer( &s3c_kbd->timers[i] );
    }

    if(input_dev)
    {
        input_free_device(input_dev);
    }

    if(s3c_kbd && s3c_kbd->timers)
    {
        kfree(s3c_kbd->timers);
    }

    if(s3c_kbd)
    {
        kfree(s3c_kbd);
    }
    printk("s3c_kbd_probe failed\n");

    return -ENODEV;
}

static int s3c_kbd_remove(struct platform_device *pdev)
{
    int                         i = 0;
    s3c_kbd_t                   *s3c_kbd = platform_get_drvdata(pdev);

    for(i=0; i<s3c_kbd->pdata->nkeys; i++)
    {
        del_timer( &s3c_kbd->timers[i] );
        disable_irq(s3c_kbd->pdata->keys[i].nIRQ);
        free_irq(s3c_kbd->pdata->keys[i].nIRQ, pdev);
    }

    input_unregister_device(s3c_kbd->input_dev);

    kfree(s3c_kbd->timers);
    kfree(s3c_kbd);

    printk("s3c_kbd_remove ok\n");

    return 0;
}

static struct platform_driver s3c_keyboard_driver = {		//总线按键驱动结构体
     .probe      = s3c_kbd_probe,//probe中文译为探测,一旦发现相应设备就调用这个函数
     .remove     = s3c_kbd_remove,//一旦设备移除就调用该函数
     .driver     = {
         .name       = "s3c_kbd",		//要和device  一致
         .owner      = THIS_MODULE,
     },
};

static int __init s3c_keyboard_drv_init(void)
{
    int            rv;

    rv = platform_driver_register(&s3c_keyboard_driver);//在驱动链表上注册
    if(rv)
    {
        printk("s3c keyboard platform driver register failure\n");
        return rv;
    }

    printk("s3c keyboard platform driver register ok\n");
    return 0;
}

static void __exit s3c_keyboard_drv_exit(void)
{
    printk("s3c keyboard driver exit\n");

    platform_driver_unregister(&s3c_keyboard_driver);
    return ;
}

module_init(s3c_keyboard_drv_init);
module_exit(s3c_keyboard_drv_exit);

MODULE_DESCRIPTION("FL2440 board keyboard input driver platform_driver");
MODULE_AUTHOR("fanmaolin");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:FL2440 keyboard driver");


kbd_device.c
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <mach/hardware.h>
#include <asm/gpio.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include "kbd_driver.h"

/*按键硬件信息*/
static s3c_kbd_info_t  s3c_kbd_gpios[] = {
    [0] = {
        .code = KEY_1,
        .nIRQ = IRQ_EINT0,// irq是interrupt request 的缩写,中断请求,这里应该是该按键的中断请求号
        .gpio = S3C2410_GPF(0),//根据原理图设置
        .setting = S3C2410_GPF0_EINT0,//中断模式
    },
    [1] = {
        .code = KEY_2,
        .nIRQ = IRQ_EINT2,
        .gpio = S3C2410_GPF(2),
        .setting = S3C2410_GPF2_EINT2,
    },
    [2] = {
        .code = KEY_3,
        .nIRQ = IRQ_EINT3,
        .gpio = S3C2410_GPF(3),
        .setting = S3C2410_GPF3_EINT3,
    },
    [3] = {
        .code = KEY_4,
        .nIRQ = IRQ_EINT4,
        .gpio = S3C2410_GPF(4),
        .setting = S3C2410_GPF4_EINT4,
    },
};

/* keyboard platform device private data */
static s3c_kbd_platform_data_t s3c_kbd_data = {
    .keys = s3c_kbd_gpios,//按键硬件信息
    .nkeys = ARRAY_SIZE(s3c_kbd_gpios),/*ARRAY_SIZE函数用来求s3c_kbd_gpios[]这个数组有多少个元素,用它来自动获取设备数,
	该函数的宏定义为:#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 	sizeof(x)是求该结构体的大小然后除以sizeof(x[0])第一个的大小		就能得到该数组有多少个元素*/
};

static void platform_kbd_release(struct device * dev)//如果在驱动卸载(rmmod)时依然想做其他事就把代码放到这里,我们现在什么都不干,一般为空函数
{
        return;
}

static struct platform_device s3c_keyboard_device = {
    .name    = "s3c_kbd",//设备名,通过这个在总线上找到driver
    .id      = 1,//一般情况下id都为1
    .dev     =
    {//platform_data成员用来存放设备的硬件信息
        .platform_data = &s3c_kbd_data,//在Platform总线上找到彼此后由设备告诉驱动硬件信息,见53行
        .release = platform_kbd_release,//在rmmod时要用到,见58行定义
    },
};


static int __init s3c_keyboard_dev_init(void)
{
    int            rv;

    rv = platform_device_register(&s3c_keyboard_device);//在platform上注册设备,调用这个函数时相当于告诉cpu这个设备已经插上
    if(rv)//http://c.biancheng.net/cpp/html/38.html	if函数的用法参考,在这里当注册成功时rv为error,深入内核看
platform_device_register可以发现,所以当注册失败时会返回真值,执行if语句
{ printk("S3C keyboard platform device register failure\n"); return rv; } printk("S3C keyboard platform device register ok\n"); return 0;}static void __exit s3c_keyboard_dev_exit(void){ printk("S3C keyboard device exit\n"); platform_device_unregister(&s3c_keyboard_device); return ;}module_init(s3c_keyboard_dev_init);module_exit(s3c_keyboard_dev_exit);MODULE_DESCRIPTION("FL2440 board keyboard input driver platform_device");MODULE_AUTHOR("fanmaolin");MODULE_LICENSE("GPL");MODULE_ALIAS("platform:FL2440 keyboard device");


kbd_driver.h
#ifndef  _KBD_DRIVER_H_
#define  _KBD_DRIVER_H_

/* keyboard hardware informtation structure definition */
typedef struct s3c_kbd_info_s
{
    int                     code;      /* input device key code  */
    int                     nIRQ;      /* keyboard IRQ number*/
    unsigned int            setting;   /* keyboard IRQ Pin Setting*/
    unsigned int            gpio;      /* keyboard GPIO port */
} s3c_kbd_info_t;

/* keyboard platform device private data structure */
typedef struct s3c_kbd_platform_data_s
{
    s3c_kbd_info_t         *keys;
    int                    nkeys;
} s3c_kbd_platform_data_t;

#endif   /* ----- #ifndef _KBD_DRIVER_H_  ----- */


下面是带你飞的干货时段:

之前在platform_led中分析过platform总线把设备和驱动放在一个双向链表上,设备链表和驱动链表,他们在链表上找到彼此,然后绑定。

先来分析kbd_device.c

看一段驱动代码,要先从module_init下手,就是几乎在最后边的那个,向上找到s3c_keyboard_dev_init,首先rv = platform_device_register(&s3c_keyboard_device);,在驱动链表上注册设备,(可以理解为设备告诉总线我来了,赶紧让驱动过来得意),你会看到他调用了s3c_keyboard_device,向上找到它,

static struct platform_device s3c_keyboard_device = {
    .name    = "s3c_kbd",//设备名,通过这个在总线上找到driver
    .id      = 1,//一般情况下id都为1
    .dev     =
    {//platform_data成员用来存放设备的硬件信息
        .platform_data = &s3c_kbd_data,//在Platform总线上找到彼此后由设备告诉驱动硬件信息,见53行
        .release = platform_kbd_release,//在rmmod时要用到,见58行定义
    },
};

嗯,这一段我已经注释的很清楚了

然后我们来看一下s3c_kbd_data,

static s3c_kbd_platform_data_t s3c_kbd_data = {
    .keys = s3c_kbd_gpios,//按键硬件信息
    .nkeys = ARRAY_SIZE(s3c_kbd_gpios),/*ARRAY_SIZE函数用来求s3c_kbd_gpios[]这个数组有多少个元素,用它来自动获取设备数,
	该函数的宏定义为:#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) 	sizeof(x)是求该结构体的大小然后除以sizeof(x[0])第一个的大小		就能得到该数组有多少个元素*/
};
注意ARRAY_SIZE函数

还有s3c_kbd_gpios,一大串

而platform_kbd_release,我们在这里啥都不干

然后呢,就是退出时调用module_exit


下面来看kbd_driver.c

还是先从module_init入手去看s3c_keyboard_drv_init,再去看s3c_keyboard_driver

static struct platform_driver s3c_keyboard_driver = {		//总线按键驱动结构体
     .probe      = s3c_kbd_probe,//probe中文译为探测,一旦发现相应设备就调用这个函数
     .remove     = s3c_kbd_remove,//一旦设备移除就调用该函数
     .driver     = {
         .name       = "s3c_kbd",		//要和device  一致
         .owner      = THIS_MODULE,
     },
};
对比之前device的代码,你会发现.name是一样的,你说他们是怎么在总线上找到彼此呢?就是通过这个哦!害羞找到之后要干点啥呢!!!当然不会啥都不干啦!

注意probe函数

static int s3c_kbd_probe(struct platform_device *pdev)。。。。
一直到
 if(s3c_kbd)
    {
        kfree(s3c_kbd);
    }
    printk("s3c_kbd_probe failed\n");


    return -ENODEV;
}

搞了一个空的struct input_dev一会要分配,s3c_kbd_platform_data_t     *pdata = pdev->dev.platform_data;//*pdata指向设备信息从这里看出,驱动和设备的信息被隔离开

你还记得kbd_device里的platform_data吗?接下来就是分配内核空间、填充信息一类的事情了

注意这一段,这就是按键驱动与led驱动最大的不同点,中断,消抖(因为硬件的因素,不可避免出现抖动,程序要避免这种bug采用定时器,这里面有很多概念的引入)

 /* Initialize all the keys and interrupt  初始化所有按键、中断 */
    for(i=0; i<pdata->nkeys; i++)
    {
        set_bit(pdata->keys[i].code, input_dev->keybit);
        s3c2410_gpio_cfgpin(pdata->keys[i].gpio, pdata->keys[i].setting);//将按键设为中断模式 
        irq_set_irq_type(pdata->keys[i].nIRQ, IRQ_TYPE_EDGE_BOTH);/*IRQ_TYPE_EDGE_BOTH (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)中断的触发类型:上下沿都触发
																	更多的触发方式参考:http://blog.chinaunix.net/uid-25445243-id-4052877.html	或 http://blog.sina.com.cn/s/blog_640029b30100uw2f.html*/
        
        rv = request_irq(pdata->keys[i].nIRQ/*中断号*/, s3c_kbd_intterupt/*中断名字*/, IRQF_DISABLED/*表示处理这个中断时禁止其他中断,即这是一个快速中断*/, pdev->name, pdev);//上面设置完中断后这里申请(安装)中断,一旦有中断发生,就调用这个中断程序(也叫中断服务处理程序)
        if( rv )
        {
            printk("error: request IRQ[%d] for key<%d> failure\n", pdata->keys[i].nIRQ, i);
            rv = -EBUSY;
            goto fail;
        }
        
        //printk("s3c_kbd request IRQ[%d] for key<%d> ok\n", pdata->keys[i].nIRQ, i);

         /* Initialize all the keys cancel dithering timer 	初始化所有按键消抖定时器*/
        setup_timer(&s3c_kbd->timers[i], cancel_dithering_timer_handler, i);
    }

这句话很重要:

  rv = request_irq(pdata->keys[i].nIRQ/*中断号*/, s3c_kbd_intterupt/*中断名字*/, IRQF_DISABLED/*表示处理这个中断时禁止其他中断,即这是一个快速中断*/, pdev->name, pdev);//上面设置完中断后这里申请(安装)中断,一旦有中断发生,就调用这个中断程序(也叫中断服务处理程序)


看cancel_dithering_timer_handler

static void  cancel_dithering_timer_handler(unsigned long data)//消抖定时器处理方式:当62行延时结束后就来到这里调用这个函数,看看是否按键按下,这样就利用timer(定时器)来消抖
{
    int                      which =(int)data;
    unsigned int             pinval;  

    pinval = s3c2410_gpio_getpin(s3c_kbd->pdata->keys[which].gpio);	//获取按键引脚电平

    if( pinval ) 
    {
        //printk("s3c_kbd key[%d] code[%d] released\n", which, s3c_kbd->pdata->keys[which].code);
        input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 0);  
    }
    else
    {
        //printk("s3c_kbd key[%d] code[%d] pressed\n", which, s3c_kbd->pdata->keys[which].code);
        input_event(s3c_kbd->input_dev, EV_KEY, s3c_kbd->pdata->keys[which].code, 1);  
    }
    
    input_sync(s3c_kbd->input_dev);
}



看 s3c_kbd_intterupt

static irqreturn_t s3c_kbd_intterupt(int irq, void *dev_id) 	//见139行,一旦发生中断后,将中断号传过来
{
    int                         i;
    int                         found = 0;
    struct platform_device      *pdev =  dev_id;
    s3c_kbd_t                   *s3c_kbd = NULL;
    
    s3c_kbd = platform_get_drvdata(pdev);
	
    for(i=0; i<s3c_kbd->pdata->nkeys; i++)	//寻找中断号
    {
        if(irq == s3c_kbd->pdata->keys[i].nIRQ)
        {
            found = 1;
            break;
        }
    }

    if(!found) /*  An ERROR interrupt */
        return IRQ_NONE;

    mod_timer(&s3c_kbd->timers[i], jiffies+CANCEL_DITHERING_DELAY);	//消抖定时器,延时		jiffies是当前时间由内核维护
	//中断的处理涉及到上半部和下半部,上半部进行响应然后离开,下半部例如定时器会在这里执行,提高效率。
    return IRQ_HANDLED;  
}


再然后就是。。。module_exit


分析完毕,补充几个概念。


中断的上半部和下半部:

上半部的功能是响应中断。当中断发生时,它就把设备驱动程序中中断处理例程的下半部挂到设备的下半部执行队列中去,然后继续等待新的中断到来。这样一来,上半部的执行速度就会很快,它就可以接受更多它负责的设备所产生的中断了。上半部之所以快,是因为它是完全屏蔽中断的,如果它没有执行完,其他中断就不能及时地处理,只能等到这个中断处理程序执行完毕以后。所以要尽可能多的对设备产生的中断进行服务和处理,中断处理程序就一定要快。

    下半部的功能是处理比较复杂的过程。下半部和上半部最大的区别是可中断,而上半部却不可中断。下半部几乎完成了中断处理程序所有的事情,因为上半部只是将下半部排到了它们所负责的设备中断的处理队列中去,然后就不做其它的处理了。下半部所负责的工作一般是查看设备以获得产生中断的事件信息,并根据这些信息(一般通过读设备上的寄存器得来)进行相应的处理。下半部是可中断的,所以在运行期间,如果其它设备产生了中断,这个下半部可以暂时的中断掉,等到那个设备的上半部运行完了,再回头运行这个下半部。

参见http://blog.youkuaiyun.com/newtonnl/article/details/46004451



request_irq

:在linux内核中用于申请中断的函数是request_irq(),详情参见http://blog.chinaunix.net/uid-25445243-id-4052877.html


消抖:通过timer(定时器)处理,定时器就在下半部,比如延时50ms,这个时间要根据不同的硬件测试才能获得。


基于platform总线的驱动分析 在设备驱动模型中,总线负责将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 阅读详情

相关推荐

platform总线

1、什么是platform总线? 相对于USB、PCI、I2C、SPI等物理总线来说,platform总线是一种虚拟、抽象出来的总线,实际中并不存在这样的总线。那为什么需要platform总线呢?其实是Linux设备驱动模型为了保持设备驱动的统一性而虚拟出来的总线。因为对于usb设备、i2c设备、pci设备、spi设备等等,他们与cpu的通信都是直接挂在相应的总线下面与我们的cpu进行数据交互的,...

2522

EB配置MCAL的ICU模块

基于S32K312的EB配置MCAL的Icu模块开发,利用边缘检测功能开启下降沿触发中断函数进行LED灯的亮灭

qq_56406511的博客 3299

Linux platform总线

Linux platform总线 文章目录Linux platform总线一、总线概念二、platform驱动框架详解2.1 platform驱动框架2.2 platform_device和platform_driver的匹配规则2.3 platform_device中的资源描述三、platform驱动框架源码剖析四、platform驱动程序模板 一、总线概念 在Linux驱动模型中总要关心三个对...

JT同学的博客 1715

S32K3学习笔记---S32K3之ICU模块

本文主要记录输入捕获测量信号周期、频率、占空比

Abler_xaing的博客 6281

Linux Platform总线+SPI总线分析

本文以MPC8308(powerpc架构),HX软件包为依据,详细内容可参考源码 CPU: e300c3MPC8308 400MHz BOARD: Freescale MPC8308ERDB DRAM: 256M NAND: 1024M一、SPI原理模型如上图所示,主设备对应SOC芯片中的SPI控制器,通常,一个SOC中可能存在多个SPI控制器,像上面的例子所示,SOC芯片中有3个

Let the development board fly! 803

HFSS实战(二)——精准仿真高速PCB差分线的信号完整性

本文深入探讨了使用HFSS进行高速PCB差分线信号完整性仿真的关键技术与实战经验。从设计文件导入、模型裁剪到差分端口设置,详细解析了精准仿真的全流程,特别强调了材料特性和3D电磁场仿真在解决微带线损耗等问题中的重要性。文章还提供了常见问题排查指南和效率优化技巧,帮助工程师提升高速PCB设计的仿真精度与效率。

weixin_38170829的博客 574

platfor设备驱动(1)-什么是platform设备驱动?platform设备驱动基础总结

Linux系统内核使用bus_type结构体表示总线,该结构体定义在文其中的match函数用于总线完成设备和驱动之间匹配 dev为设备,drv为驱动,所以每一条总线都必须实现这个函数。platform总线是bus_type的一个示例,在其中使用进行驱动和设备之间的匹配。return!return 1;return 1;= NULL;:设备树的匹配方式,通过compatible属性在OF表匹配,匹配后执行probe函数。

嵌入式Linux开发从业者,立志做一名优秀的博客输出者! 1937

从零到一:YOLOv11高帧率模型在RDK X5上的ONNX转换与部署实战

本文详细介绍了将最新的YOLOv11高帧率模型部署到地平线RDK X5开发板的完整实战流程。内容涵盖从PyTorch模型导出ONNX、地平线工具链环境配置与模型量化编译,到编写高效的板端推理代码,并针对高帧率目标进行了后处理优化。通过实战避坑指南,帮助开发者在RDK X5上实现30FPS以上的实时目标检测。

weixin_29089057的博客 139

Linux驱动-platform驱动模型

Linux-驱动开发-platform驱动模型

weixin_47491758的博客 1096

华为鸿蒙系统也能用!5分钟搞定谷歌三件套安装(附最新APK下载链接)

本文为华为鸿蒙系统用户提供了一份详尽的谷歌三件套(Google Play服务、商店及框架)手动安装指南。文章核心在于无需Root,通过ADB命令获取设备信息,并从可靠来源下载匹配版本的APK,按照正确顺序安装,从而解决海外应用依赖问题,实现5分钟快速部署。

c7d8e9的博客 6513

Linux系统之Platform设备驱动

当我们把platform驱动注册到platform总线时,platform总线会根据platform驱动的参数,查找与之匹配的设备。当存在与驱动匹配的设备时,总线将会把设备和驱动绑定。其中resource结构体就是用于登记相关的硬件资源信息,如资源的类型、资源的地址空间等等,这里将不展开叙述,有兴趣的朋友可以查阅注册platform设备的相关代码。当我们的platform驱动注册后,platform总线将驱动与总线上的设备进行匹配,当匹配属性一致时,驱动将与该设备进行绑定,并执行。

userpid的博客 1851

PPT+视频 | 详解智能营销增益模型(uplift model)

分享营销增益模型的原理和常见的建模及评估方法,并以淘票票智能票补为例介绍uplift model技术的实践经验。

阿里文娱技术 8626

Linux驱动中的platform总线分析

最近在复习platform总线相关的知识,碰到一篇感觉不错的文章。转载自:http://blog.youkuaiyun.com/pillarbuaa/article/details/7680372概述从Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_devi

樊茂林的博客 6905

《UDS协议从入门到精通》系列——图解0x85:控制DTC的设置

如何让ECU停止或恢复DTC状态位更新?有哪些注意点?图解+示例教你快速上手UDS 0x85服务!!

车载系统攻城狮 3863

prplOS适配USB LAN网卡

本文介绍了将USB LAN网卡(如rndis0)适配到prplOS的LAN管理流程。

四儿家的小祖宗的博客 322

基于platform的总线、设备、驱动模型分析

如I2C总线,I2C总线是集成在SOC内部的,因此总线是跟着平台走到,不同芯片厂商,所使用的I2C控制器不同,引起其驱动也有所不同,但又一个问题,如果不同芯片厂商与相同的I2C设备进行通讯,使这些外围的I2C设备工作起来,如果不对其解耦,那么每一个SOC平台都需要写一个对应的I2C设备驱动,这样耦合性过高,不方便移植。由 3.4 可知 module_init最终会注册一个设备到对应总线的总线设备链表中,再调用platform_driver_register时,即在总线中找到对应的设备,匹配成功。

jamecer的博客 1158

2022年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题5)-网络部分解析-附详细代码

2022年全国职业院校技能大赛网络系统管理赛项模块A:网络构建(样题5)-网络部分解析-附详细代码

bfq05234214的博客 416

Linux驱动编程 - platform平台设备驱动总线

Linux系统中的platform平台设备驱动

hinewcc的博客 1719

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【Linux】Linux驱动开发:platform总线驱动

/定义在linux/module.h中使用时,参数如下:of:总线类型match_table:idtable数组首地址。

W__winter的博客 2758

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

十一、Linux驱动之platform总线设备驱动

1. 基本概念     从Linux2.6开始Linux加入了一套驱动管理和注册机制—platform平台总线驱动模型。platform平台总线是一条虚拟总线,platform_device为相应的设备,platform_driver为相应的驱动。与传统的bus/device/driver机制相比,platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的...

墨、白的博客 2548

嵌入式驱动学习第四周——platform总线

外设与Soc的连接是通过接口,但是接口的不同本质上是通信协议的不同,于是内核就抽象出各种总线,将同一协议的设备连接在一起管理。总线分为物理总线和虚拟总线,物理总线是客观存在的,比如usb总线,有usb协议的设备连接在上面;虚拟总线式软件上模拟的,如platform总线,没有哪种设备用的platform协议,是内核为了统一用总线上方式管理设备虚拟出来的总线。使用总线的话,可以实现数据和方法分离,设备里包含了数据,驱动是硬件操作的方法。

qq_43419761的博客 1207

Linux 驱动之 platform 总线

1、platform 简介 2、platform 框架 2.1、platform 总线 2.2、platform 驱动 platform 驱动用结构体 platform_driver 来表示,该结构体内容为: /* include/linux/platform_device.h */ struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct plat...

王小波的猪的博客 1662

驱动开发之platform总线

此结构体定义在文件 include/linux/device.h/* 总线名字 *//* 总线属性 *//* 设备属性 *//* 驱动属性 */match 函数,就是完成设备和驱动之间匹配的,总线就是使用 match 函数来根据注册的设备来查找对应的驱 动,或者根据注册的驱动来查找相应的设备,因此每一条总线都必须实现此函数match 函数有 两个参数:dev 和 drv,这两个参数分别为 device 和 device_driver 类型,也就是设备和驱动。

m0_46392035的博客 1842

[linux 驱动]platform总线设备驱动详解与实战

device_driver 结构体(表示 设备驱动)中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表, 设备树中的每个设备节点的 compatible 属性会和 of_match_table 表中的所有成员比较,查看是 否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数 就会执行。platform_device 结构体包含了设备的相关信息,如设备的 ID、资源(内存、IO 地址等)、设备名称和设备状态等。

weixin_49406449的博客 2010

Linux —— 驱动——platform平台总线

adcconadcdat0clkcon:这些是指向硬件寄存器的指针,用于控制ADC和时钟。wq:等待队列头,用于在ADC转换完成时唤醒等待的线程。condition:一个标志,用于指示ADC转换是否完成。arg:传递给中断处理程序的参数(在这个例子中,实际上并未在中断处理函数中使用)。

m0_71703182的博客 1874
上一篇: windows10 如何给应用程序设置快捷键及一些常用好玩快捷键
下一篇: plat_button驱动测试程序及select()函数、阻塞与非阻塞
fml1997
博客等级 码龄10年 30粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值