Platform

​ 在Linux设备驱动模型中 (since v2.6),需要关心总线,设备和驱动这三个实体。总线将设备与驱动绑定。在设备注册或者注册驱动的时候,总线都会寻找与其相对应的驱动或设备进行匹配。

​ 一个现实的Linux 设备和驱动通凋阁需要挂接在一种总线上,对于本身依附于 PCI.
USB,I2C、SPI等的设备而言,自然不是问题但是在嵌人式系统里面,在 Soc 系统中华集成的独立外设控制器、挂接在 Soc 内存空间的外设等却不依附于此类总线。基于这一背景,Linux 发明了一种虚拟的总线,称为platform 总线,相应的设备称为platform_device,而驱动成为platform _driver。

所谓的platform_device并不是与字符设备、块设备和网络设备并列的概念。比如lcd, i2c, rtc等都是platform_device,而他们本身就是字符设备。

platform_driver

platform_driver的结构体定义如下:

struct platform_driver {
int (*probe)(struct platform_device *);
void (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver;
const struct platform_device_id *id_table;
bool prevent_deferred_probe;
/*
* For most device drivers, no need to care about this flag as long as
* all DMAs are handled through the kernel DMA API. For some special
* ones, for example VFIO drivers, they know how to manage the DMA
* themselves and set this flag so that the IOMMU layer will allow them
* to setup and manage their own I/O address space.
*/
bool driver_managed_dma;
};

其中,probe, remove是我们做平台驱动开发经常接触的接口函数。

platform_driver中的suspend, resume函数已经不推荐使用了,更推荐使用platform_driver.driver->pm来填充函数。

这里涉及到一个成员struct device_driver driver;,其定义如下:

struct device_driver {
const char *name;
const struct bus_type *bus;

struct module *owner;
const char *mod_name; /* used for built-in modules */

bool suppress_bind_attrs; /* disables bind/unbind via sysfs */
enum probe_type probe_type;

const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;

int (*probe) (struct device *dev);
void (*sync_state)(struct device *dev);
int (*remove) (struct device *dev);
void (*shutdown) (struct device *dev);
int (*suspend) (struct device *dev, pm_message_t state);
int (*resume) (struct device *dev);
const struct attribute_group **groups;
const struct attribute_group **dev_groups;

const struct dev_pm_ops *pm;
void (*coredump) (struct device *dev);

struct driver_private *p;
};

竟然发现这里还有一套probe/remove/suspend/resume函数。但这里按下不表,只需要知道这些函数在platform中没有使用到即可。

platform_device

platform_device结构体定义如下:

struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev;
u64 platform_dma_mask;
struct device_dma_parameters dma_parms;
u32 num_resources;
struct resource *resource;

const struct platform_device_id *id_entry;
/*
* Driver name to force a match. Do not set directly, because core
* frees it. Use driver_set_override() to set or clear it.
*/
const char *driver_override;

/* MFD cell pointer */
struct mfd_cell *mfd_cell;

/* arch specific additions */
struct pdev_archdata archdata;
};

platform bus

platform总线的注册在driver/base/platform.c +1513的位置,代码实现如下:

struct device platform_bus = {
.init_name = "platform",
};
EXPORT_SYMBOL_GPL(platform_bus);
...
const struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.probe = platform_probe,
.remove = platform_remove,
.shutdown = platform_shutdown,
.dma_configure = platform_dma_configure,
.dma_cleanup = platform_dma_cleanup,
.pm = &platform_dev_pm_ops,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
...

int __init platform_bus_init(void)
{
int error;

early_platform_cleanup();

error = device_register(&platform_bus);
if (error) {
put_device(&platform_bus);
return error;
}
error = bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);

return error;
}

其中,重点关注bus_register。

/**
* bus_register - register a driver-core subsystem
* @bus: bus to register
*
* Once we have that, we register the bus with the kobject
* infrastructure, then register the children subsystems it has:
* the devices and drivers that belong to the subsystem.
*/
int bus_register(const struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
struct kobject *bus_kobj;
struct lock_class_key *key;

priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;

priv->bus = bus;

// 后面会用到这个变量
priv->drivers_autoprobe = 1;

...

retval = add_probe_files(bus);
...
}
EXPORT_SYMBOL_GPL(bus_register);

