linux驱动中probe函数是怎么调用的

10 篇文章 2 订阅

linux驱动的三个概念:设备、驱动、总线

probe何时被调用:在总线上驱动和设备的名字匹配,就会调用驱动的probe函数

probe函数被调用后,系统就调用platform设备的probe函数完成驱动注册最后工作。下面是probe被调用前的一些流程。

device一般是先于driver注册,但也不全是这样的顺序。Linux的Device和Driver的注册过程分别枚举挂在该BUS上所有的Driver和Device实现了这种时序无关性。

Platform机制的本身使用并不复杂,由两部分组成:platform_device 和 platfrom_driver
通过Platform机制开发发底层驱动的大致流程为:   定义 platform_device 注册 platform_device 定义 platform_driver 注册 platform_driver。

 

注册device

在2.6内核中platform设备用结构体platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,

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

以i2c驱动为例,定义一个device

struct platform_device s3c_device_i2c = {
      .name = "s3c2410-i2c",
      .id = -1,
      .num_resources = ARRAY_SIZE(s3c_i2c_resource),
      .resource = s3c_i2c_resource,
};

定义好了platform_device结构体后就可以调用函数platform_add_devices()向系统中添加该设备了,之后可以调用platform_device_register()进行设备注册。要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用,即执行platform_driver_register之前,原因是因为驱动注册时需要匹配内核中所以已注册的设备名

s3c2410-i2c的platform_device是在系统启动时,在cpu.c里的s3c_arch_init()函数里进行注册的,这个函数申明为arch_initcall(s3c_arch_init);会在系统初始化阶段被调用。
arch_initcall的优先级高于module_init(driver在这个函数中被注册到总线中)。所以会在Platform驱动注册之前调用。(详细参考include/linux/init.h)
 

注册driver

驱动程序需要实现结构体struct platform_driver,参考drivers/i2c/busses

/* device driver for platform bus bits */
static struct platform_driver s3c2410_i2c_driver = {
      .probe = s3c24xx_i2c_probe,
      .remove = s3c24xx_i2c_remove,
      .resume = s3c24xx_i2c_resume,
      .driver = {
               .owner = THIS_MODULE,
               .name = "s3c2410-i2c",
      },
};

...

static int __init s3c2410_i2c_init(void)
{
    return platform_driver_register(&s3c2410_i2c_driver);   //注册平台驱动
}


module_init(s3c2410_i2c_init);

在驱动初始化函数中调用函数platform_driver_register()注册platform_driver,需要注意的是s3c_device_i2c结构中name元素和s3c2410_i2c_driver结构中driver.name必须是相同的,这样在platform_driver_register()注册时会对所有已注册的所有platform_device中的name和当前注册的platform_driver的driver.name进行比较,只有找到相同的名称的platfomr_device才能注册成功,具体调用流程可以参考文章开头的流程图。当注册成功时会调用platform_driver结构元素probe函数指针,这里就是s3c24xx_i2c_probe,当进入probe函数后,需要获取设备的资源信息,注册定时器、中断、创建设备节点等等。

 

本文章部分内容转自:

https://www.cnblogs.com/lifan3a/articles/5045447.html

http://blog.csdn.net/thl789/article/details/6723350

  • 17
    点赞
  • 211
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个使用Linux SPI总线和W5500芯片的驱动程序示例,可供参考: ```c #include <linux/kernel.h> #include <linux/module.h> #include <linux/spi/spi.h> #include <linux/of_gpio.h> #include <linux/gpio.h> #define W5500_SPI_MODE (SPI_MODE_0) #define W5500_SPI_BITS_PER_WORD (8) #define W5500_SPI_MAX_SPEED_HZ (10 * 1000 * 1000) struct w5500_data { struct spi_device *spi; struct gpio_desc *reset_gpio; }; static int w5500_reset(struct w5500_data *w5500) { if (!w5500->reset_gpio) return -ENODEV; gpiod_set_value_cansleep(w5500->reset_gpio, 1); msleep(10); gpiod_set_value_cansleep(w5500->reset_gpio, 0); msleep(10); gpiod_set_value_cansleep(w5500->reset_gpio, 1); msleep(150); return 0; } static int w5500_probe(struct spi_device *spi) { struct device *dev = &spi->dev; struct w5500_data *w5500; int ret; w5500 = devm_kzalloc(dev, sizeof(*w5500), GFP_KERNEL); if (!w5500) return -ENOMEM; spi->bits_per_word = W5500_SPI_BITS_PER_WORD; spi_setup(spi); w5500->spi = spi; w5500->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(w5500->reset_gpio)) { ret = PTR_ERR(w5500->reset_gpio); dev_err(dev, "Failed to get reset GPIO: %d\n", ret); return ret; } ret = w5500_reset(w5500); if (ret) { dev_err(dev, "Failed to reset W5500: %d\n", ret); return ret; } return 0; } static int w5500_remove(struct spi_device *spi) { struct w5500_data *w5500 = spi_get_drvdata(spi); if (w5500_reset(w5500)) { dev_warn(&spi->dev, "Failed to reset W5500 on removal\n"); } return 0; } static const struct of_device_id w5500_of_match[] = { { .compatible = "wiznet,w5500", }, { }, }; MODULE_DEVICE_TABLE(of, w5500_of_match); static struct spi_driver w5500_driver = { .driver = { .name = "w5500", .of_match_table = w5500_of_match, }, .probe = w5500_probe, .remove = w5500_remove, }; module_spi_driver(w5500_driver); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("W5500 SPI driver"); MODULE_LICENSE("GPL"); ``` 此驱动程序在设备树使用以下属性: ```dts spi { #address-cells = <1>; #size-cells = <0>; w5500@0 { compatible = "wiznet,w5500"; reg = <0>; spi-max-frequency = <10000000>; reset-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>; }; }; ``` 该驱动程序使用 `devm_gpiod_get()` 函数获取重置 GPIO 引脚,以及 `gpiod_set_value_cansleep()` 函数将 GPIO 引脚设置为适当的值。它还使用 SPI API 函数来设置 SPI 总线并进行数据传输。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值