
Platform 点亮一个 LED1. PlatformPlatform 是 Linux 为片上外设提供的一种标准驱动模型其核心思想是将硬件描述与驱动逻辑分离。硬件信息由设备树描述Linux 根据设备树创建 Platform Device驱动程序实现 Platform DriverPlatform 总线负责两者的自动匹配匹配成功后调用 probe() 完成设备初始化。2. 设备树修改2.1 在设备树里添加 GPIO 节点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; };gpioled节点名称Node Name表示这里定义了一个叫 gpioled 的设备。在开发板上可以查看是否挂载上切换到 device-tree 目录查看#address-cells 1;这个属性表示如果这个节点下面还有子节点那么子节点的地址用 1 个 cell 表示。#size-cells 1; 子节点的 size大小用 1 个 cell 表示。compatible “atkalpha,gpioled”;告诉 Linux 我是一个类型为 atkalpha,gpioled 的设备。pinctrl-names “default”;这个设备有一套默认的引脚配置default 是这套配置的名字。pinctrl-0 pinctrl_led;默认使用 pinctrl_led 这组引脚配置这个配置下方有说明。led-gpio gpio1 3 GPIO_ACTIVE_LOW;LED 使用 gpio1 控制器的第 3 号引脚。status “okay”;表示启用这个设备。如果写status “disabled”;Linux 会忽略这个节点。2.2 在设备树中添加 pinctrl 引脚配置节点pinctrl_led: ledgrp { fsl,pins MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0 /* LED0 */ ; };pinctrl_led:这个是节点标签它的作用是给该节点起一个引用名称方便其他节点引用。ledgrp这个是节点名称表示它是给 LED 用的。MX6UL_PAD_GPIO1_IO03__GPIO1_IO03含义是把 i.MX6ULL 的 GPIO1_IO03 这个物理引脚配置为 GPIO1_IO03 功能。0x10B0引脚电气属性配置值用于设置驱动能力、速度、上下拉等。3. 驱动代码3.1 主体框架以下为主体框架static int led_probe(struct platform_device *dev) { } static int led_remove(struct platform_device *dev) { return 0; } struct of_device_id led_of_match[] { {.compatible }, { /* sentinel */ }, }; struct platform_driver led_driver { .driver .probe .remove }; /* 驱动加载 */ static int __init leddriver_init(void) { return platform_driver_register(led_driver); } /* 驱动卸载 */ static void __exit leddriver_exit(void) { platform_driver_unregister(led_driver); } module_init(leddriver_init); module_exit(leddriver_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(huangjiajing);整体框架主要由以下四部分组成Platform 驱动框架│├── probe() 设备匹配成功后执行├── remove() 驱动卸载或设备移除时执行├── platform_driver 描述 Platform 驱动└── 驱动注册与注销首先是 struct platform_driver led_driver 这个结构体struct platform_driver { int (*probe)(struct platform_device *); int (*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; };里面用到 probe、remove 这两个函数还有 device_driver driver 这个结构体。probe()Platform 驱动的入口函数负责设备初始化和资源申请。remove()Platform 驱动的退出函数负责资源释放和设备清理。device_driver driver用于描述驱动信息如name、of_match_table供 Linux 完成设备与驱动的匹配。接下来介绍 device_driver 这个结构体struct device_driver { const char *name; 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 */ const struct of_device_id *of_match_table; const struct acpi_device_id *acpi_match_table; int (*probe) (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 dev_pm_ops *pm; struct driver_private *p; };这里面要用到的是 name 和 of_match_table用于匹配设备树name 用于指定驱动名称在没有使用设备树时Linux 可以根据设备名和驱动名进行匹配。of_match_table 用于指定设备树匹配表Linux 根据设备树节点的 compatible 属性与该匹配表进行匹配。匹配成功后会调用 probe()。接下来 probe() 和 remove() 的实现逻辑与前一章类似。3.2 完整代码#include linux/module.h #include linux/kernel.h #include linux/init.h #include linux/fs.h #include linux/slab.h #include linux/uaccess.h #include linux/io.h #include linux/cdev.h #include linux/device.h #include linux/of.h #include linux/of_address.h #include linux/of_irq.h #include linux/gpio.h #include linux/of_gpio.h #include linux/string.h #include linux/irq.h #include asm/mach/map.h #include asm/uaccess.h #include asm/io.h #include linux/interrupt.h #include linux/poll.h #include linux/fcntl.h #include linux/ide.h #include linux/platform_device.h #define GPIOLED_CNT 1 #define GPIOLED_NAME dtsplatled #define LEDOFF 0 #define LEDON 1 /* gpioled设备结构体 */ struct gpioled_dev{ dev_t devid; int major; int minor; struct cdev cdev; struct class *class; struct device *device; struct device_node *nd; int led_gpio; }; struct gpioled_dev gpioled; /* LED */ static int led_open(struct inode *inode, struct file *filp) { filp-private_data gpioled; return 0; } static int led_release(struct inode *inode, struct file *filp) { return 0; } static ssize_t led_write(struct file *filp, const char __user *buf, size_t count, loff_t *ppos) { int ret; unsigned char databuf[1]; struct gpioled_dev *dev filp-private_data; ret copy_from_user(databuf, buf, count); if(ret 0) { return -EINVAL; } if(databuf[0] LEDON) { gpio_set_value(dev-led_gpio, 0); } else if(databuf[0] LEDOFF) { gpio_set_value(dev-led_gpio, 1); } return 0; } /* 操作集 */ static const struct file_operations led_fops { .owner THIS_MODULE, .write led_write, .open led_open, .release led_release, }; static int led_probe(struct platform_device *dev) { /* 1获取设备节点 */ #if 0 gpioled.nd of_find_node_by_path(/gpioled); if(gpioled.nd NULL) { ret -EINVAL; goto fail_findnode; } #endif gpioled.nd dev-dev.of_node; /* 2, 获取LED所对应的GPIO */ gpioled.led_gpio of_get_named_gpio(gpioled.nd, led-gpio, 0); if(gpioled.led_gpio 0) { printk(cant find led gpio\r\n); ret -EINVAL; goto fail_findnode; } printk(led gpio num %d\r\n, gpioled.led_gpio); /* 3,申请IO */ ret gpio_request(gpioled.led_gpio, led-gpio); if (ret) { printk(Failed to request the led gpio\r\n); ret -EINVAL; goto fail_findnode; } /* 4,使用IO设置为输出 */ ret gpio_direction_output(gpioled.led_gpio, 1); if (ret) { goto fail_setoutput; } /* 5,输出底电平点亮LED灯*/ gpio_set_value(gpioled.led_gpio, 0); return 0; fail_setoutput: gpio_free(gpioled.led_gpio); fail_findnode: return ret; } static int led_remove(struct platform_device *dev) { printk(led remove\r\n); /* 关灯 */ gpio_set_value(gpioled.led_gpio, 1); /* 注销字符设备驱动 */ cdev_del(gpioled.cdev); unregister_chrdev_region(gpioled.devid, GPIOLED_CNT); device_destroy(gpioled.class, gpioled.devid); class_destroy(gpioled.class); /* 释放IO */ gpio_free(gpioled.led_gpio); return 0; } struct of_device_id led_of_match[] { {.compatible atkalpha,gpioled}, { /* sentinel */ }, }; struct platform_driver led_driver { .driver { .name imx6ull-led, /* 无设备树和设备进行匹配驱动名字 */ .of_match_table led_of_match, /* 设备树匹配表 */ }, .probe led_probe, .remove led_remove, }; /* 驱动加载 */ static int __init leddriver_init(void) { return platform_driver_register(led_driver); } /* 驱动卸载 */ static void __exit leddriver_exit(void) { platform_driver_unregister(led_driver); } module_init(leddriver_init); module_exit(leddriver_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(huangjiajing); static int __init leddriver_init(void) { return platform_driver_register(led_driver); } /* 驱动卸载 */ static void __exit leddriver_exit(void) { platform_driver_unregister(led_driver); } module_init(leddriver_init); module_exit(leddriver_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(huangjiajing);