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

本文对比了Linux中device_register()和platform_device_register()的功能及实现细节。解释了这两种方法如何初始化和注册设备,并通过具体实例说明了它们之间的差异。
AI助手已提取文章相关产品:

  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函数。

您可能感兴趣的与本文相关内容

<think>嗯,用户现在想了解的是Linux设备驱动中的`device_register`函数的使用相关问题。之前他们问了关于`dev_set_drvdata`的内容,现在转向更底层的设备注册函数。我需要结合之前的回答用户提供的引用内容来组织信息。 首先,用户提供的引用[1]提到`platform_device_register``platform_driver_register`,指出并不是所有驱动都需要显式注册平台设备。这可能意味着用户对设备注册的不同方式存在疑问,需要区分`device_register`平台设备的注册。引用[2]讨论了字符设备杂项设备的注册,比如`misc_register``register_chrdev`,这可能帮助用户理解不同设备类型的注册方式。 在之前的回答中,我详细讲解了`dev_set_drvdata`,涉及设备私有数据的绑定。现在用户的问题是关于`device_register`,这是一个更基础的函数,用于向内核注册设备结构体。需要明确`device_register`的作用、使用场景以及与其他注册函数(如`platform_device_register`)的区别。 用户可能没有明确表达的是,他们可能在实际开发中遇到了设备注册的问题,或者在阅读代码时发现了不同的注册方法,想了解何时使用`device_register`以及如何正确使用它。需要确保回答结构清晰,步骤明确,并提供代码示例帮助理解。 需要注意内核版本差异,比如旧版本可能需要手动初始化`dev->p`,而新版本可能简化了流程。此外,生命周期管理错误处理也是关键点,比如使用`devm`系列函数自动释放资源,避免内存泄漏。 最后,用户可能还需要了解`device_register`与`platform_device_register`的关系,以及何时选择哪种注册方式。结合引用[1]的内容,可以说明平台设备通常是预定义的,而`device_register`更适用于动态创建设备的情况,比如热插拔设备或总线设备。</think>--- ### **device_register函数详解(Linux设备驱动)** --- #### **一、核心功能与作用** 1. **基本定义** `device_register` 是Linux内核中用于**动态注册设备对象**的核心接口,属于设备模型(Device Model)的关键组成部分。其功能包括: - 将 `struct device` 对象注册到内核设备树 - 触发用户空间事件(如`uevent`,通知`udev`创建设备节点) - 与已注册的驱动进行匹配(通过`bus->match`方法)[^1] 2. **与platform_device_register区别** - `platform_device_register`:专用于注册平台设备(`platform_device`),通常用于描述**静态资源**(如设备地址、中断号等),适用于预定义的硬件设备[^1] - `device_register`:通用设备注册接口,适用于**动态创建设备**(如热插拔设备、虚拟设备、总线设备等) --- #### **二、函数原型与参数解析** ```c int device_register(struct device *dev); ``` | **参数** | **类型** | **说明** | |----------|-------------------|-------------------------------------------------------------------------| | `dev` | `struct device *` | 预初始化的设备对象指针,需至少设置以下字段:`parent`, `bus`, `name` | | **返回值** | `int` | 0表示成功,负数表示错误(如内存不足或重复注册) | --- #### **三、使用步骤与代码示例** ##### **1. 定义设备对象** ```c #include <linux/device.h> // 自定义设备结构体(嵌入struct devicestruct my_custom_device { struct device dev; // 必须作为第一个成员 int custom_param; void *private_data; }; ``` ##### **2. 初始化设备** ```c // 设备释放回调(必须实现) static void my_device_release(struct device *dev) { struct my_custom_device *my_dev = container_of(dev, struct my_custom_device, dev); kfree(my_dev); } // 创建设备实例 struct my_custom_device *my_dev_alloc(void) { struct my_custom_device *my_dev; my_dev = kzalloc(sizeof(*my_dev), GFP_KERNEL); if (!my_dev) return NULL; // 初始化device结构体 device_initialize(&my_dev->dev); // 引用[1]中设备模型的基础初始化 my_dev->dev.parent = NULL; // 若挂载到总线,需指定父设备 my_dev->dev.bus = &my_bus_type; // 关联总线类型 my_dev->dev.release = my_device_release; dev_set_name(&my_dev->dev, "my_device_%d", 0); // 动态命名 return my_dev; } ``` ##### **3. 注册设备到内核** ```c int my_device_register(struct my_custom_device *my_dev) { int ret; ret = device_register(&my_dev->dev); if (ret < 0) { pr_err("Device registration failed: %d\n", ret); put_device(&my_dev->dev); // 减少引用计数,触发release回调 } return ret; } ``` ##### **4. 注销设备** ```c void my_device_unregister(struct my_custom_device *my_dev) { device_unregister(&my_dev->dev); // 自动调用put_device } ``` --- #### **四、关键注意事项** 1. **设备生命周期管理** - **注册前**:必须调用 `device_initialize()` 完成内核对象初始化 - **注销时**:`device_unregister` 会触发 `dev->release` 回调,需在此释放资源 - **内存安全**:若使用`kzalloc`分配设备对象,必须实现`release`回调释放内存 2. **命名冲突与动态分配** - 使用 `dev_set_name()` 动态生成设备名称(避免`/sys/devices`下的名称冲突) - 示例:`dev_set_name(dev, "sensor-%02d", id);` 3. **与驱动的匹配规则** - 总线类型(`dev->bus`)必须与驱动声明的一致(如`&platform_bus_type`或自定义总线) - 匹配条件通过 `bus->match` 方法实现(如比较 `dev->name` `drv->id_table`) --- #### **五、典型应用场景** 1. **动态创建设备** ```c // 创建设备对象并注册 struct my_custom_device *dev = my_dev_alloc(); if (dev) { dev->custom_param = 100; device_register(&dev->dev); } ``` 2. **虚拟设备驱动** ```c // 虚拟温度传感器 static struct my_custom_device virtual_sensor = { .dev = { .init_name = "virtual_temp", .bus = &platform_bus_type, .release = virtual_sensor_release, }, .private_data = &temp_calibration_table, }; device_register(&virtual_sensor.dev); ``` 3. **热插拔设备模拟** ```c // 模拟USB设备插入 struct device *usb_port = get_usb_host_controller(); struct my_custom_device *usb_dev = my_dev_alloc(); usb_dev->dev.parent = usb_port; device_register(&usb_dev->dev); ``` --- #### **六、调试与常见错误** 1. **错误代码解析** | **错误码** | **原因** | **解决方案** | |------------|----------------------------------|---------------------------------------| | `-EEXIST` | 设备名称重复 | 使用`dev_set_name()`动态生成唯一名称 | | `-ENOMEM` | 内存分配失败 | 检查`kzalloc`返回值,优化内存分配策略 | | `-EINVAL` | 未设置`dev->parent`或`dev->bus` | 明确设备父节点所属总线类型 | 2. **调试技巧** - 查看设备树:`ls /sys/devices/` 或 `tree /sys/bus/` - 通过`dmesg`观察注册日志: ```bash dmesg | grep "my_device" ``` - 使用`strace`跟踪系统调用: ```bash strace -e trace=ioctl,openat insmod my_driver.ko ``` --- ### **相关问题** 1. 如何通过`device_register`实现设备与驱动的自动匹配? 2. 在热插拔场景中,如何确保`dev->release`回调的安全性? 3. `device_register`与`platform_device_register`的性能差异有哪些? 4. 如何通过`sysfs`为自定义设备添加属性文件? 5. 在多设备场景下,如何避免`dev_set_name`的命名冲突? --- ### **引用说明** [^1]: 平台设备与通用设备的注册差异(参考`platform_device_register`的设计逻辑) [^2]: 设备模型的内存管理与生命周期(结合`devm_`系列函数分析)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值