Linux version: 4.14
Code link: Linux source code (v4.14) - Bootlin
例子:fwnode_property_read_string(child, "label", &led.name);
struct gpio_led led;
fwnode_property_read_string(child, "label", &led.name);
(1)先看下 gpio_led 结构体:
/* For the leds-gpio driver */
struct gpio_led {
const char *name;
const char *default_trigger;
unsigned gpio;
unsigned active_low : 1;
unsigned retain_state_suspended : 1;
unsigned panic_indicator : 1;
unsigned default_state : 2;
unsigned retain_state_shutdown : 1;
/* default_state should be one of LEDS_GPIO_DEFSTATE_(ON|OFF|KEEP) */
struct gpio_desc *gpiod;
};
(2)对于 fwnode_handle 结构体:
(device_node 的成员变量 fwnode 就是 fwnode_handle 类型)
struct fwnode_handle {
struct fwnode_handle *secondary;
const struct fwnode_operations *ops;
};
其中 fwnode_operations 结构体:
struct fwnode_operations {
void (*get)(struct fwnode_handle *fwnode);
void (*put)(struct fwnode_handle *fwnode);
bool (*device_is_available)(const struct fwnode_handle *fwnode);
bool (*property_present)(const struct fwnode_handle *fwnode,
const char *propname);
int (*property_read_int_array)(const struct fwnode_handle *fwnode,
const char *propname,
unsigned int elem_size, void *val,
size_t nval);
int
(*property_read_string_array)(const struct fwnode_handle *fwnode_handle,
const char *propname, const char **val,
size_t nval);
struct fwnode_handle *(*get_parent)(const struct fwnode_handle *fwnode);
struct fwnode_handle *
(*get_next_child_node)(const struct fwnode_handle *fwnode,
struct fwnode_handle *child);
struct fwnode_handle *
(*get_named_child_node)(const struct fwnode_handle *fwnode,
const char *name);
int (*get_reference_args)(const struct fwnode_handle *fwnode,
const char *prop, const char *nargs_prop,
unsigned int nargs, unsigned int index,
struct fwnode_reference_args *args);
struct fwnode_handle *
(*graph_get_next_endpoint)(const struct fwnode_handle *fwnode,
struct fwnode_handle *prev);
struct fwnode_handle *
(*graph_get_remote_endpoint)(const struct fwnode_handle *fwnode);
struct fwnode_handle *
(*graph_get_port_parent)(struct fwnode_handle *fwnode);
int (*graph_parse_endpoint)(const struct fwnode_handle *fwnode,
struct fwnode_endpoint *endpoint);
};
(2)接着看 fwnode_property_read_string 函数:
int fwnode_property_read_string(const struct fwnode_handle *fwnode,
const char *propname, const char **val)
{
int ret = fwnode_property_read_string_array(fwnode, propname, val, 1);
return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
其中调用了 fwnode_property_read_string_array 函数:
int fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
const char *propname, const char **val,
size_t nval)
{
int ret;
ret = fwnode_call_int_op(fwnode, property_read_string_array, propname,
val, nval);
if (ret == -EINVAL && !IS_ERR_OR_NULL(fwnode) &&
!IS_ERR_OR_NULL(fwnode->secondary))
ret = fwnode_call_int_op(fwnode->secondary,
property_read_string_array, propname,
val, nval);
return ret;
}
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
fwnode_property_read_string_array 函数中 fwnode_call_int_op 调用了 fwnode 中 fwnode_operations 的 property_read_string_array ,并把参数传进 property_read_string_array。
#define fwnode_has_op(fwnode, op) \
((fwnode) && (fwnode)->ops && (fwnode)->ops->op)
#define fwnode_call_int_op(fwnode, op, ...) \
(fwnode ? (fwnode_has_op(fwnode, op) ? \
(fwnode)->ops->op(fwnode, ## __VA_ARGS__) : -ENXIO) : \
-EINVAL)
对于"## __VA_ARGS__"可参考: #、##、__VA_ARGS__的使用_Vane Zhang的博客-CSDN博客
(3)fwnode_operations 回调函数的设置是在内核初始化发生的,追踪到内核初始化的代码:
/* initialize a node */
extern struct kobj_type of_node_ktype;
extern const struct fwnode_operations of_fwnode_ops;
static inline void of_node_init(struct device_node *node)
{
kobject_init(&node->kobj, &of_node_ktype);
node->fwnode.ops = &of_fwnode_ops;
}
对于 of_fwnode_ops:
const struct fwnode_operations of_fwnode_ops = {
.get = of_fwnode_get,
.put = of_fwnode_put,
.device_is_available = of_fwnode_device_is_available,
.property_present = of_fwnode_property_present,
.property_read_int_array = of_fwnode_property_read_int_array,
.property_read_string_array = of_fwnode_property_read_string_array,
.get_parent = of_fwnode_get_parent,
.get_next_child_node = of_fwnode_get_next_child_node,
.get_named_child_node = of_fwnode_get_named_child_node,
.get_reference_args = of_fwnode_get_reference_args,
.graph_get_next_endpoint = of_fwnode_graph_get_next_endpoint,
.graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint,
.graph_get_port_parent = of_fwnode_graph_get_port_parent,
.graph_parse_endpoint = of_fwnode_graph_parse_endpoint,
};
EXPORT_SYMBOL_GPL(of_fwnode_ops);
查看其中的 of_fwnode_property_read_string_array 函数:
static int
of_fwnode_property_read_string_array(const struct fwnode_handle *fwnode,
const char *propname, const char **val,
size_t nval)
{
const struct device_node *node = to_of_node(fwnode);
return val ?
of_property_read_string_array(node, propname, val, nval) :
of_property_count_strings(node, propname);
}
const struct device_node *node = to_of_node(fwnode); 该语句通过 fwnode 节点返回其所在的 device_node 节点 node(fwnode 是 device_node 的成员变量),然后对 node 调用 of 函数来获取设备树相关的属性值。
从上面的分析可以看出 fwnode 相关的函数最终也是调用 of 函数,他们的功能是一样的。
参考: