[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1382466229-15123-5-git-send-email-f.fainelli@gmail.com>
Date: Tue, 22 Oct 2013 11:23:49 -0700
From: "Florian Fainelli" <f.fainelli@...il.com>
To: netdev@...r.kernel.org
cc: davem@...emloft.net, s.hauer@...gutronix.de, nbd@...nwrt.org,
blogic@...nwrt.org, jogo@...nwrt.org, gary@...assoc.com,
"Florian Fainelli" <f.fainelli@...il.com>
Subject: [PATCH 4/4 net-next] net: phy: add fake switch driver
Add a fake switch driver which can be used to test both the kernel
swconfig API and user-land swconfig tool for regression and features
additions.
Signed-off-by: Florian Fainelli <f.fainelli@...il.com>
---
drivers/net/phy/Kconfig | 8 ++
drivers/net/phy/Makefile | 1 +
drivers/net/phy/swconfig-hwsim.c | 230 +++++++++++++++++++++++++++++++++++++++
3 files changed, 239 insertions(+)
create mode 100644 drivers/net/phy/swconfig-hwsim.c
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index d02ed5a..6bb940e 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -18,6 +18,14 @@ config SWCONFIG
Switch configuration API using netlink. This allows
you to configure the VLAN features of certain switches.
+config SWCONFIG_HWSIM
+ tristate "Fake switch driver"
+ depends on SWCONFIG
+ ---help---
+ Fake switch driver which simulates an 8 port Gigabit switch
+ for regression testing of the swconfig kernel and user-space
+ API.
+
comment "MII PHY device drivers"
config AT803X_PHY
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 1998034..b72405a 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -4,6 +4,7 @@ libphy-objs := phy.o phy_device.o mdio_bus.o
obj-$(CONFIG_PHYLIB) += libphy.o
obj-$(CONFIG_SWCONFIG) += swconfig.o
+obj-$(CONFIG_SWCONFIG_HWSIM) += swconfig-hwsim.o
obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_DAVICOM_PHY) += davicom.o
obj-$(CONFIG_CICADA_PHY) += cicada.o
diff --git a/drivers/net/phy/swconfig-hwsim.c b/drivers/net/phy/swconfig-hwsim.c
new file mode 100644
index 0000000..39cb141
--- /dev/null
+++ b/drivers/net/phy/swconfig-hwsim.c
@@ -0,0 +1,230 @@
+/*
+ * Simulation swconfig driver
+ *
+ * Copyright (C) 2013, Florian Fainelli <f.fainelli@...il.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/swconfig.h>
+
+struct swconfig_hwsim_port_state {
+ unsigned int speed;
+ unsigned int link;
+ unsigned int duplex;
+};
+
+#define SWCONFIG_HWSIM_NUM_PORTS 8
+
+struct swconfig_hwsim_state {
+ struct switch_dev dev;
+ struct net_device *loopback;
+ char buf[255];
+ struct swconfig_hwsim_port_state ports[SWCONFIG_HWSIM_NUM_PORTS];
+};
+
+#define get_state(_dev) container_of((_dev), struct swconfig_hwsim_state, dev)
+
+static int swconfig_hwsim_get_name(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct swconfig_hwsim_state *state = get_state(dev);
+
+ val->value.s = state->dev.name;
+
+ return 0;
+}
+
+static int swconfig_hwsim_get_port_status(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct swconfig_hwsim_state *state = get_state(dev);
+ struct swconfig_hwsim_port_state *port_state;
+ int port = val->port_vlan;
+ char *buf = state->buf;
+ int len;
+
+ port_state = &state->ports[port];
+
+ if (!port_state->link)
+ len = snprintf(buf, sizeof(state->buf), "down");
+ else
+ len = snprintf(buf, sizeof(state->buf),
+ "up, %d Mbps, %s duplex",
+ port_state->speed,
+ port_state->duplex ? "full" : "half");
+
+ buf[len] = '\0';
+
+ val->value.s = buf;
+
+ return 0;
+}
+
+static int swconfig_hwsim_get_link(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct swconfig_hwsim_state *state = get_state(dev);
+ struct swconfig_hwsim_port_state *port_state;
+ int port = val->port_vlan;
+
+ port_state = &state->ports[port];
+
+ if (port_state->link)
+ val->value.i = port_state->speed;
+ else
+ val->value.i = 0;
+
+ return 0;
+}
+
+static int swconfig_hwsim_set_link(struct switch_dev *dev,
+ const struct switch_attr *attr,
+ struct switch_val *val)
+{
+ struct swconfig_hwsim_state *state = get_state(dev);
+ struct swconfig_hwsim_port_state *port_state;
+ int port = val->port_vlan;
+
+ /* Validate user input speed */
+ switch (val->value.i) {
+ case SWITCH_PORT_SPEED_UNKNOWN:
+ case SWITCH_PORT_SPEED_10:
+ case SWITCH_PORT_SPEED_100:
+ case SWITCH_PORT_SPEED_1000:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ port_state = &state->ports[port];
+
+ if (val->value.i == SWITCH_PORT_SPEED_UNKNOWN)
+ port_state->link = 0;
+ else {
+ port_state->link = 1;
+ port_state->speed = val->value.i;
+ }
+
+ return 0;
+}
+
+enum swconfig_hwsim_port {
+ SWCONFIG_HWSIM_PORT_STATUS,
+ SWCONFIG_HWSIM_PORT_LINK,
+};
+
+static const struct switch_attr swconfig_hwsim_attr_port[] = {
+ [SWCONFIG_HWSIM_PORT_STATUS] = {
+ .id = SWCONFIG_HWSIM_PORT_STATUS,
+ .type = SWITCH_TYPE_STRING,
+ .description = "Returns the port status",
+ .name = "status",
+ .get = swconfig_hwsim_get_port_status,
+ },
+ [SWCONFIG_HWSIM_PORT_LINK] = {
+ .id = SWCONFIG_HWSIM_PORT_LINK,
+ .type = SWITCH_TYPE_INT,
+ .description = "Sets link speed",
+ .name = "link",
+ .get = swconfig_hwsim_get_link,
+ .set = swconfig_hwsim_set_link,
+ },
+};
+
+enum swconfig_hwsim_globals {
+ SWCONFIG_HWSIM_GET_NAME,
+};
+
+static const struct switch_attr swconfig_hwsim_attr_global[] = {
+ [SWCONFIG_HWSIM_GET_NAME] = {
+ .id = SWCONFIG_HWSIM_GET_NAME,
+ .type = SWITCH_TYPE_STRING,
+ .description = "Returns the name of the switch",
+ .name = "name",
+ .get = swconfig_hwsim_get_name,
+ },
+};
+
+static int swconfig_hwsim_apply_config(struct switch_dev *dev)
+{
+ return 0;
+}
+
+static int swconfig_hwsim_reset_switch(struct switch_dev *dev)
+{
+ struct swconfig_hwsim_state *state = get_state(dev);
+ unsigned int i;
+
+ for (i = 0; i < SWCONFIG_HWSIM_NUM_PORTS; i++) {
+ state->ports[i].speed = 1000;
+ state->ports[i].link = 1;
+ state->ports[i].duplex = 1;
+ }
+
+ return 0;
+}
+
+static const struct switch_dev_ops swconfig_hwsim_ops = {
+ .attr_global = {
+ .attr = swconfig_hwsim_attr_global,
+ .n_attr = ARRAY_SIZE(swconfig_hwsim_attr_global),
+ },
+
+ .attr_port = {
+ .attr = swconfig_hwsim_attr_port,
+ .n_attr = ARRAY_SIZE(swconfig_hwsim_attr_port),
+ },
+
+ .apply_config = swconfig_hwsim_apply_config,
+ .reset_switch = swconfig_hwsim_reset_switch,
+};
+
+static struct swconfig_hwsim_state swconfig_hwsim_state = {
+ .dev = {
+ .name = "Fake switch",
+ .ops = &swconfig_hwsim_ops,
+ .ports = SWCONFIG_HWSIM_NUM_PORTS,
+ .cpu_port = SWCONFIG_HWSIM_NUM_PORTS - 1,
+ },
+};
+
+int swconfig_hwsim_init(void)
+{
+ swconfig_hwsim_state.loopback = dev_get_by_name(&init_net, "lo");
+ if (!swconfig_hwsim_state.loopback)
+ return -ENODEV;
+
+
+ return register_switch(&swconfig_hwsim_state.dev,
+ swconfig_hwsim_state.loopback);
+}
+
+void swconfig_hwsim_exit(void)
+{
+ unregister_switch(&swconfig_hwsim_state.dev);
+ dev_put(swconfig_hwsim_state.loopback);
+}
+
+module_init(swconfig_hwsim_init);
+module_exit(swconfig_hwsim_exit);
+
+MODULE_AUTHOR("Florian Fainelli <f.fainelli@...il.com>");
+MODULE_DESCRIPTION("Fake switch driver");
+MODULE_LICENSE("Dual BSD/GPL");
--
1.8.3.2
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists