Как подключить Ethernet-устройство напрямую к коммутатору в Linux?
У нас есть встроенная плата, в которой устройство Ethernet напрямую подключено к коммутатору без промежуточного уровня. Чтобы усложнить задачу, шина mdio Ethernet-устройства подключается к mdio коммутатора для управления.
Мне удалось использовать фиксированный драйвер mdio/phy для включения Ethernet, и это работает путем сопоставления конфигурации коммутатора по умолчанию с фиксированными phy.
Как мне теперь подключиться к шине mdio, чтобы изменить настройки переключателя? Так как присоединенный phy устройства Ethernet заполнен фиксированным phy, как мне теперь подключить реальную шину mdio к системе, чтобы я мог ее настроить. Кажется, что нет прямого интерфейса пользовательского пространства к шине mdio. Создаю ли я поддельное устройство Ethernet, единственная цель которого - получить доступ к шине mdio, или мне как-то подключить его к устройству ethernet, к которому затем будут подключены две шины mdio?
PS: похоже, что физический драйвер шины mdio находит коммутатор, но как мне с ним разговаривать?
3 ответа
Этот патч позволяет мне читать и записывать все регистры на устройствах 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", ®num) != 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", ®num) != 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 с 32 адресами регистров, которые может содержать каждое устройство mdio. Поскольку устройства 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);
Надеюсь, что это полезно для кого-то еще.
Вы можете написать псевдо phy драйвер на основе вашего phy id. Вы можете получить phy id, прочитав регистры phy. Этот драйвер даст вам управление шиной 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);
Можно использовать следующий патч для расширения 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", ®num) != 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", ®num) != 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);