platform驱动平台下,关于probe函数中,形参dev的“dev->dev.of_node;”的理解

本文介绍了Linux内核中如何使用platform_driver_register函数注册驱动,并在设备树匹配到相应设备时执行probe函数。probe函数的参数dev指向platform_device结构体,其中的dev.of_node成员用于获取设备树节点信息。通过这个信息,可以方便地配置和操作设备,如设置GPIO。此外,还展示了如何通过dev->dev.of_node替代of_find_node_by_path来获取设备节点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        驱动函数里通过int platform_driver_register (struct platform_driver *driver)函数注册的驱动, 

183 static int __init miscbeep_init(void)
184 {
185 return platform_driver_register(&beep_driver);
186 }

198 module_init(miscbeep_init);

当platform总线检测到设备树有和该驱动匹配的设备节点后,就执行probe函数(  int (*probe) (struct device *dev); ),此时probe函数的形参dev指针指向的变量是platform_device类型,platform_device结构体保存的是platform平台下的设备信息。

22 struct platform_device {
23 const char *name; 
24 int id; 
25 bool id_auto;
26 struct device dev;
27 u32 num_resources; 
28 struct resource *resource;
29
30 const struct platform_device_id *id_entry;
31 char *driver_override; /* Driver name to force a match */
32
33 /* MFD cell pointer */
34 struct mfd_cell *mfd_cell;
35
36 /* arch specific additions */
37 struct pdev_archdata archdata;
38 };

        其 26行的“struct device dev” 成员(类似于C++的父类,platform_device继承了struct device )已经获取到了设备树中的节点信息,可以通过此成员获取节点信息,即“ miscbeep.nd = dev->dev.of_node;”

