Linux驱动中的platform总线分析

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

最近在复习platform总线相关的知识,碰到一篇感觉不错的文章。

http://blog.youkuaiyun.com/pillarbuaa/article/details/7680372

概述

从Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device 和 platform_driver 。Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用 platform_driver 进行注册。

linux_platform_driver 机制和传统的device_driver机制(即:通过 driver_register 函数进行注册)相比,一个十分明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序中用使用这些资源时,通过platform device提供的标准接口进行申请并使用。

platform 是一个虚拟的地址总线,相比 PCI、USB,它主要用于描述SOC上的片上资源。platform 所描述的资源有一个共同点:在CPU 的总线上直接取址。平台设备会分到一个名称(用在驱动绑定中)以及一系列诸如地址和中断请求号(IRQ)之类的资源。

platform 总线下驱动的开发步骤是:

1、 设备

需要实现的结构体是:platform_device 。

1)初始化 resource 结构变量

2)初始化 platform_device 结构变量

3)向系统注册设备:platform_device_register。

以上三步,必须在设备驱动加载前完成,即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。platform_driver_register()中添加device到内核最终还是调用的device_add函数。Platform_device_add和device_add最主要的区别是多了一步insert_resource(p, r),即将platform资源(resource)添加进内核,由内核统一管理。

2、驱动

驱动注册中,需要实现的结构体是:platform_driver 。

在驱动程序的初始化函数中,调用了platform_driver_register()注册 platform_driver 。需要注意的是:platform_driver 和 platform_device 中的 name 变量的值必须是相同的 。这样在 platform_driver_register() 注册时,会将当前注册的 platform_driver 中的 name 变量的值和已注册的所有 platform_device 中的 name 变量的值进行比较,只有找到具有相同名称的 platform_device 才能注册成功。当注册成功时,会调用 platform_driver 结构元素 probe 函数指针。

platform_driver_register()的注册过程:


    1 platform_driver_register(&s3c2410fb_driver)

    2 driver_register(&drv->driver)

    3 bus_add_driver(drv)

    4 driver_attach(drv)

    5 bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)

    6 __driver_attach(struct device * dev, void * data)

    7 driver_probe_device(drv, dev)

    8 really_probe(dev, drv)

    在really_probe()中:为设备指派管理该设备的驱动:dev->driver = drv, 调用probe()函数初始化设备:drv->probe(dev)

platform_device_系列函数及其设备注册的作用

http://blog.youkuaiyun.com/maopig/article/details/7410055
platform_device_系列函数,实际上是注册了一个叫platform的虚拟总线。使用约定是如果一个不属于任何总线的设备,例如蓝牙,串口等设备,都需要挂在这个虚拟总线上。

driver/base/platform.c

//platform设备声明
struct device platform_bus = {
    .bus_id        = "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);

//platform总线设备声明
struct bus_type platform_bus_type = {
    .name        = "platform",
    .dev_attrs    = platform_dev_attrs,
    .match        = platform_match,
    .uevent        = platform_uevent,
    .suspend    = platform_suspend,
    .suspend_late    = platform_suspend_late,
    .resume_early    = platform_resume_early,
    .resume        = platform_resume,
};
EXPORT_SYMBOL_GPL(platform_bus_type);

int __init platform_bus_init(void)
{
    int error;

    error = device_register(&platform_bus);//注册了"platform"的设备
    if (error)
        return error;
    error = bus_register(&platform_bus_type);//注册了叫"platform"的总线
    if (error)
        device_unregister(&platform_bus);
    return error;
}


//这里在platform总线上挂设备
int platform_device_add(struct platform_device *pdev)
{
    int i, ret = 0;

    if (!pdev)
        return -EINVAL;

    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;//父设备设置为platform_bus

    pdev->dev.bus = &platform_bus_type;//设置挂在platform总线上

    if (pdev->id != -1)
        snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
             pdev->id);
    else
        strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

    for (i = 0; i < pdev->num_resources; i++) {
        struct resource *p, *r = &pdev->resource[i];

        if (r->name == NULL)
            r->name = pdev->dev.bus_id;

        p = r->parent;
        if (!p) {
            if (r->flags & IORESOURCE_MEM)
                p = &iomem_resource;
            else if (r->flags & IORESOURCE_IO)
                p = &ioport_resource;
        }

        if (p && insert_resource(p, r)) {
            printk(KERN_ERR
                   "%s: failed to claim resource %d\n",
                   pdev->dev.bus_id, i);
            ret = -EBUSY;
            goto failed;
        }
    }

    pr_debug("Registering platform device '%s'. Parent at %s\n",
         pdev->dev.bus_id, pdev->dev.parent->bus_id);

    ret = device_add(&pdev->dev);
    if (ret == 0)
        return ret;

failed:
    while (--i >= 0)
        if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
            release_resource(&pdev->resource[i]);
    return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);


//常用的platform_device_register,内部调用了platform_device_add,将设备挂在了platform总线上
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*/
int platform_device_register(struct platform_device *pdev)
{
    device_initialize(&pdev->dev);
    return platform_device_add(pdev);
}
EXPORT_SYMBOL_GPL(platform_device_register);

要用注册一个platform驱动的步骤:

1,注册设备platform_device_register
2,注册驱动platform_driver_register
注册时候的两个名字必须一样,才能match上,才能work,例如:

struct platform_device pxa3xx_device_nand = {
    .name        = "pxa3xx-nand",
    .id        = -1,
    .dev        = {
        .dma_mask = &pxa3xx_nand_dma_mask,
        .coherent_dma_mask = DMA_BIT_MASK(32),
    },
    .resource    = pxa3xx_resource_nand,
    .num_resources    = ARRAY_SIZE(pxa3xx_resource_nand),
};

static struct platform_driver pxa3xx_nand_driver = {
    .driver = {
        .name    = "pxa3xx-nand",
    },
    .probe        = pxa3xx_nand_probe,
    .remove        = pxa3xx_nand_remove,
#ifdef CONFIG_PM
    .suspend    = pxa3xx_nand_suspend,
    .resume        = pxa3xx_nand_resume,
#endif
};

而且device注册的时候,可以给driver传参数

struct device {
    struct klist        klist_children;
    struct klist_node    knode_parent;    /* node in sibling list */
    struct klist_node    knode_driver;
    struct klist_node    knode_bus;
    struct device        *parent;

    struct kobject kobj;
    char    bus_id[BUS_ID_SIZE];    /* position on parent bus */
    struct device_type    *type;
    unsigned        is_registered:1;
    unsigned        uevent_suppress:1;

    struct semaphore    sem;    /* semaphore to synchronize calls to
                     * its driver.
                     */

    struct bus_type    *bus;        /* type of bus device is on */
    struct device_driver *driver;    /* which driver has allocated this
                       device */
    void        *driver_data;    /* data private to the driver */
    void        *platform_data;    /* Platform specific data, device
                       core doesn't touch it */
    struct dev_pm_info    power;

#ifdef CONFIG_NUMA
    int        numa_node;    /* NUMA node this device is close to */
#endif
    u64        *dma_mask;    /* dma mask (if dma'able device) */
    u64        coherent_dma_mask;/* Like dma_mask, but for
                         alloc_coherent mappings as
                         not all hardware supports
                         64 bit addresses for consistent
                         allocations such descriptors. */

    struct device_dma_parameters *dma_parms;

    struct list_head    dma_pools;    /* dma pools (if dma'ble) */

    struct dma_coherent_mem    *dma_mem; /* internal for coherent mem
                         override */
    /* arch specific additions */
    struct dev_archdata    archdata;

    spinlock_t        devres_lock;
    struct list_head    devres_head;

    /* class_device migration path */
    struct list_head    node;
    struct class        *class;
    dev_t            devt;    /* dev_t, creates the sysfs "dev" */
    struct attribute_group    **groups;    /* optional groups */

    void    (*release)(struct device *dev);
};
传参数都是通过platform_data传,所以定义为void *
    void        *platform_data;    /* Platform specific data, device




static struct pxa3xx_nand_platform_data XXX_nand_info = {
    .parts            = android_256m_v75_partitions,
    .nr_parts        = ARRAY_SIZE(android_256m_v75_partitions),
};

static void __init XXX_init_nand(void)
{
    pxa3xx_device_nand.dev.platform_data = &XXX_nand_info;
    platform_device_register(&pxa3xx_device_nand);
}




static int __init pxa3xx_nand_probe(struct platform_device *pdev)
{
    struct pxa3xx_nand_platform_data *pdata;
    struct nand_chip *this;
    struct pxa3xx_nand_info *info;
    struct resource *res;
    struct clk *clk = NULL, *smc_clk = NULL;
    int status = -1;
    struct mtd_partition *parts;
    unsigned int data_buf_len;
#ifdef CONFIG_MTD_NAND_PXA3xx_DMA
    unsigned int buf_len;
#endif
    int i, ret = 0;
#ifdef CONFIG_MTD_PARTITIONS
    int err;
#endif

    pdata = pdev->dev.platform_data;
....
....
....
}

下面解释一下pxa_register_device函数

    pxa_set_ohci_info(&XXX_ohci_info);

void __init pxa_set_ohci_info(struct pxaohci_platform_data *info)
{
    pxa_register_device(&pxa27x_device_ohci, info);
}

void __init pxa_register_device(struct platform_device *dev, void *data)
{
    int ret;

    dev->dev.platform_data = data;

    ret = platform_device_register(dev);
    if (ret)
        dev_err(&dev->dev, "unable to register device: %d\n", ret);
}

其实上,也就是给driver传参数,通过dev.platform_data。

到这里,platform_device系列函数,基本算通了,系列函数还有一堆设置的函数,和device_register同级别的那些功能函数,用法基本差不多,只不过都将设备挂在了platform总线上。

platform_device_register向系统注册设备

platform_driver_register向系统注册驱动,过程中在系统寻找注册的设备(根据.name),找到后运行.probe进行初始化。

**************************************************************

device_register()和platform_device_register()的区别(转载)  


首先看device和platform_device区别
由struct platform_device {
const char * name; //NOTE:此处设备的命名应和相应驱动程序命名一致
u32 id;            //以实现driver binding
struct device dev;
u32 num_resources;
struct resource * resource;
};
可知:platform_device由device派生而来

内核中关于Platform devices的注释
Platform devices
~~~~~~~~~~~~~~~~
Platform devices are devices that typically appear as autonomous
entities in the system. This includes legacy port-based devices and
host bridges to peripheral buses, and most controllers integrated
into system-on-chip platforms. What they usually have in common
is direct addressing from a CPU bus. Rarely, a platform_device will
be connected through a segment of some other kind of bus; but its
registers will still be directly addressable.

Platform devices are given a name, used in driver binding, and a
list of resources such as addresses and IRQs.
个人理解:Platform devices是相对独立的设备,拥有各自独自的资源(addresses and IRQs)

一个Platform devices实例:

static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb, //片上的各个设备
&s3c_device_lcd, //下面以s3c_device_lcd为例
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};

/* LCD Controller */
static struct resource s3c_lcd_resource[] = { //LCD的两个资源
[0] = {
.start = S3C2410_PA_LCD,
.end = S3C2410_PA_LCD + S3C2410_SZ_LCD,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}

};

struct platform_device s3c_device_lcd = {//s3c_device_lcd设备
.name = "s3c2410-lcd",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource = s3c_lcd_resource,
.dev = { //device实例
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};

s3c_device_lcd的resource中硬件地址:

#define S3C2410_LCDREG(x) ((x) + S3C2410_VA_LCD)

/* LCD control registers */
#define S3C2410_LCDCON1 S3C2410_LCDREG(0x00)
#define S3C2410_LCDCON2 S3C2410_LCDREG(0x04)
#define S3C2410_LCDCON3 S3C2410_LCDREG(0x08)
#define S3C2410_LCDCON4 S3C2410_LCDREG(0x0C)
#define S3C2410_LCDCON5 S3C2410_LCDREG(0x10)

#define S3C2410_LCDCON1_CLKVAL(x) ((x) << 8)
#define S3C2410_LCDCON1_MMODE (1<<7)
#define S3C2410_LCDCON1_DSCAN4 (0<<5)
#define S3C2410_LCDCON1_STN4 (1<<5)
#define S3C2410_LCDCON1_STN8 (2<<5)
#define S3C2410_LCDCON1_TFT (3<<5)
--------------------------
#define S3C2410_ADDR(x) (0xF0000000 + (x))

/* LCD controller */
#define S3C2410_VA_LCD S3C2410_ADDR(0x00600000)
#define S3C2410_PA_LCD (0x4D000000)
#define S3C2410_SZ_LCD SZ_1M

再分析device_register()和platform_device_register()的实现代码:

device_register()------------------------

/**
* device_register - register a device with the system.
* @dev: pointer to the device structure
*
* This happens in two clean steps - initialize the device
* and add it to the system. The two steps can be called
* separately, but this is the easiest and most common.
* I.e. you should only call the two helpers separately if
* have a clearly defined need to use and refcount the device
* before it is added to the hierarchy.
*/

int device_register(struct device *dev)
{
device_initialize(dev); //初始化设备结构
return device_add(dev); //添加设备到设备层
}

platform_device_register()--------------------
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*
*/
int platform_device_register(struct platform_device * pdev)
{
device_initialize(&pdev->dev); //初始化设备结构
return platform_device_add(pdev); //添加一个片上的设备到设备层
}

由以上函数可知:device_register()和platform_device_register()都会首先初始化设备,区别在于第二步:其实platform_device_add()包括device_add(),只不过要先注册resources

platform_device_add()----------------------
/**
* platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
* This is part 2 of platform_device_register(), though may be called
* separately _iff_ pdev was allocated by platform_device_alloc().
*/
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;

/*++++++++++++++
The platform_device.dev.bus_id is the canonical name for the devices.
It's built from two components:

* platform_device.name ... which is also used to for driver matching.
* platform_device.id ... the device instance number, or else "-1"
to indicate there's only one.

These are concatenated, so name/id "serial"/0 indicates bus_id "serial.0", and
"serial/3" indicates bus_id "serial.3"; both would use the platform_driver
named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id)
and use the platform_driver called "my_rtc".
++++++++++++++*/

if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
else //"-1" indicate there's only one
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
for (i = 0; i < pdev->num_resources; i++) { //遍历设备资源个数,如LCD的两个资源:控制器和IRQ
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) { //resources分为两种IORESOURCE_MEM和IORESOURCE_IO
          //CPU对外设IO端口物理地址的编址方式有两种:I/O映射方式和内存映射方式
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}

if (p && insert_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d/n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s/n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
ret = device_add(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0)
if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource[i]);
return ret;
}

