文章目录
想象一下,你正在开发一个嵌入式系统(比如智能家居控制器)。系统里有很多外设(比如 LED 灯、温度传感器),它们直接集成在芯片上(SoC),不像 USB 或网卡那样可以热插拔。这些设备的特点是:
1. 固定不变:寄存器地址、中断号等资源不会改变。
2.需要初始化:必须明确配置(比如设置寄存器值)才能工作。
3.不能自动检测:不像 USB 设备那样插拔即用。
传统方法的痛点:
早期的驱动开发方式是将硬件信息(比如寄存器地址)直接写死在驱动代码中。比如:
#define LED_REGISTER_ADDR 0x80000000
这样做的问题:
1.驱动和硬件强耦合:换一块板子(比如寄存器地址变了),驱动就要重写。
2.维护困难:同一驱动需要为不同平台维护多个版本。
3.编译复杂:不同硬件需要不同的编译配置。
Platform 模型的好处
Platform 驱动模型通过 “分离设备描述和驱动实现” 解决了这些问题。简单来说就是:
1.硬件信息(设备):通过设备树(Device Tree)或代码单独描述。
2.驱动逻辑:只关注如何操作硬件,不关心硬件的具体地址。
3.动态匹配:内核自动将驱动和设备关联,无需硬编码。
二、Platform 驱动模型的三大核心组件
1.Platform 总线(虚拟总线)
1.作用:像“红娘”一样,把设备和驱动匹配起来。
2.匹配规则:
通过设备名称(name
)。
通过设备树中的 compatible
字段。
通过 id_table
表(支持多个设备变体)。
2.Platform 设备(platform_device
)
1.作用:描述硬件资源(寄存器地址、中断号等)。
2.定义方式:现在主流使用设备树,即在 .dts
文件中定义设备节点,如
gpioled {//添加设备节点
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-gpioled";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_led>;
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
3.Platform 驱动(platform_driver
)
作用:实现设备的操作逻辑(初始化、读写等)。
核心函数:
probe()
:设备匹配成功后调用,用于初始化硬件。
remove()
:驱动卸载时释放资源。如下:
static int led_probe(struct platform_device *dev)//相当于初始化函数
{
printk("led driver and device was matched!\r\n");
/* 1、设置设备号 */
if (leddev.major) {
leddev.devid = MKDEV(leddev.major, 0);
register_chrdev_region(leddev.devid, LEDDEV_CNT, LEDDEV_NAME);
} else {
alloc_chrdev_region(&leddev.devid, 0, LEDDEV_CNT, LEDDEV_NAME);
leddev.major = MAJOR(leddev.devid);
}
/* 2、注册设备 */
cdev_init(&leddev.cdev, &led_fops);
cdev_add(&leddev.cdev, leddev.devid, LEDDEV_CNT);
/* 3、创建类 */
leddev.class = class_create(THIS_MODULE, LEDDEV_NAME);
if (IS_ERR(leddev.class)) {
return PTR_ERR(leddev.class);
}
/* 4、创建设备 */
leddev.device = device_create(leddev.class, NULL, leddev.devid, NULL, LEDDEV_NAME);
if (IS_ERR(leddev.device)) {
return PTR_ERR(leddev.device);
}
/* 5、初始化IO */
leddev.node = of_find_node_by_path("/gpioled");
if (leddev.node == NULL){
printk("gpioled node nost find!\r\n");
return -EINVAL;
}
leddev.led0 = of_get_named_gpio(leddev.node, "led-gpio", 0);
if (leddev.led0 < 0) {
printk("can't get led-gpio\r\n");
return -EINVAL;
}
gpio_request(leddev.led0, "led0");
gpio_direction_output(leddev.led0, 1); /* led0 IO设置为输出,默认高电平 */
return 0;
}
static int led_remove(struct platform_device *dev)
{
gpio_set_value(leddev.led0, 1); /* 卸载驱动的时候关闭LED */
gpio_free(leddev.led0); /* 释放IO */
cdev_del(&leddev.cdev); /* 删除cdev */
unregister_chrdev_region(leddev.devid, LEDDEV_CNT); /* 注销设备号 */
device_destroy(leddev.class, leddev.devid);
class_destroy(leddev.class);
return 0;
}
三、Platform 驱动的工作流程
1. 设备描述,即在设备树中定义设备:
2.驱动注册,驱动代码中定义 platform_driver
,并注册到内核:
3.驱动与设备匹配,即内核启动时,会扫描设备树中的设备节点,如果发现某个设备的 compatible
字段与某个驱动的 of_match_table
匹配,就会调用驱动的 probe()
函数,完成初始化。如下为定义的匹配项:
四、测试
1.将驱动文件挂载到imx6ull设备中,
2. 加载驱动后查看对应的platfoam驱动,
3.点灯测试,
总结
概念 | 作用 |
Platform 总线 | 负责匹配设备和驱动,像“红娘”一样连接两者。 |
Platform 设备 | 描述硬件资源(寄存器、中断等),通常通过设备树定义。 |
Platform 驱动 | 实现设备操作逻辑,通过 probe() 初始化设备,通过 remove() 释放资源。 |