static int miscbeep_probe(struct platform_device *dev)
 {
 int ret = 0;

 printk("beep driver and device was matched!\r\n");
 /* 设置 BEEP 所使用的 GPIO */
 /* 1、获取设备节点:beep */
 //miscbeep.nd = of_find_node_by_path("/beep");
 
 miscbeep.nd = dev->dev.of_node;//用这种方式替代上一行“miscbeep.nd = of_find_node_by_path("/beep");”的方式来获取设备节点

device结构体:设备驱动模型中的基础结构体之一。结构体定义如下:

struct device {

  /*设备所依附的父设备,大多数情况下,这样的设备是某种总线或主控制器,如果该成员变量的值为NULL,表示当前设备是一个最顶端设备,通常这样的设备都不是你想得到的那个*/

  struct device *parent;

  struct device_private *p;

  struct kobject kobj;
  const char *init_name; /* initial name of the device */
  const struct device_type *type;

  struct mutex mutex; /* mutex 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 *platform_data; /* Platform specific data, device core doesn't touch it */
   struct dev_pm_info power;
  struct dev_pm_domain *pm_domain;

#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;

  struct device_node *of_node; /* associated device tree node */

  dev_t devt; /* dev_t, creates the sysfs "dev" */
  u32 id; /* device instance */

  spinlock_t devres_lock;
  struct list_head devres_head;

  struct klist_node knode_class;
  struct class *class;
  const struct attribute_group **groups; /* optional groups */

  void (*release)(struct device *dev);
};

<think>嗯,用户让我解释这个`st7789v_probe`函数。我需要先仔细看看代码的结构,然后逐步分析每个部分的作用。用户可能对Linux驱动开发不太熟悉,所以需要尽量用简单的语言解释清楚。 首先,这个函数是SPI设备驱动probe函数,当设备与驱动匹配时会被调用。函数名中的`st7789v`应该是指某个显示控制器,比如ST7789V型号的屏幕。probe函数主要负责初始化设备,分配资源,注册到系统中。 接下来看代码的结构。函数开头声明了一些变量,比如`backlight`设备节点指针、`ctx`结构体指针和返回值`ret`。然后使用`devm_kzalloc`分配内存,这是设备管理的内存分配函数,会自动释放,防止内存泄漏。如果分配失败,返回`-ENOMEM`错误。 接着,`spi_set_drvdata`把`ctx`和`spi`设备关联起来,方便后续使用。然后初始化DRM面板,设置设备和函数指针。这里可能涉及到显示框架的部分,需要提到DRM的作用。 然后是获取电源管理`regulator`,使用`devm_regulator_get`获取名为"power"的调节器。如果出错,打印错误信息并返回错误码。不过注意到代码里有个拼写错误,“gyd get our reset line”应该是“Couldn't get our power line”之类的,可能用户提供的代码有误,但需要指出来。 接下来获取复位GPIO,`devm_gpiod_get`获取名为"reset"的GPIO,设置为低电平输出。如果失败,返回错误。这里用户可能关心GPIO初始化的过程,以及复位信号的作用。 然后解析背光设备节点,`of_parse_phandle`获取"backlight"属性,找到对应的背光设备。如果存在,通过`of_find_backlight_by_node`获取背光设备,并检查是否成功。如果背光设备还没准备好,返回`-EPROBE_DEFER`让内核稍后再试。 之后,调用`drm_panel_add`将面板添加到DRM子系统中。如果失败,跳转到错误处理部分`err_free_backlight`,释放背光设备资源。最后返回0表示成功。 用户可能需要了解每个步骤的作用,尤其是资源申请和错误处理部分。比如为什么使用`devm_`系列函数,错误处理中的`goto`用法,以及`-EPROBE_DEFER`的作用。另外,代码中的拼写错误可能影响理解,需要指出。 需要确保解释清晰,分步骤说明每个关键函数和结构体的作用,帮助用户理解驱动初始化的流程。同时,提醒用户注意代码中的潜在问题,比如拼写错误和可能的资源泄漏,尽管使用了`devm`,但某些情况下仍需手动释放。</think>以下是对 `st7789v_probe` 函数的逐段解释,结合 SPI 设备驱动的核心逻辑和代码功能: --- ### **函数作用** `st7789v_probe` 是 Linux 内核中 **SPI 设备驱动**的探测函数Probe Function),当内核检测到与驱动匹配的 SPI 设备(例如 ST7789V 显示屏控制器)时,此函数会被调用。它的核心任务是: 1. **初始化设备硬件**(如电源、复位引脚、背光)。 2. **分配设备数据结构**并绑定到 SPI 设备。 3. **注册显示面板**到 DRM(Direct Rendering Manager)子系统,使显示屏能被图形系统控制。 --- ### **逐段代码分析** #### **1. 设备上下文分配** ```c struct st7789v *ctx; ctx = devm_kzalloc(&spi->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; ``` - **目的**:为设备分配内存(`struct st7789v` 是自定义的显示屏控制结构体)。 - **关键点**: - `devm_kzalloc`:内核提供的“设备管理”内存分配函数,自动释放内存,避免内存泄漏。 - `GFP_KERNEL`:内存分配标志,表示在进程上下文中可休眠等待内存。 --- #### **2. 绑定 SPI 设备与驱动数据** ```c spi_set_drvdata(spi, ctx); ctx->spi = spi; ``` - **目的**:将设备上下文 `ctx` 与 SPI 设备 `spi` 绑定,便于后续通过 `spi_get_drvdata` 获取上下文。 - **意义**:驱动操作中可通过 `spi` 对象访问设备私有数据。 --- #### **3. 初始化 DRM 面板** ```c drm_panel_init(&ctx->panel); ctx->panel.dev = &spi->dev; ctx->panel.funcs = &st7789v_drm_funcs; ``` - **目的**:初始化 DRM 面板,设置显示屏的底层操作函数(如使能、禁用、显示图像)。 - **关键结构**: - `st7789v_drm_funcs`:自定义的 DRM 面板操作函数集合(例如 `prepare`, `enable`, `disable` 等回调函数)。 --- #### **4. 获取电源管理(Power Regulator)** ```c ctx->power = devm_regulator_get(&spi->dev, "power"); if (IS_ERR(ctx->power)) dev_err(&spi->dev, "gyd get our reset line\n") // 存在拼写错误,应为 "Couldn't get power" return PTR_ERR(ctx->power); ``` - **目的**:通过设备树(Device Tree)获取名为 "power" 的电源调节器(Regulator),用于控制显示屏电源。 - **注意**: - `devm_regulator_get`:自动管理资源的获取函数- 代码中存在拼写错误(`gyd` 应为 `Couldn't`),实际开发中需修正。 --- #### **5. 获取复位引脚(Reset GPIO)** ```c ctx->reset = devm_gpiod_get(&spi->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ctx->reset)) { dev_err(&spi->dev, "Couldn't get our reset line\n"); return PTR_ERR(ctx->reset); } ``` - **目的**:获取复位引脚 GPIO,并初始化为低电平(`GPIOD_OUT_LOW`)。 - **作用**:通过拉高或拉低复位引脚电平,控制显示屏硬件复位。 --- #### **6. 获取背光设备(Backlight)** ```c backlight = of_parse_phandle(spi->dev.of_node, "backlight", 0); if (backlight) { ctx->backlight = of_find_backlight_by_node(backlight); of_node_put(backlight); if (!ctx->backlight) return -EPROBE_DEFER; } ``` - **目的**:从设备树中解析背光设备节点,并获取背光控制对象。 - **关键点**: - `of_parse_phandle`:解析设备树中的 `backlight` 属性,获取背光设备节点。 - `-EPROBE_DEFER`:若背光设备未初始化,延迟探测(等待背光驱动加载完成)。 --- #### **7. 注册 DRM 面板** ```c ret = drm_panel_add(&ctx->panel); if (ret < 0) goto err_free_backlight; return 0; ``` - **目的**:将 DRM 面板添加到系统中,使图形子系统(如 DRM/KMS)能控制显示屏。 - **错误处理**:若注册失败,跳转到 `err_free_backlight` 标签,释放背光资源。 --- #### **8. 错误处理(释放资源)** ```c err_free_backlight: if (ctx->backlight) put_device(&ctx->backlight->dev); return ret; ``` - **作用**:若注册面板失败,释放已获取的背光设备资源,避免内存泄漏。 --- ### **关键问题与改进建议** 1. **拼写错误修正**: - `dev_err(&spi->dev, "gyd get our reset line\n")` → 应为 `"Couldn't get power regulator"`。 2. **电源与复位顺序**: - 实际驱动中,通常需要先启用电源(`regulator_enable`),再操作复位引脚。 3. **延迟探测处理**: - 若背光设备未就绪,返回 `-EPROBE_DEFER` 是正确做法,确保依赖关系。 --- ### **代码流程图** ```plaintext 开始 ├─ 分配设备内存 (ctx) ├─ 绑定 SPI 设备和 ctx ├─ 初始化 DRM 面板 ├─ 获取电源 (power) ├─ 获取复位引脚 (reset) ├─ 获取背光设备 (backlight) ├─ 注册 DRM 面板 │ ├─ 成功 → 返回 0 │ └─ 失败 → 释放背光资源 └─ 结束 ``` --- ### **总结** 此函数是 ST7789V 显示屏驱动的核心初始化逻辑,完成了硬件资源获取、DRM 面板注册等关键步骤。实际开发中需结合设备树配置(如定义 `reset-gpios`, `power-supply`, `backlight` 等属性),并确保时序和错误处理的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值