这个subsys_private是bus的一个私有变量,后面可以通过 bus_to_subsys函数找到该私有变量。

这里记住drivers_autoprobe这个变量,后面会用到。

add_probe_files

这里可以拓展一下add_probe_files函数:

static ssize_t drivers_autoprobe_show(const struct bus_type *bus, char *buf)
{
struct subsys_private *sp = bus_to_subsys(bus);
int ret;

if (!sp)
return -EINVAL;

ret = sysfs_emit(buf, "%d\n", sp->drivers_autoprobe);
subsys_put(sp);
return ret;
}

static ssize_t drivers_autoprobe_store(const struct bus_type *bus,
const char *buf, size_t count)
{
struct subsys_private *sp = bus_to_subsys(bus);

if (!sp)
return -EINVAL;

if (buf[0] == '0')
sp->drivers_autoprobe = 0;
else
sp->drivers_autoprobe = 1;

subsys_put(sp);
return count;
}

static ssize_t drivers_probe_store(const struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
int err = -EINVAL;

dev = bus_find_device_by_name(bus, NULL, buf);
if (!dev)
return -ENODEV;
if (bus_rescan_devices_helper(dev, NULL) == 0)
err = count;
put_device(dev);
return err;
}

static BUS_ATTR_WO(drivers_probe);
static BUS_ATTR_RW(drivers_autoprobe);

static int add_probe_files(const struct bus_type *bus)
{
int retval;

retval = bus_create_file(bus, &bus_attr_drivers_probe);
if (retval)
goto out;

retval = bus_create_file(bus, &bus_attr_drivers_autoprobe);
if (retval)
bus_remove_file(bus, &bus_attr_drivers_probe);
out:
return retval;
}

该函数通过bus_create_file创建了两个文件,其实就是在/sys/bus/<bus-name>/下创建了drivers_probedrivers_autoprobe文件。

其中,BUS_ATTR_WO和BUS_ATTR_RW是语法糖,这里就看RW的好了。

定义如下:

#define BUS_ATTR_RW(_name) \
struct bus_attribute bus_attr_##_name = __ATTR_RW(_name)

#define __ATTR_RW(_name) __ATTR(_name, 0644, _name##_show, _name##_store)

#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}

这个语法糖定义了bus_attribute结构体,设置了文件权限,然后指定了show函数和store函数。

回到show和store函数中,这里先看autoprobe的:

static ssize_t drivers_autoprobe_show(const struct bus_type *bus, char *buf)
{
struct subsys_private *sp = bus_to_subsys(bus);
int ret;

if (!sp)
return -EINVAL;

ret = sysfs_emit(buf, "%d\n", sp->drivers_autoprobe);
subsys_put(sp);
return ret;
}

static ssize_t drivers_autoprobe_store(const struct bus_type *bus,
const char *buf, size_t count)
{
struct subsys_private *sp = bus_to_subsys(bus);

if (!sp)
return -EINVAL;

if (buf[0] == '0')
sp->drivers_autoprobe = 0;
else
sp->drivers_autoprobe = 1;

subsys_put(sp);
return count;
}

autoprobe这个文件可读可写,里面修改了sp->drivers_autoprobe的值。

这里先提前揭秘了,autoprobe这个变量就是来控制插入驱动或创建新device的时候是否自动匹配设备或驱动。

继续看probe文件的写函数:

static ssize_t drivers_probe_store(const struct bus_type *bus,
const char *buf, size_t count)
{
struct device *dev;
int err = -EINVAL;

dev = bus_find_device_by_name(bus, NULL, buf);
if (!dev)
return -ENODEV;
if (bus_rescan_devices_helper(dev, NULL) == 0)
err = count;
put_device(dev);
return err;
}

一目了然,可以通过echo进来设备名进行手动probe。