相关参考+++++++++++++++++++++++
device_initialize()------------------
/** </drivers/base/core.c>
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers,
* including adding it to the device hierarchy.
* It is the first half of device_register(), if called by
* that, though it can also be called separately, so one
* may use @dev's fields (e.g. the refcount).
*/

void device_initialize(struct device *dev)
{
kobj_set_kset_s(dev, devices_subsys);
kobject_init(&dev->kobj);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}
device_add(struct device *dev)-------------
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
* This adds it to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
*/
结构体resource----------------------
/* < /include/linux/ioport.h>
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
---------------------------

原文地址:http://blog.chinaunix.net/u1/58968/showart_467998.html

在8250.c(driver/serial/8250.c)的初始化函数serial8250_init()中,给出了一个很简单的例子

static struct platform_device *serial8250_isa_devs;

……

//create a platform_device

serial8250_isa_devs = platform_device_alloc(“serial8250”,PLAT8250_DEV_LEGACY);

platform_device_add(serial8250_isa_devs); //add the platform_device to system

platform_driver_register(&serial8250_isa_driver);//then register the platform_driver

还有另外一个比较类似的比较,就是driver_register和platform_driver_register的比较platform_driver_register(&xx_driver) 会向系统注册xx_driver这个驱动程序,这个函数会根据 xx_driver中的.name内容,搜索系统注册的device中有没有这个platform_device,如果有,就会执行 platform_driver(也就是xx_driver的类型)中的.probe函数。

对只需要初始化运行一次的函数都加上__init属性,__init 宏告诉编译器如果这个模块被编译到内核则把这个函数放到(.init.text)段,module_exit的参数卸载时同__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数,因为编译进内核的模块不需要做清理工作,显然__init和__exit对动态加载的模块是无效的,只支持完全编译进内核。


probe()函数是什么时候被调用,设备和驱动是怎么联系起来的

http://blog.youkuaiyun.com/charistain_huang/article/details/6612431

probe()函数是什么时候被调用,设备和驱动是怎么联系起来的??
platform_add_devices(ldd6410_devices, ARRAY_SIZE(ldd6410_devices)); //这是bsp中添加所有的设备–》 platform_device_register(devs[i]);//注册平台设备—》platform_device_add(pdev);将平台设备加入到platform_bus中—》device_add(&pdev->dev);

下面是驱动:

static int __init gpio_led_init(void)
{
 return platform_driver_register(&gpio_led_driver);   //注册平台驱动
}
platform_driver_register(&gpio_led_driver) ----》driver_register(&drv->driver);----》bus_add_driver(drv); //添加驱动到总线 ---》driver_attach(drv);//为驱动寻找相应的设备----》

int driver_attach(struct device_driver *drv)
{
 return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);   //遍历设备总线寻找驱动
}



-----》__driver_attach()

static int __driver_attach(struct device *dev, void *data)
{
 struct device_driver *drv = data;

 /*
  * Lock device and try to bind to it. We drop the error
  * here and always return 0, because we need to keep trying
  * to bind to devices and some drivers will return an error
  * simply if it didn't support the device.
  *
  * driver_probe_device() will spit a warning if there 
  * is an error.
  */

 if (drv->bus->match && !drv->bus->match(dev, drv))   //通过match判断驱动和设备是否匹配,这里通过比较dev和drv中的设备名来判断,所以设备名需要唯一
  return 0;

 if (dev->parent) /* Needed for USB */
  down(&dev->parent->sem);
 down(&dev->sem);
 if (!dev->driver)
  driver_probe_device(drv, dev);  //   驱动和设备绑定
 up(&dev->sem);
 if (dev->parent)
  up(&dev->parent->sem);

 return 0;
}


