如何在linux中将以太网设备直接连接到交换机?
我们有一个嵌入式板,以太网设备直接连接到交换机,中间没有物理层。为了使事情更加复杂,以太网设备的mdio总线连接到交换机的mdio进行控制 我已经设法使用固定mdio/phy驱动程序来启用以太网,并且通过将交换机的默认配置与固定phy相匹配来实现 现在如何连接到mdio总线以更改交换机设置? 由于以太网设备的连接phy由固定phy填充,我现在如何将真正的mdio总线连接到系统,以便对其进行配置。 mdio总线似乎没有直接的用户空间接口。我是否创建了一个假的以太网设备,其唯一目的是访问mdio总线,或者我是否以某种方式将其连接到将连接两条mdio总线的以太网设备 附言:如何在linux中将以太网设备直接连接到交换机?,linux,linux-kernel,linux-device-driver,embedded-linux,ethernet,Linux,Linux Kernel,Linux Device Driver,Embedded Linux,Ethernet,我们有一个嵌入式板,以太网设备直接连接到交换机,中间没有物理层。为了使事情更加复杂,以太网设备的mdio总线连接到交换机的mdio进行控制 我已经设法使用固定mdio/phy驱动程序来启用以太网,并且通过将交换机的默认配置与固定phy相匹配来实现 现在如何连接到mdio总线以更改交换机设置? 由于以太网设备的连接phy由固定phy填充,我现在如何将真正的mdio总线连接到系统,以便对其进行配置。 mdio总线似乎没有直接的用户空间接口。我是否创建了一个假的以太网设备,其唯一目的是访问mdio总线
物理mdio总线驱动程序似乎找到了交换机,但我如何与之通信?此补丁允许我读取和写入检测到的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
};
希望这对其他人有帮助。您可以根据您的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);
#包括
#包括
#包括
#包括
模块描述(“psuedo phy驱动程序”);
模块许可证(“GPLv2”);
模块作者(“jags”);
静态int phy_id=0;
静态u32 reg_num=0;
静态u16数据=0;
静态结构mii_总线*mvl_总线=NULL;
静态结构kobject*kobj_开关;
/*支持的设备ID表*/
静态结构mdio_设备_id marvell_88E6065_id[]{
{0x01410c89,0xfffffc00},
{}
};
模块设备表(mdio,marvell 88E6065\u id);
静态ssize_t switch_conf_show(struct kobject*kobj,struct kobj_attribute*attr,
字符*buf)
{
返回sprintf(buf,“%x\n”,数据);
}
静态ssize_t switch_conf_store(struct kobject*kobj,struct attribute*attr,
常量字符*缓冲区,大小(大小)
{
u32值;
sscanf(缓冲区、%x、&value);
if(值&0xFF000000){
phy_id=(值&0xFF000000)>>24;
reg_num=(值&0x00FF0000)>>16;
数据=(值和0x0000FFFF);
mdiobus_写入(mvl_总线、物理标识、注册号、数据);
}
否则{
phy_id=(值&0xFF00)>>8;
reg_num=(值&0x00FF);
数据=mdiobus_读取(mvl_总线、phy_id、reg_num);
}
返回大小;
}
静态结构kobj_属性开关_配置_属性=
__属性(开关配置,0666,开关配置显示,开关配置存储);
静态结构属性*attrs_开关[]={
&开关_conf_attribute.attr,
无效的
};
静态结构属性\u组属性\u组\u开关={
.attrs=attrs\u开关,
};
/*在管理模式下初始化Marvell 88E6065交换机*/
静态int marvell_88E6065_探测器(结构物理设备*phydev)
{
int err=0;
mvl_总线=phydev->bus;
如果(mvl_总线==NULL)
返回-1;
pr_调试(“检测到Marvell开关!!\n”);
pr_调试(“交换机id为%04x%04x\n”,mdiobus_读取(mvl_总线,0x8,0x2),mdiobus_读取(mvl_总线,0x08,0x03));
如果(错误){
printk(KERN_INFO“marvell伪物理的mdio写入失败\n”);
返回-1;
}
返回0;
}
/*物理层驱动器*/
静态结构物理驱动程序marvell\u 88E6065\u驱动程序={
.phy_id=0x01410c89,
.phy\u id\u mask=0xffffffff,
.name=“Marvell 88E6065”,
.features=(物理物理基本功能),
.flags=物理层有中断,
.probe=marvell_88E6065_探头,
.config\u aneg=genphy\u config\u aneg,
.read_status=genphy_read_status,
.driver={.owner=此_模块},
};
/*开关初始化功能*/
/*初始出口*/
静态int_uuuinit mdio_88E6065_init()
{
int-ret=0;
kobj_switch=kobject_create_and_add(“switch”、&asai_iotg_kset->kobj);
如果(!kobj_开关)
return-ENOMEM;
ret=sysfs\u创建组(kobj\u开关和attr\u组开关);
如果(ret){
kobject_put(kobj_开关);
返回ret;
}
返回phy_驱动程序_寄存器(&marvell_88E6065_驱动程序);
}
静态无效uuu退出mdio_88E6065_u退出()
{
kobject_put(kobj_开关);
phy_驱动程序_注销(&marvell_88E6065_驱动程序);
}
模块初始化(mdio 88E6065初始化);
模块出口(mdio 88E6065出口);
我认为创建假以太网设备的方法似乎是合理的。就是,如果物理介质已全部设置,您需要做的只是将设备表示到网络堆栈中,以便用户空间可以访问它。为了回答我自己的问题,我已扩展了mdio的sysfs接口,以包含每个检测到的mdio设备的寄存器接口文件00-31,现在我可以直接读取和写入mdio寄存器,而无需进行任何修改涉及来自用户空间的以太网设备。这个补丁只有20行左右。@Slobobobbaby,你找到了解决方案真是太好了,你能为后代发帖吗?
#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);