目录
1 修改设备树
2 修改驱动
3 驱动源码
3.1 驱动源码
3.2 设备树节点
3.3 驱动源码分析
3.3.1 ##解释
3.3.2 class_create解释
3.3.3 class_create_file解释
3.3.4 of_get_named_gpio_flags解释
3.3.5 devm_gpio_request解释
3.3.6 platform_driver_register解释
3.3.7 platform_driver_unregister解释
修改设备树添加my_wifi_led资源。
--- a/kernel/linux-5.10/arch/arm64/boot/dts/rockchip/rk3588-ir88mx01-v10.dtsi
+++ b/kernel/linux-5.10/arch/arm64/boot/dts/rockchip/rk3588-ir88mx01-v10.dtsi
@@ -232,6 +232,8 @@
gpio2_out = <&gpio3 RK_PA2 GPIO_ACTIVE_LOW>;
gpio3_in = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>;
gpio3_out = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
+
+ my_wifi_led = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;
};
minix_modem {
这样就可以在/proc/device-tree/jw_io_init/看到有my_wifi_led文件。
rockchip@ubuntu:/sys/class/jw_io_control$ ls /proc/device-tree/jw_io_init/
compatible gpio0_in gpio0_out gpio1_in gpio1_out gpio2_in gpio2_out gpio3_in gpio3_out my_wifi_led name pinctrl-0 pinctrl-names wifi_led z32_reset zigbee_led
修改驱动添加能控制led灯亮灭的文件节点。
--- a/kernel/linux-5.10/drivers/jw/jw_gpio/jw_io_core.c
+++ b/kernel/linux-5.10/drivers/jw/jw_gpio/jw_io_core.c
@@ -111,7 +111,8 @@ CLASS_GPIO_DEFINE(gpio2_in, gpio2_in_pin)
CLASS_GPIO_DEFINE(gpio2_out, gpio2_out_pin)
CLASS_GPIO_DEFINE(gpio3_in, gpio3_in_pin)
CLASS_GPIO_DEFINE(gpio3_out, gpio3_out_pin)
-
+CLASS_GPIO_DEFINE(my_wifi_led, wifi_led_pin)
+
static int jw_io_control_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -141,6 +142,7 @@ static int jw_io_control_probe(struct platform_device *pdev)
GPIO_REQUEST(gpio2_out, gpio2_out_pin, 1);
GPIO_REQUEST(gpio3_in, gpio3_in_pin, 0);
GPIO_REQUEST(gpio3_out, gpio3_out_pin, 1);
+ GPIO_REQUEST(my_wifi_led, wifi_led_pin, 1);
return 0;
}
这样修改后就可以在/sys/class/jw_io_control目录下对文件节点my_wifi_led进行写操作,从而控制led灯的亮灭。
rockchip@ubuntu:/sys/class/jw_io_control$ ls
gpio0_in gpio0_out gpio1_in gpio1_out gpio2_in gpio2_out gpio3_in gpio3_out my_wifi_led wifi_led z32_reset zigbee_led
rockchip@ubuntu:/sys/class/jw_io_control$ echo 1 > my_wifi_led
rockchip@ubuntu:/sys/class/jw_io_control$ echo 0 > my_wifi_led
kernel/linux-5.10/drivers/jw/jw_gpio/jw_io_core.c驱动代码内容如下所示
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Debug */
#if 1
#define DBG(x...) printk(x)
#else
#define DBG(x...) do { } while (0)
#endif
#define GPIO_PIN_DEFINE(pin) unsigned int pin;
#define FUNC_GPIO_SHOW(name, p) \
static ssize_t name##_show(struct class *class,struct class_attribute *attr, char *buf) { \
int value; \
gpio_direction_input(p); \
value = gpio_get_value(p); \
return snprintf(buf, 3, "%d\n", value); \
}
#define FUNC_GPIO_STORE(name, p) \
static ssize_t name##_store(struct class *class,struct class_attribute *attr, const char *buf, size_t count) { \
int enable; \
sscanf(buf, "%d", &enable); \
gpio_direction_output(p, enable ? 1 : 0); \
return count; \
}
#define CLASS_GPIO_DEFINE(nm, pin) \
GPIO_PIN_DEFINE(pin) \
FUNC_GPIO_SHOW(nm, pin) \
FUNC_GPIO_STORE(nm, pin) \
struct class_attribute class_attr_##nm = { \
.attr = { \
.name = __stringify(nm), \
.mode = 0666}, \
.show = nm##_show, \
.store = nm##_store,\
};
#define GPIO_REQUEST(name, pin, inout) { \
ret = of_get_named_gpio_flags(node, #name, 0, &flags); \
if (ret < 0) { \
printk("%s() Can not read property" #name "\n", __FUNCTION__); \
} else { \
pin = ret; \
printk(#name ": %d, state: %d\r\n", ret, !flags); \
ret = devm_gpio_request(&pdev->dev, pin, #name); \
if (ret < 0) { \
printk("%s() devm_gpio_request " #name " request ERROR\n", __FUNCTION__); \
} else { \
if (!inout) { \
ret = gpio_direction_input(pin); \
if (ret < 0) { \
printk("%s() gpio_direction_input " #name " set ERROR\n", __FUNCTION__); \
} \
} else {\
ret = gpio_direction_output(pin, !flags); \
if (ret < 0) { \
printk("%s() gpio_direction_output " #name " set ERROR\n", __FUNCTION__); \
} \
} \
} \
} \
ret = class_create_file(g_jwio_class, &class_attr_##name); \
if (ret) { \
printk(KERN_ERR "%s:Fail to creat " #name " class file\n", __func__); \
return ret; \
} else { \
printk("class_create jw_io_control " #name " OK !\n"); \
} \
}
static struct class *g_jwio_class;
CLASS_GPIO_DEFINE(z32_reset, z32_reset_pin)
CLASS_GPIO_DEFINE(wifi_led, wifi_led_pin)
CLASS_GPIO_DEFINE(zigbee_led, zigbee_led_pin)
CLASS_GPIO_DEFINE(gpio0_in, gpio0_in_pin)
CLASS_GPIO_DEFINE(gpio0_out, gpio0_out_pin)
CLASS_GPIO_DEFINE(gpio1_in, gpio1_in_pin)
CLASS_GPIO_DEFINE(gpio1_out, gpio1_out_pin)
CLASS_GPIO_DEFINE(gpio2_in, gpio2_in_pin)
CLASS_GPIO_DEFINE(gpio2_out, gpio2_out_pin)
CLASS_GPIO_DEFINE(gpio3_in, gpio3_in_pin)
CLASS_GPIO_DEFINE(gpio3_out, gpio3_out_pin)
CLASS_GPIO_DEFINE(my_wifi_led, wifi_led_pin)
static int jw_io_control_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
enum of_gpio_flags flags;
static struct class *pclass;
int ret;
printk(" #######jw_io_control_probe####### \n");
pclass = class_create(THIS_MODULE, "jw_io_control");
if (pclass == NULL) {
printk(KERN_ERR "jw_io_control create failed!\r\n");
return -1;
}
g_jwio_class = pclass;
GPIO_REQUEST(z32_reset, z32_reset_pin, 1);
GPIO_REQUEST(wifi_led, wifi_led_pin, 1);
GPIO_REQUEST(zigbee_led, zigbee_led_pin, 1);
GPIO_REQUEST(gpio0_in, gpio0_in_pin, 0);
GPIO_REQUEST(gpio0_out, gpio0_out_pin, 1);
GPIO_REQUEST(gpio1_in, gpio1_in_pin, 0);
GPIO_REQUEST(gpio1_out, gpio1_out_pin, 1);
GPIO_REQUEST(gpio2_in, gpio2_in_pin, 0);
GPIO_REQUEST(gpio2_out, gpio2_out_pin, 1);
GPIO_REQUEST(gpio3_in, gpio3_in_pin, 0);
GPIO_REQUEST(gpio3_out, gpio3_out_pin, 1);
GPIO_REQUEST(my_wifi_led, wifi_led_pin, 1);
return 0;
}
static int jw_io_control_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id jw_io_control_of_match[] = {
{ .compatible = "jw_config", },
{},
};
MODULE_DEVICE_TABLE(of, jw_io_control_of_match);
static struct platform_driver jw_io_control_driver = {
.probe = jw_io_control_probe,
.remove = jw_io_control_remove,
.driver = {
.name = "jw_config",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(jw_io_control_of_match),
},
};
static int __init jw_io_control_init(void)
{
platform_driver_register(&jw_io_control_driver);
return 0;
}
static void __exit jw_io_control_exit(void)
{
platform_driver_unregister(&jw_io_control_driver);
}
late_initcall(jw_io_control_init);
MODULE_DESCRIPTION("jw config Driver");
MODULE_LICENSE("GPL");
219 jw_io_init {
220 compatible = "jw_config";
221 pinctrl-names = "default";
222 pinctrl-0 = <&jw_io_config >;
223 z32_reset = <&gpio1 RK_PA4 GPIO_ACTIVE_HIGH>;
224 wifi_led = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;
225 zigbee_led = <&gpio3 RK_PB1 GPIO_ACTIVE_LOW>;
226
227 gpio0_in = <&gpio3 RK_PA4 GPIO_ACTIVE_LOW>;
228 gpio0_out = <&gpio3 RK_PA0 GPIO_ACTIVE_LOW>;
229 gpio1_in = <&gpio3 RK_PA5 GPIO_ACTIVE_LOW>;
230 gpio1_out = <&gpio3 RK_PA1 GPIO_ACTIVE_LOW>;
231 gpio2_in = <&gpio3 RK_PA6 GPIO_ACTIVE_LOW>;
232 gpio2_out = <&gpio3 RK_PA2 GPIO_ACTIVE_LOW>;
233 gpio3_in = <&gpio3 RK_PA7 GPIO_ACTIVE_LOW>;
234 gpio3_out = <&gpio3 RK_PA3 GPIO_ACTIVE_LOW>;
235
236 my_wifi_led = <&gpio4 RK_PC2 GPIO_ACTIVE_LOW>;
237 };
435 jw_io_init {
436 jw_io_config: jw_io_config {
437 rockchip,pins = <1 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>,
438 <4 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>,
439 <3 RK_PB1 RK_FUNC_GPIO &pcfg_pull_up>,
440 <3 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>,
441 <3 RK_PA1 RK_FUNC_GPIO &pcfg_pull_up>,
442 <3 RK_PA2 RK_FUNC_GPIO &pcfg_pull_up>,
443 <3 RK_PA3 RK_FUNC_GPIO &pcfg_pull_up>,
444 <3 RK_PA4 RK_FUNC_GPIO &pcfg_pull_up>,
445 <3 RK_PA5 RK_FUNC_GPIO &pcfg_pull_up>,
446 <3 RK_PA6 RK_FUNC_GPIO &pcfg_pull_up>,
447 <3 RK_PA7 RK_FUNC_GPIO &pcfg_pull_up>;
448 };
449 };
## 是一个特殊的预处理操作符,称为“标记粘贴”操作符。它的作用是在宏展开时,将两个宏参数(或宏参数与常量字符串)连接成一个单一的标识符。
代码中的“pclass = class_create(THIS_MODULE, "jw_io_control");”会在 /sys/class/ 文件下创建一个jw_io_control 文件,class_create 函数分析如下所示。
函数原型 |
#define class_create(owner, name) \ ({ \ static struct lock_class_key __key; \ __class_create(owner, name, &__key); \ }) struct class *__class_create(struct module *owner, const char *name, struct lock_class_key *key) |
|
参数 |
struct module *owner |
指向该设备类所属模块的指针。模块是内核中可加载和卸载的代码块,这个参数允许内核跟踪哪个模块创建了设备类。如果设备类是由内核核心代码(而非可加载模块)创建的,则此参数可能为 NULL |
const char *name |
设备类的名称 |
|
struct lock_class_key *key |
指向锁类键的指针,用于锁调试。锁类键是内核中用于调试锁竞争和死锁问题的一种机制。如果不需要锁调试,此参数可以传递 NULL |
|
返回值 |
struct class * |
成功:class结构体指针 失败:NULL |
功能 |
用于创建新的设备类。设备类的创建允许内核将具有相似功能的设备组织在一起,并提供了通过 /sys/class/ 目录与用户空间交互的接口 |
以下代码中的“class_create_file(g_jwio_class, &class_attr_##name);”会在 /sys/class/jw_io_control/文件下创建一个 name 的文件。
pclass = class_create(THIS_MODULE, "jw_io_control")
g_jwio_class = pclass;
class_create_file(g_jwio_class, &class_attr_##name);
class_create_file 函数分析如下所示。
函数原型 |
static inline int __must_check class_create_file(struct class *class, const struct class_attribute *attr) |
|
参数 |
struct class *class |
指向class结构体的指针,这个结构体代表了Linux中的一个设备类。在Linux中,设备被组织成类和设备,类是所有共享相同属性的设备的集合。例如,usb类包含了所有的USB设备 |
const struct class_attribute *attr |
指向class_attribute结构体的指针,这个结构体定义了将要被创建的文件(属性)的属性,比如文件名、模式(只读、只写、可读可写)、显示和存储函数(用于从内核读取或写入内核的数据)等。 |
|
返回值 |
||
功能 |
1、在/sys/class/ 2、创建的文件名由class_attribute结构体中的attr.name字段指定。 3、文件的操作(如读取或写入)通过class_attribute结构体中定义的show和store回调函数实现。 |
of_get_named_gpio_flags 函数的分析如下所示。
函数原型 |
int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags) |
|
参数 |
struct device_node *np |
设备树节点指针 |
const char *list_name |
GPIO属性名称 |
|
int index |
属性值的索引 |
|
enum of_gpio_flags *flags |
用于保存GPIO属性值的标志 |
|
返回值 |
int |
成功:返回GPIO号 失败:负数 |
功能 |
从设备树中获取指定名称的GPIO列表中第index个GPIO的编号以及可能的标志 |
devm_gpio_request 函数的分析如下所示,该函数通过结合 devres 资源管理机制来简化 GPIO 的分配和释放过程,确保在设备驱动程序卸载时,GPIO 能够自动释放,减少了手动释放的负担。
函数原型 |
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) |
|
参数 |
struct device *dev |
设备结构体指针 |
unsigned gpio |
GPIO号 |
|
const char *label |
用于表示请求的GPIO引脚 |
|
返回值 |
int |
成功:0 失败:负数 |
功能 |
请求一个GPIO引脚设备使用 |
devm_gpio_request 函数在内核源码中的原型如下所示。
364 /**
365 * devm_gpio_request - request a GPIO for a managed device
366 * @dev: device to request the GPIO for
367 * @gpio: GPIO to allocate
368 * @label: the name of the requested GPIO
369 *
370 * Except for the extra @dev argument, this function takes the
371 * same arguments and performs the same function as
372 * gpio_request(). GPIOs requested with this function will be
373 * automatically freed on driver detach.
374 *
375 * If an GPIO allocated with this function needs to be freed
376 * separately, devm_gpio_free() must be used.
377 */
378
379 int devm_gpio_request(struct device *dev, unsigned gpio, const char *label)
380 {
381 unsigned *dr;
382 int rc;
383
384 dr = devres_alloc(devm_gpio_release, sizeof(unsigned), GFP_KERNEL);
385 if (!dr)
386 return -ENOMEM;
387
388 rc = gpio_request(gpio, label);
389 if (rc) {
390 devres_free(dr);
391 return rc;
392 }
393
394 *dr = gpio;
395 devres_add(dev, dr);
396
397 return 0;
398 }
platform_driver_register 函数的分析如下所示。
函数原型 |
#define platform_driver_register(drv) __platform_driver_register(drv, THIS_MODULE) |
|
参数 |
struct platform_driver *drv |
指向 platform_driver 结构的指针。platform_driver 结构定义了平台驱动的相关信息和操作 |
返回值 |
||
功能 |
用于注册平台设备驱动 |
__platform_driver_register 函数在内核源码中的原型如下所示。
648 int __platform_driver_register(struct platform_driver *drv,
649 struct module *owner)
650 {
651 drv->driver.owner = owner;
652 drv->driver.bus = &platform_bus_type;
653 drv->driver.probe = platform_drv_probe;
654 drv->driver.remove = platform_drv_remove;
655 drv->driver.shutdown = platform_drv_shutdown;
656
657 return driver_register(&drv->driver);
658 }
platform_driver_unregister 函数的分析如下所示。
函数原型 |
void platform_driver_unregister(struct platform_driver *drv) |
|
参数 |
struct platform_driver *drv |
指向 platform_driver 结构的指针。platform_driver 结构定义了平台驱动的相关信息和操作 |
返回值 |
||
功能 |
用于注销平台设备驱动 |
platform_driver_unregister 函数在内核源码中的原型如下所示。
665 void platform_driver_unregister(struct platform_driver *drv)
666 {
667 driver_unregister(&drv->driver);
668 }
190 void driver_unregister(struct device_driver *drv)
191 {
192 if (!drv || !drv->p) {
193 WARN(1, "Unexpected driver unregister!\n");
194 return;
195 }
196 driver_remove_groups(drv, drv->groups);
197 bus_remove_driver(drv);
198 }