driver_probe_device(drv, dev);  ---》really_probe(dev, drv);




static int really_probe(struct device *dev, struct device_driver *drv)
{
 int ret = 0;

 atomic_inc(&probe_count);
 pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
   drv->bus->name, __func__, drv->name, dev->bus_id);
 WARN_ON(!list_empty(&dev->devres_head));

 dev->driver = drv;
 if (driver_sysfs_add(dev)) {
  printk(KERN_ERR "%s: driver_sysfs_add(%s) failed\n",
   __func__, dev->bus_id);
  goto probe_failed;
 }

 if (dev->bus->probe) {   
  ret = dev->bus->probe(dev);
  if (ret)
   goto probe_failed;
 } else if (drv->probe) {
  ret = drv->probe(dev);  //这里才真正调用了驱动的probe
  if (ret)
   goto probe_failed;
 }

 driver_bound(dev);
 ret = 1;
 pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
   drv->bus->name, __func__, dev->bus_id, drv->name);
 goto done;

probe_failed:
 devres_release_all(dev);
 driver_sysfs_remove(dev);
 dev->driver = NULL;

 if (ret != -ENODEV && ret != -ENXIO) {
  /* driver matched but the probe failed */
  printk(KERN_WARNING
         "%s: probe of %s failed with error %d\n",
         drv->name, dev->bus_id, ret);
 }
 /*
  * Ignore errors returned by ->probe so that the next driver can try
  * its luck.
  */
 ret = 0;
done:
 atomic_dec(&probe_count);
 wake_up(&probe_waitqueue);
 return ret;
}

http://apps.hi.baidu.com/share/detail/21837006

Platform Device and Drivers

从< linux/platform_device.h>我们可以了解Platform bus上面的驱动模型接口:platform_device,platform_driver。和PCI和USB这些大结构的总线不同,虚拟总线Platform bus使用最小结构来集成SOC processer上的各种外设,或者各种“legacy”之间的互联。

Platform device
典型的Platform device是系统中的各种自主设备,包括各种桥接在外围总线上的port-based device和host,以及各种集成在SOC platform上的控制器。他们都有一个特点,那就是CPU BUS可以直接寻址,或者特殊的情况platform_device连接在其他总线上,但是它的寄存器是可以被直接寻址的。
Platform device有一个名字,用来进行driver的绑定;还有诸如中断,地址之类的一些资源列表

struct platform_device {
const char *name;
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource;
};


Platform drivers
Platform driver满足标准driver model,对driver的discovery/enumeration是在driver外部进行的,driver提供了probe()和 remove()方法.Platfomr dirvers通过标准模型提供power management和shutdown通知。

struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*suspend_late)(struct platform_device *, pm_message_t state);
int (*resume_early)(struct platform_device *);
int (*resume)(struct platform_device *);
struct device_driver driver;
};

Probe()函数必须验证指定设备的硬件是否真的存在,probe()可以使用设备的资源,包括时钟,platform_data等,Platform driver可以通过下面的函数完成对驱动的注册:
int platform_driver_register(struct platform_driver *drv);
一般来说设备是不能被热插拔的,所以可以将probe()函数放在init段里面来节省driver运行时候的内存开销:
int platform_driver_probe(struct platform_driver *drv,
int (probe)(struct platform_device ))

Device Enumeration
作为一个规则,平台(一般来说是板级)启动代码会注册所有的Platform device:
int platform_device_register(struct platform_device *pdev);
int platform_add_devices(struct platform_device **pdevs, int ndev);
一般来说只会注册那些实际存在的设备,不过也存在特殊的情况,比如kernel可能需要与一个不在板子上的外部网络适配器工作,或者那些不挂在任何总线上的控制器。
一般情况下,固件启动的过程会输出一个描述板子上所有存在设备的表。如果没有这个表,系统启动代码建立正确的设备的唯一方法就是为一个特定的板子编译一个kernel。这种board-specific kernel广泛用在嵌入式和一般系统的开发上。
在大部分情况下,设备的memory和IRQ资源不足够让驱动正常工作。board setup code会用device的platform_data域来为设备提供一些额外的资源。
嵌入式系统上的设备会频繁地使用一个或者多个时钟,这些时钟因为节电的原因只有在真正使用的时候才会被打开,系统在启动过程中会为设备分配时钟,可以通过clk_get(&pdev->dev, clock_name)来获得需要的时钟。

Legacy Drivers : Device Probing
一些driver并不会完全遵守标准driver model,这些driver会去注册自己的platform device,而不是让系统来完成注册。这是不值得推
荐的,主要用来兼容以前的一些旧设备。可以通过下面的API来支持这些legacy driver,一般这些API使用在不支持热插拔的driver上面:
struct platform_device *platform_device_alloc(
const char *name, int id);
可以使用platform_device_alloc动态地创建一个设备,一个更好的方法是,通过下面的函数动态创建一个设备,并把这个设备注册到系统中:
struct platform_device *platform_device_register_simple(
const char *name, int id,
struct resource *res, unsigned int nres);

Device Naming and Driver Binding
platform_device.dev.bus_id是一个设备在总线上的名字,它包含两部分:
* platform_device.name 设备名字,用来进行driver的匹配
* platform_device.id 设备实例的标号,如果是-1,表示同样名字的设备只有一个
举个简单的例子,name/id是“serial/1”则它的bus_id就是serial.1 如果name/id是“serial/0”则它的bus_id就是serial.0 ,如果它的name/id是“serial/-1”则它的bus_id就是serial。
driver的绑定是通过driver core自动完成的,完成driver和device的匹配后以后会自动执行probe()函数,如果函数执行成功,则driver和device就绑定在一起了,drvier和device匹配的方法有3种:
* 当一个设备注册的时候,他会在总线上寻找匹配的driver,platform device一般在系统启动很早的时候就注册了
* 当一个驱动注册[platform_driver_register()]的时候,他会遍历所有总线上的设备来寻找匹配,在启动的过程驱动的注册一般比较晚,或者在模块载入的时候
* 当一个驱动注册[platform_driver_probe()]的时候, 功能上和使用platform_driver_register()是一样的,唯一的区别是它不能被以后其他的device probe了,也就是说这个driver只能和一个device绑定。

  Platform device 和 Platform driver实际上是cpu总线可以直接寻址的设备和驱动,他们挂载在一个虚拟的总线platform_bus_type上,是一种bus-specific设备和驱动。与其他bus-specific驱动比如pci是一样的。他们都是将device和device_driver加了一个warpper产生,仔细看看platform_device就可以看到它必然包含一个device dev,而platform_driver也一样,它必然包含一个device_driver driver。
  所有的设备通过bus_id挂在总线上,多个device可以共用一个driver,但是一个device不可以对应多个driver。驱动去注册时候会根据设备名寻找设备,没有设备会注册失败,注册的过程会通过probe来进行相应资源的申请,以及硬件的初始化,如果probe执行成功,则device和driver的绑定就成功了。设备注册的时候同样会在总线上寻找相应的驱动,如果找到他也会试图绑定,绑定的过程同样是执行probe。

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/yili_xie/archive/2010/01/15/5193609.aspx

platform_driver_probe与platform_driver_register的区别

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

相关推荐

Tomcat源码:Acceptor与Poller、PollerEvent

前文中我们介绍到NioEndpoint的start方法启动了Acceptor与Poller这两个异步线程来处理连接请求,本文我们就来接着介绍这两个组件。目录前言一、Acceptor1、构造方法2、run方法LimitLatchNioChannel二、Poller与PollerEvent2、pollerregisterruneventsprocessKey。

CRUD工程师 2065

Linux内核平台设备驱动模型platform_device和platform_driver框架

关于Linux内核的平台设备驱动模型框架

D584640810的博客 4390

IC验证 AHB-RAM 项目(二)——接口与事务代码的编写

本文介绍了准备工作及接口、事务相关代码编写要点。在接口部分,重点说明了lvc_ahb_if.sv、rkv_ahbram_if.sv和rkv_ahbram_tb.sv三个关键文件的功能,其中rkv_ahbram_if.sv通过动态任务实现复位信号控制。事务部分则涉及lvc_ahb_types.sv中的枚举类型定义和lvc_ahb_transaction.sv中的随机化变量设置,用于构建AHB传输激励。整个验证环境采用模块化设计,通过接口实现TB与DUT的通信,为后续验证工作奠定基础。

weixin_45926435的博客 405

嵌入式Linux 开发经验:platform_driver_register 的使用方法

嵌入式Linux 开发经验:platform_driver_register 的使用方法

坚韧与专注 7806

NotImplementedError: Cannot copy out of meta tensor; no data! Please use torch.nn.Module.to_empty()

Please use torch.nn.Module.to_empty() instead of torch.nn.Module.to()输出(llama) (base) [root@localhost home]# nvidia-smi Unable to determine the device handle for GPU0000:09:00.0: Unknown Error。这是典型的“孤儿进程”,但它还是。(僵尸进程),占用 GPU 资源。如果进程仍然不消失,并且。,这意味着它的父进程是。

m0_61636632的博客 2096

从零开始跑通3DGS教程:(五)3DGS训练

本文介绍了在Docker环境中训练3DGS(3D Gaussian Splatting)的方法,属于《从零开始跑通3DGS教程》系列文章的第五部分。主要内容包括:Docker环境准备(基于pytorch/pytorch:1.13.1-cuda11.6-cudnn8-runtime镜像)、容器内conda环境配置(安装必要依赖和3DGS相关组件)、以及训练命令执行步骤。文章提供了详细的命令行操作流程,帮助读者快速搭建3DGS训练环境。适合在Linux系统、Nvidia GPU环境下使用,完整教程可参考系列文章

诺有缸的高飞鸟的博客 1754

platform_device与platform_driver

 通常编写linux字符设备常接触到的file_operations以及miscdevice,然后申请设备号,注册字符设备,没有涉及到设备驱动模型,而驱动模型里,device_driver根本没有涉及到设备操作的函数、file_operations等,只有一些电源管理,热插拔相关的函数。platform_device里也主要是resource的管理,所以感觉两者根本就没关系,也很奇怪为什么要弄两套...

weixin_34221773的博客 535

BoT-SORT实战:手把手教你实现BoT-SORT训练和测试

文章目录摘要1、数据准备2、配置BoT-SORT环境2.1、获取代码2.2、创建虚拟环境2.3、安装必要的包2.3.1、安装pytorch2.3.2、安装cython2.3.3、安装Cython-bbox2.3.4、安装faiss2.3.5、安装其他的包3、训练3.1、生成REID数据集3.2、预训练模型3.2.1、ByteTrack预训练模型3.2.2、REID预训练模型3.3、开始训练4、如何更改FASTREID的BackBone?5、测试5.1、测试视频5.2、测试摄像头5.3、多类别测试 摘要 Bo

m0_47867638的博客 2298

linux的platform驱动

如下内容来自《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》   将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息(比如从设备树中获取到设备信息),然后根据获取到的设备信息来初始化设备。 这样就相当于驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。这个就是 Linux 中的总线(bus)、驱动(driver)和设备(device)模型,也就是常说的驱动分离。总线就是驱动和设备信息的月老,负责给两者牵线搭桥,如图:   当我们向系统注册一个驱动的时候,总

qlexcel的专栏 2343

Firefox卸载卡住不动的解决方案

本文主要介绍了Firefox卸载卡住不动的解决方案,希望能对使用Windows的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 15万+

Yolov8轻量级网络:Ghostnet、G_ghost、Ghostnetv2家族大作战(一):华为Ghostnet,超越谷歌MobileNet | CVPR2020

Yolov8引入轻量级网络Ghostnet、G_ghost、Ghostnetv2等,减少最新的深度神经网络的计算成本,引入高效的神经网络结构。

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 2568

Linux之Platform设备驱动

上述代码假定为了实现 funca(), 对于同类设备而言, 操作流程一致, 都要经过通用代码 A、 底层 ops1、通用代码 B、 底层 ops2、 通用代码 C、 底层 ops3这几步, 分层设计明显带来的好处是, 对于通用代码 A、B、 C, 具体的底层驱动不需要再实现, 而仅仅只关心其底层的操作ops1、 ops2、 ops3。当内核中有驱动注册时, 总线就会在右侧的设备中查找, 是否有匹配的设备, 同样的, 当有设备注册到内核中时, 也会在总线左侧查找是否有匹配的驱动。

风间琉璃的博客 3047

[网络安全自学篇] 七十三.WannaCry勒索病毒复现及分析(四)蠕虫传播机制全网源码详细解读

这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您喜欢,一起进步。前文分享了逆向分析OllyDbg动态调试工具的基本用法,包括界面介绍、常用快捷键和TraceMe案例分析。这篇文章将继续详细讲解WannaCry蠕虫的传播机制,带领大家详细阅读源代码,分享WannaCry勒索病毒是如何传播感染的。作者分析该病毒一个月,一方面觉得自己技术菜,另一方面深知系统安全需要坚持,继续加油。希望文章对您有所帮助~

