如何在Linux中直接将以太网设备连接到交换机?

9
我们有一个嵌入式板,其中以太网设备直接连接到交换机而没有phy在中间。更复杂的是,以太网设备的mdio总线连接到交换机的mdio上以进行控制。
我已经成功使用固定mdio/phy驱动程序启用了以太网,并通过将交换机的默认配置与固定phy的配置相匹配来实现。
那么,我现在应该如何连接到mdio总线以更改交换机的设置呢? 由于以太网设备附加的phy被固定phy填充,因此现在如何将真实的mdio总线连接到系统以便进行配置。 似乎没有直接的用户空间接口可用于mdio总线。我需要创建一个虚拟的以太网设备,其唯一目的是访问mdio总线,还是将其某种方式连接到以太网设备上,然后将两个mdio总线连接起来?
附注: 物理mdio总线驱动似乎能够找到交换机,但是我该如何与其通信呢?

我认为创建一个虚拟以太网设备的方法是合理的。也就是说,如果物理介质已经全部设置好了,你只需要将设备表示给网络堆栈,这样用户空间就可以访问它。 - Peter L.
1
为了回答自己的问题,我扩展了sysfs接口,使其包含每个检测到的mdio设备的寄存器接口文件00-31,现在我可以直接从用户空间读写mdio寄存器,而不需要涉及以太网设备。这个补丁只有20行左右。 - slobobaby
@slobobaby,很棒你找到了解决方案,能否为后人发布自我回答 - Abhijeet Kasurde
3个回答

6
这个补丁使我能够读写系统中检测到的所有mdio设备中的所有寄存器。
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index dc92097..668150e 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -439,8 +439,85 @@ phy_id_show(struct device *dev, struct device_attribute *attr, char *buf)
    return sprintf(buf, "0x%.8lx\n", (unsigned long)phydev->phy_id);
 }

+static ssize_t
+mdio_reg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+   struct phy_device *phydev = to_phy_device(dev);
+   struct mii_bus* bus = phydev->bus;
+   int regnum;
+   int val;
+
+   if (sscanf(attr->attr.name, "%d", &regnum) != 1)
+       return -EINVAL;
+
+   val = mdiobus_read(bus, phydev->addr, regnum);
+   if (val < 0)
+       return -EIO;
+
+   return sprintf(buf, "0x%.4x\n", val);
+}
+
+static ssize_t
+mdio_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+   struct phy_device *phydev = to_phy_device(dev);
+   struct mii_bus* bus = phydev->bus;
+   int regnum;
+   int val;
+   int err;
+
+   if (sscanf(attr->attr.name, "%d", &regnum) != 1)
+       return -EINVAL;
+
+   if (sscanf(buf, "%d", &val) != 1)
+       return -EINVAL;
+
+   if (val < 0 || val > 0xffff)
+       return -EINVAL;
+
+   err = mdiobus_write(bus, phydev->addr, regnum, val);
+   if (err < 0)
+       return -EIO;
+
+   return size;
+}
+
+#define MDIO_REG(_name) __ATTR(_name, (S_IWUSR | S_IRUGO), mdio_reg_show, mdio_reg_store)
+
 static struct device_attribute mdio_dev_attrs[] = {
    __ATTR_RO(phy_id),
+   MDIO_REG(0),
+   MDIO_REG(1),
+   MDIO_REG(2),
+   MDIO_REG(3),
+   MDIO_REG(4),
+   MDIO_REG(5),
+   MDIO_REG(6),
+   MDIO_REG(7),
+   MDIO_REG(8),
+   MDIO_REG(9),
+   MDIO_REG(10),
+   MDIO_REG(11),
+   MDIO_REG(12),
+   MDIO_REG(13),
+   MDIO_REG(14),
+   MDIO_REG(15),
+   MDIO_REG(16),
+   MDIO_REG(17),
+   MDIO_REG(18),
+   MDIO_REG(19),
+   MDIO_REG(20),
+   MDIO_REG(21),
+   MDIO_REG(22),
+   MDIO_REG(23),
+   MDIO_REG(24),
+   MDIO_REG(25),
+   MDIO_REG(26),
+   MDIO_REG(27),
+   MDIO_REG(28),
+   MDIO_REG(29),
+   MDIO_REG(30),
+   MDIO_REG(31),
    __ATTR_NULL
 };

它扩展了已经存在的sysfs接口,每个mdio设备可以包含32个寄存器地址。由于mdio设备不是物理设备,它们没有遵循phy标准,因此我必须对phy检测进行修改,以允许所有设备出现:

--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -339,9 +339,12 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
        if (r)
                return ERR_PTR(r);

+       /* BRM: this is patently not the case for our marvell switch */
+#if 0
        /* If the phy_id is mostly Fs, there is no device there */
        if ((phy_id & 0x1fffffff) == 0x1fffffff)
                return NULL;
+#endif


    dev = phy_device_create(bus, addr, phy_id, is_c45, &c45_ids);

希望这对其他人有所帮助。


0
可以使用以下补丁来扩展新内核版本中的sysfs。它可以干净地修补5.4.72。
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 5d1d63690d6e..a47fb6d06746 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -543,10 +543,123 @@ phy_has_fixups_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(phy_has_fixups);
 
+static ssize_t
+mdio_reg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+   struct phy_device *phydev = to_phy_device(dev);
+   struct mii_bus* bus = phydev->mdio.bus;
+   int regnum;
+   int val;
+
+   if (sscanf(attr->attr.name, "%d", &regnum) != 1)
+       return -EINVAL;
+
+   val = mdiobus_read(bus, phydev->mdio.addr, regnum);
+   if (val < 0)
+       return -EIO;
+
+   return sprintf(buf, "0x%.4x\n", val);
+}
+
+static ssize_t
+mdio_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+   struct phy_device *phydev = to_phy_device(dev);
+   struct mii_bus* bus = phydev->mdio.bus;
+   int regnum;
+   int val;
+   int err;
+
+   if (sscanf(attr->attr.name, "%d", &regnum) != 1)
+       return -EINVAL;
+
+   if (sscanf(buf, "%d", &val) != 1)
+       return -EINVAL;
+
+   if (val < 0 || val > 0xffff)
+       return -EINVAL;
+
+   err = mdiobus_write(bus, phydev->mdio.addr, regnum, val);
+   if (err < 0)
+       return -EIO;
+
+   return size;
+}
+
+#define __ATTR_RW_MOD(_name) __ATTR(_name, (S_IWUSR | S_IRUGO),        \
+            mdio_reg_show, mdio_reg_store)
+
+#define MDIO_REG(_name)  struct device_attribute dev_attr_##_name = __ATTR_RW_MOD(_name)
+
+static MDIO_REG(0);
+static MDIO_REG(1);
+static MDIO_REG(2);
+static MDIO_REG(3);
+static MDIO_REG(4);
+static MDIO_REG(5);
+static MDIO_REG(6);
+static MDIO_REG(7);
+static MDIO_REG(8);
+static MDIO_REG(9);
+static MDIO_REG(10);
+static MDIO_REG(11);
+static MDIO_REG(12);
+static MDIO_REG(13);
+static MDIO_REG(14);
+static MDIO_REG(15);
+static MDIO_REG(16);
+static MDIO_REG(17);
+static MDIO_REG(18);
+static MDIO_REG(19);
+static MDIO_REG(20);
+static MDIO_REG(21);
+static MDIO_REG(22);
+static MDIO_REG(23);
+static MDIO_REG(24);
+static MDIO_REG(25);
+static MDIO_REG(26);
+static MDIO_REG(27);
+static MDIO_REG(28);
+static MDIO_REG(29);
+static MDIO_REG(30);
+static MDIO_REG(31);
+
 static struct attribute *phy_dev_attrs[] = {
    &dev_attr_phy_id.attr,
    &dev_attr_phy_interface.attr,
    &dev_attr_phy_has_fixups.attr,
+   &dev_attr_0.attr,
+   &dev_attr_1.attr,
+   &dev_attr_2.attr,
+   &dev_attr_3.attr,
+   &dev_attr_4.attr,
+   &dev_attr_5.attr,
+   &dev_attr_6.attr,
+   &dev_attr_7.attr,
+   &dev_attr_8.attr,
+   &dev_attr_9.attr,
+   &dev_attr_10.attr,
+   &dev_attr_11.attr,
+   &dev_attr_12.attr,
+   &dev_attr_13.attr,
+   &dev_attr_14.attr,
+   &dev_attr_15.attr,
+   &dev_attr_16.attr,
+   &dev_attr_17.attr,
+   &dev_attr_18.attr,
+   &dev_attr_19.attr,
+   &dev_attr_20.attr,
+   &dev_attr_21.attr,
+   &dev_attr_22.attr,
+   &dev_attr_23.attr,
+   &dev_attr_24.attr,
+   &dev_attr_25.attr,
+   &dev_attr_26.attr,
+   &dev_attr_27.attr,
+   &dev_attr_28.attr,
+   &dev_attr_29.attr,
+   &dev_attr_30.attr,
+   &dev_attr_31.attr,
    NULL,
 };
 ATTRIBUTE_GROUPS(phy_dev);


