tarjeta - tag vlan linux
¿Cómo conectar un dispositivo ethernet directamente a un switch en Linux? (2)
Tenemos una placa integrada donde el dispositivo ethernet está conectado directamente a un interruptor sin una phy en el medio. Para hacer las cosas más complicadas, el bus mdio del dispositivo ethernet está conectado al mdio del conmutador para su control.
He logrado utilizar el controlador fijo mdio / phy para habilitar ethernet y eso funciona al hacer coincidir la configuración predeterminada del switch con las ph fijas.
¿Cómo me conecto ahora al bus mdio para cambiar la configuración del interruptor? Dado que el phy adjunto del dispositivo de ethernet está ocupado por el ph fijo, ¿cómo puedo ahora conectar el bus de mdio real al sistema para poder configurarlo? Parece que no hay una interfaz directa de espacio de usuario para un bus mdio. ¿Creo un dispositivo falso de Ethernet cuyo único propósito es acceder al bus mdio o de alguna manera lo conecto al dispositivo ethernet que luego tendrá dos buses mdio conectados?
PD: Parece que el controlador físico del mdio bus encuentra el interruptor, pero ¿cómo hablo con él?
Este parche me permite leer y escribir todos los registros en los dispositivos mdio detectados en un sistema.
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
};
Extiende la interfaz sysfs ya presente con las 32 direcciones de registro que cada dispositivo mdio puede contener. Como los dispositivos mdio no eran físicos, no seguían el estándar phy, así que tuve que hackear la detección phy para permitir que aparecieran todos los dispositivos:
--- 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);
Espero que esto sea útil para otra persona.
Puede escribir el controlador pseudo phy basado en su identificación phy. Puede obtener su identificación leyendo los registros phy. Este controlador le dará manejo al bus mdio al que está conectado el interruptor. Este es mi controlador, en mi caso, i.MX6 estaba conectado al conmutador Marvell 88E6065. Luego exporté la interfaz sysfs y pude configurar el cambio desde el espacio de usuario a través de la interfaz sysfs. Espero que esto ayude a alguien.
#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);