杨秀璋的专栏 1万+

LINUX PLATFORM总线、设备及驱动接口分析

前面几章我们分析了LINUX设备驱动模型,说明了总线注册、设备注册、驱动注册以及它们之间是如何绑定的。借助之前的分析基础,本文以platform模块为例,介绍系统中如何创建总线、设备以及驱动的实例。 platform总线是一个虚拟的总线,该模块的出现实现了资源与驱动的隔离。一般没有驱动程序 仅实现platform-driver,该模型一般用于各具体的设备驱动模块(如i2c、spi、pc...

lickylin的专栏 1430

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

Statistics and Machine Learning Toolbox

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

Linux驱动之platform 总线设备注册流程分析

了解数据模型keyObjectkeyset;了解总线模型:设备、驱动、总线了解了 注册自己的总线并创建属性文件驱动总线bus注册流程分析platform平台总线注册流程分析在总线下注册设备及设备注册流程分析这里 就开始分析platform平台总线设备注册流程分析。其实 就是以总线设备、总线驱动、总线为知识点展开理论知识,以platform平台总线为例展开进一步讨论研究知识点,深化理解。Linux驱动之platform总线设备注册流程分析流程,对比总线下设备注册,思路其实都是一样的。

十年饮冰,难凉热血 878

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Linux Platform总线+SPI总线分析-SPI驱动

 一、SPI原理模型 如上图所示,主设备对应SOC芯片中的SPI控制器,通常,一个SOC中可能存在多个SPI控制器,像上面的例子所示,SOC芯片中有3个SPI控制器。每个控制器下可以连接多个SPI从设备,每个从设备有各自独立的CS引脚。每个从设备共享另外3个信号引脚:SCK、MISO、MOSI。任何时刻,只有一个CS引脚处于有效状态,与该有效CS引脚连接的设备此时可以与主设备(SP

伍意的博客 4135

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

linux下总线、设备、驱动模型分析-platform

linux、bus、设备、驱动模型,platform分析

m0_46685251的博客 199

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

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

weixin_49406449的博客 2010

Linux下的platform总线驱动代码分析

前言: 本篇blog就针对platform总线驱动的调用机制,就直接抠出代码进行分析了。 Evdev.c (drivers\input) module_init(evdev_init); module_exit(evdev_exit); static int __init evdev_init(void) { return input_register_han

Winston 654

Linux Platform设备驱动分析1

Linux Platform设备驱动分析1(基于Linux6.6)---基本概念

ListQ的博客 1060

Linux platform设备驱动

本文章来源于正点原子资料,记录下来,以后参考。 基于正点原子imx6ull开发板 1、platform总线 Linux系统内核使用bus_type结构体表示总线,此结构体定义在文件include/linux/device.h, bus_type 结构体内容如下: struct bus_type { const char *name; /* 总线名字*/ const char *dev_name; struct device *dev_root; struct device_attribu

qq_31487199的博客 478

platform总线驱动分析

platform总线驱动分析 1.前言 在Linux的设备模型中,关心总线、设备和驱动这3个实体,总线将设备和驱动绑定。在系统每注册一个设备的时候,会寻找与之匹配的驱动;相反的,在系统每注册一个驱动的时候,会寻找与之匹配的设备,而匹配由总线完成。 一个现实的Linux设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCI、USB、I2C、SPI等的设备而言,这自然不是问题,但是在嵌入式系统里...

eurphan_y的博客 254

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

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

hinewcc的博客 1719

Linux platform总线驱动框架

Linux平台总线驱动框架摘要 Linux平台总线驱动框架由三个核心组件构成: 总线(bus):负责管理挂载其上的设备和驱动 设备(device):表示物理硬件资源 驱动(driver):提供设备操作接口 该框架采用面向对象设计,通过platform_device描述硬件资源(如内存地址、中断号),platform_driver管理具体驱动。匹配过程由platform_bus完成,匹配成功后调用probe函数初始化设备。相比单片机驱动,Linux驱动具有更严格的标准化、更好的复用性和更复杂的资源管理机制,包

Ricardo_yu的博客 688

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

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

jamecer的博客 1158

驱动开发中platform设备驱动架构详解

总线是一条虚拟总线(只有一条),这类总线没有对应的硬件结构。为相应的设备,为相应的驱动。

让技术没有门槛 4065
上一篇: 版本控制系统GIT和SVN的区别
下一篇: platform总线按键驱动分析(精华版)
fml1997
博客等级 码龄10年 30粉丝 56原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值