0

你可以根据你的phy id编写伪phy驱动程序。通过读取phy寄存器,你可以获取phy id。这个驱动程序将为你提供连接交换机的mdio总线的句柄。这是我的驱动程序,在我的情况下,i.MX6连接到了Marvell 88E6065交换机。然后我导出了sysfs接口,通过sysfs接口我能够从用户空间配置交换机。希望这能帮助到某些人。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/phy.h>
#include <linux/asai_iotg.h>

MODULE_DESCRIPTION("psuedo phy driver");
MODULE_LICENSE("GPLv2");
MODULE_AUTHOR("jags");

static int phy_id = 0;
static u32 reg_num = 0;
static u16 data = 0;
static struct mii_bus *mvl_bus = NULL;
static struct kobject *kobj_switch;

/* Supported Device ID Tables */
static struct mdio_device_id marvell_88E6065_id[] = {
    {0x01410c89, 0xfffffc00},
    {}
};
MODULE_DEVICE_TABLE(mdio, marvell_88E6065_id);

static ssize_t switch_conf_show(struct kobject *kobj, struct kobj_attribute *attr,
        char *buf)
{
    return sprintf(buf, "%x\n", data);
}

static ssize_t  switch_conf_store(struct kobject *kobj, struct attribute *attr,
        const char *buffer, size_t size)
{
    u32 value;
    sscanf(buffer, "%x", &value);
    if (value & 0xFF000000) {
        phy_id = (value & 0xFF000000) >> 24;
        reg_num = (value & 0x00FF0000) >> 16;
        data =  (value & 0x0000FFFF);
        mdiobus_write(mvl_bus, phy_id, reg_num, data);
    }
    else {
        phy_id = (value & 0xFF00) >> 8;
        reg_num = (value & 0x00FF);
        data = mdiobus_read(mvl_bus, phy_id, reg_num);
    }
    return size;
}

static struct kobj_attribute switch_conf_attribute =
__ATTR(switch_conf, 0666, switch_conf_show, switch_conf_store);

static struct attribute *attrs_switch[] = {
    &switch_conf_attribute.attr,
    NULL,
};

static struct attribute_group attr_group_switch = {
    .attrs = attrs_switch,
};

/* Initialize the Marvell 88E6065 switch in managed mode */
static int marvell_88E6065_probe(struct phy_device *phydev)
{
    int err = 0;
    mvl_bus = phydev->bus;
    if(mvl_bus == NULL)
        return -1;

    pr_debug("Detected Marvell switch !!\n");
    pr_debug("switch id is %04x%04x\n", mdiobus_read(mvl_bus, 0x8, 0x2), mdiobus_read(mvl_bus, 0x08, 0x03));

    if(err) {
        printk(KERN_INFO "mdio write failed for marvell pseudo phy\n");
        return -1;
    }
    return 0;
}

/* PHY Driver */
static struct phy_driver marvell_88E6065_driver = {
    .phy_id     = 0x01410c89,
    .phy_id_mask    = 0xffffffff,
    .name       = "Marvell 88E6065",
    .features   = (PHY_BASIC_FEATURES),
    .flags      = PHY_HAS_INTERRUPT,
    .probe      = marvell_88E6065_probe,
    .config_aneg    = genphy_config_aneg,
    .read_status    = genphy_read_status,
    .driver     = { .owner = THIS_MODULE },
};
/*Switch initialize function */

/* Init exit */
static int __init mdio_88E6065_init()
{
    int ret = 0;
    kobj_switch = kobject_create_and_add("switch", &asai_iotg_kset->kobj);
    if (!kobj_switch)
        return -ENOMEM;
    ret = sysfs_create_group(kobj_switch, &attr_group_switch);
    if (ret) {
        kobject_put(kobj_switch);
        return ret;
    }
    return phy_driver_register(&marvell_88E6065_driver);
}

static void __exit mdio_88E6065_exit()
{
    kobject_put(kobj_switch);
    phy_driver_unregister(&marvell_88E6065_driver);
}

module_init(mdio_88E6065_init);
module_exit(mdio_88E6065_exit);                                               

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接