kobjetc是linux操作系统中非常重要的对象。我们知道,linux内核中有一个通用的惯例就是:对象一般都是嵌入到被管理的数据结构中。如struct list_head,struct rb_node...等对象都是嵌入到数据结构中,然后通过 struct list_head,struct rb_node...等对象将被管理的数据结构挂载到链表或者插入到红黑树中。
kobjetc就是这样的对象。kobject和sysfs文件系统是进行了深度的绑定。sysfs中每个文件背后就是一个kobject,所以在struct device数据定义中存在一个Kobject成员。
struct device {
struct device *parent;
struct device_private *p;
struct kobject kobj;
...
}
如何在sysfs文件系统中创建目录或者文件已经超过了本文的范畴。读者可以阅读其他相关的资料,也可以查看源码kobject.c文件。其中涉及的主要接口就两个:
static sysfs_create_file(struct kobject *kobj,const struct attribute *attr);
static void sysfs_remove_file(struct kobject *kobj,const struct attribute *attr);
其他类似的接口无非是这两个基础接口的变体
int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr)
void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr)
int sysfs_create_group(struct kobject *kobj,const struct attribute_group *grp);
调用上面的接口的效果就是在sysfs文件系统中kobj对应的入口目录下创建对应的文件。通过属性attribute中的show和store函数来实现读写文件的值。这样就相当于通过sysfs文件系统在内核和用户空间之间提供了一个桥梁,这个桥梁对于phy来说最直观也是最有价值的操作就是用来读写phy内部的寄存器从实现某些功能。当然通过phy-tools,socket,ioctl等方式也可以进行phy寄存器的操作,只是通过sysfs更加简便。
注意,相对于/proc的混乱,/sys的目录结构是经过尽心的设计的,不要随意在其顶层添加目录。大多数编程中几乎不会独立的创建kboject,也很少会在/sysfs中去创建文件和目录,涉及到在sysfs中创建目录和文件的操作,内核中已经完成了接口的封装并将其放在了合理的接口当中,如device_create函数,调用其时会在/sys/devices下创建对应的kboject入口目录,同时在/dev下创建对应的设备节点等等。所以除了非常充分的理由,开发中我们几乎不会去调用上述的添加属性的接口。
我们来看下,如何通过sysfs来调试phy。其实在内核代码中有厂家已经这么干了。请大家打开/drivers/net/phy/tja110x.c(什么?你没得内核代码,那说个锤子),NXP为tja110x的车载以太网实现的Phy驱动中就是这么干的。
/*一堆属性的定义*/
static DEVICE_ATTR(master_cfg, S_IWUSR | S_IRUSR,
sysfs_get_master_cfg, sysfs_set_master_cfg);
static DEVICE_ATTR(power_cfg, S_IWUSR | S_IRUSR,
sysfs_get_power_cfg, sysfs_set_power_cfg);
static DEVICE_ATTR(loopback_cfg, S_IWUSR | S_IRUSR,
sysfs_get_loopback_cfg, sysfs_set_loopback_cfg);
static DEVICE_ATTR(cable_test, S_IRUSR, sysfs_get_cable_test, NULL);
static DEVICE_ATTR(test_mode, S_IWUSR | S_IRUSR,
sysfs_get_test_mode, sysfs_set_test_mode);
static DEVICE_ATTR(led_cfg, S_IWUSR | S_IRUSR,
sysfs_get_led_cfg, sysfs_set_led_cfg);
static DEVICE_ATTR(link_status, S_IRUSR, sysfs_get_link_status, NULL);
static DEVICE_ATTR(wakeup_cfg, S_IWUSR | S_IRUSR,
sysfs_get_wakeup_cfg, sysfs_set_wakeup_cfg);
static DEVICE_ATTR(snr_wlimit_cfg, S_IWUSR | S_IRUSR,
sysfs_get_snr_wlimit_cfg, sysfs_set_snr_wlimit_cfg);
static struct attribute *nxp_sysfs_entries[] = {
&dev_attr_master_cfg.attr,
&dev_attr_power_cfg.attr,
&dev_attr_loopback_cfg.attr,
&dev_attr_cable_test.attr,
&dev_attr_test_mode.attr,
&dev_attr_led_cfg.attr,
&dev_attr_link_status.attr,
&dev_attr_wakeup_cfg.attr,
&dev_attr_snr_wlimit_cfg.attr,
NULL
};
static struct attribute_group nxp_attribute_group = {
.name = "configuration",
.attrs = nxp_sysfs_entries,
};
err = sysfs_create_group(&phydev->mdio.dev.kobj, &nxp_attribute_group);
调用sysfs_create_group把这些属性定义注册到sysfs中,就会在phydev->mdio.dev.kobj目录下生成属性对应的文件,通过cat和echo操作就可以实现phy功能的开关。
上面的注册部分,大家根据自己项目的需要把show和store函数修改掉,就可以编译到内核中去尝试下了,非常方便的方法。