lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1560329827-6345-2-git-send-email-Raju.Lakkaraju@microchip.com>
Date:   Wed, 12 Jun 2019 14:27:06 +0530
From:   Raju Lakkaraju <Raju.Lakkaraju@...rochip.com>
To:     netdev@...r.kernel.org
Cc:     UNGLinuxDriver@...rochip.com, f.fainelli@...il.com, andrew@...n.ch,
        Raju Lakkaraju <Raju.Lakkaraju@...rochip.com>
Subject: [RFC, net-next v0 1/2] net: phy: mscc: Add PHY Netlink Interface along with Cable Diagnostics command

From: Raju Lakkaraju <Raju.Lakkaraju@...rochip.com>

Add the PHY Netlink interface driver to implement the cable
diagnostics of Microsemi Ethernet PHYs.

Signed-off-by: Raju Lakkaraju <Raju.Lakkaraju@...rochip.com>
---
 drivers/net/phy/Kconfig       |   6 ++
 drivers/net/phy/Makefile      |   1 +
 drivers/net/phy/phy.c         |  17 ++++
 drivers/net/phy/phy_netlink.c | 226 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/phy.h           |  88 ++++++++++++++++
 include/linux/phy_netlink.h   |  48 +++++++++
 6 files changed, 386 insertions(+)
 create mode 100644 drivers/net/phy/phy_netlink.c
 create mode 100644 include/linux/phy_netlink.h

diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index db5645b..a105058 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -406,6 +406,12 @@ config MICROCHIP_T1_PHY
 	---help---
 	  Supports the LAN87XX PHYs.
 
+config PHY_NETLINK
+        tristate "PHY Netlink Interface"
+    depends on NET
+        ---help---
+          Supports the PHY netlink interface.
+
 config MICROSEMI_PHY
 	tristate "Microsemi PHYs"
 	---help---
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index bac339e..96aad9b 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_MICREL_KS8995MA)	+= spi_ks8995.o
 obj-$(CONFIG_MICREL_PHY)	+= micrel.o
 obj-$(CONFIG_MICROCHIP_PHY)	+= microchip.o
 obj-$(CONFIG_MICROCHIP_T1_PHY)	+= microchip_t1.o
+obj-$(CONFIG_PHY_NETLINK)	+= phy_netlink.o
 obj-$(CONFIG_MICROSEMI_PHY)	+= mscc.o
 obj-$(CONFIG_NATIONAL_PHY)	+= national.o
 obj-$(CONFIG_NXP_TJA11XX_PHY)	+= nxp-tja11xx.o
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index d915076..29b2921 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1251,3 +1251,20 @@ int phy_ethtool_nway_reset(struct net_device *ndev)
 	return phy_restart_aneg(phydev);
 }
 EXPORT_SYMBOL(phy_ethtool_nway_reset);
+
+int phy_cabdiag_request(struct net_device *ndev, struct phy_cabdiag_req *cfg)
+{
+	struct phy_device *phydev = ndev->phydev;
+
+	if (!phydev)
+		return -ENODEV;
+
+	if (!phydev->drv)
+		return -EIO;
+
+	if (phydev->drv->request_cable_diag)
+		return phydev->drv->request_cable_diag(phydev, cfg);
+
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(phy_cabdiag_request);
diff --git a/drivers/net/phy/phy_netlink.c b/drivers/net/phy/phy_netlink.c
new file mode 100644
index 0000000..896c1b2
--- /dev/null
+++ b/drivers/net/phy/phy_netlink.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2019 Microchip Technology
+
+#include <linux/bitmap.h>
+#include <linux/rtnetlink.h>
+#include <net/sock.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+#include <net/sock.h>
+#include <linux/phy_netlink.h>
+
+static struct genl_family phynl_genl_family;
+
+static const struct nla_policy cabdiag_op_policy[CABDIAG_OP_ATTR_MAX + 1] = {
+	[CABDIAG_OP_ATTR_NOOP]     = { .type = NLA_UNSPEC },
+	[CABDIAG_OP_ATTR_REQUEST]  = { .type = NLA_NESTED },
+};
+
+static const struct nla_policy
+cabdiag_pair_sta_policy[CABDIAG_PAIR_STA_ATTR_MAX + 1] = {
+	[CABDIAG_PAIR_STA_ATTR_NOOP]     = { .type = NLA_UNSPEC },
+	[CABDIAG_PAIR_STA_ATTR_STATUS]   = { .type = NLA_U8 },
+	[CABDIAG_PAIR_STA_ATTR_LENGTH]   = { .type = NLA_U8 },
+};
+
+static const struct nla_policy cabdiag_req_policy[CABDIAG_REQ_ATTR_MAX + 1] = {
+	[CABDIAG_REQ_ATTR_NOOP]       = { .type = NLA_UNSPEC },
+	[CABDIAG_REQ_ATTR_IFNAME]     = { .type = NLA_STRING },
+	[CABDIAG_REQ_ATTR_OP_STA]     = { .type = NLA_U8 },
+	[CABDIAG_REQ_ATTR_PAIRS_MASK] = { .type = NLA_U8 },
+	[CABDIAG_REQ_ATTR_TIMEOUT]    = { .type = NLA_U8 },
+	[CABDIAG_REQ_ATTR_STATUS]     =
+	NLA_POLICY_NESTED_ARRAY(cabdiag_pair_sta_policy),
+};
+
+static int phynl_interface_check(struct genl_info *info, const char *ifname)
+{
+	struct net *net = genl_info_net(info);
+	struct net_device *netdev;
+	int ret = 0;
+
+	netdev = dev_get_by_name(net, ifname);
+	if (netdev) {
+		if (netdev->phydev) {
+			if (!netdev->phydev->drv) {
+				pr_err("PHYNL: netdev->phydev->drv == NULL\n");
+				ret = -EINVAL;
+				goto out_dev_put;
+			}
+		} else {
+			pr_err("PHYNL: netdev->phydev == NULL\n");
+			ret = -EINVAL;
+			goto out_dev_put;
+		}
+	} else {
+		pr_err("PHYNL: can't find net device\n");
+		return -ENODEV;
+	}
+
+out_dev_put:
+	dev_put(netdev);
+
+	return ret;
+}
+
+static int phynl_cabdiag_request(struct genl_info *info, struct nlattr *nest)
+{
+	struct nlattr *tb[CABDIAG_REQ_ATTR_MAX + 1];
+	struct net *net = genl_info_net(info);
+	struct phy_cabdiag_req cfg;
+	struct net_device *netdev;
+	struct sk_buff *msg;
+	__be64 val_64;
+	char *ifname;
+	void *hdr;
+	int ret;
+
+	if (!nest) {
+		pr_err("PHYNL: nelink message error\n");
+		return -EINVAL;
+	}
+
+	memset(&cfg, 0, sizeof(struct phy_cabdiag_req));
+	ret = nla_parse_nested(tb, CABDIAG_REQ_ATTR_MAX, nest,
+			       cabdiag_req_policy, info->extack);
+	if (ret < 0)
+		return ret;
+
+	ifname = (char *)nla_data(tb[CABDIAG_REQ_ATTR_IFNAME]);
+	if (tb[CABDIAG_REQ_ATTR_IFNAME]) {
+		ret = phynl_interface_check(info, ifname);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (tb[CABDIAG_REQ_ATTR_PAIRS_MASK])
+		cfg.pairs_bitmask =
+		nla_get_u8(tb[CABDIAG_REQ_ATTR_PAIRS_MASK]);
+
+	if (tb[CABDIAG_REQ_ATTR_TIMEOUT])
+		cfg.timeout_cnt = nla_get_u8(tb[CABDIAG_REQ_ATTR_TIMEOUT]);
+
+	netdev = dev_get_by_name(net, ifname);
+
+	/* Initialize to default status */
+	cfg.pairs[CABDIAG_PAIR_A].length = CD_LENGTH_NOT_SUPPORTED;
+	cfg.pairs[CABDIAG_PAIR_B].length = CD_LENGTH_NOT_SUPPORTED;
+	cfg.pairs[CABDIAG_PAIR_C].length = CD_LENGTH_NOT_SUPPORTED;
+	cfg.pairs[CABDIAG_PAIR_D].length = CD_LENGTH_NOT_SUPPORTED;
+	cfg.pairs[CABDIAG_PAIR_A].status = CD_STATUS_NOT_SUPPORTED;
+	cfg.pairs[CABDIAG_PAIR_B].status = CD_STATUS_NOT_SUPPORTED;
+	cfg.pairs[CABDIAG_PAIR_C].status = CD_STATUS_NOT_SUPPORTED;
+	cfg.pairs[CABDIAG_PAIR_D].status = CD_STATUS_NOT_SUPPORTED;
+	ret = phy_cabdiag_request(netdev, &cfg);
+	if (ret < 0)
+		goto out_dev_put;
+
+	/* Reply back */
+	msg = genlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg) {
+		ret = -ENOMEM;
+		goto out_dev_put;
+	}
+
+	hdr = genlmsg_put_reply(msg, info, &phynl_genl_family, 0,
+				PHYNL_CMD_CABDIAG);
+
+	nest = nla_nest_start(msg, CABDIAG_OP_ATTR_REQUEST | NLA_F_NESTED);
+	nla_put_u8(msg, CABDIAG_REQ_ATTR_OP_STA, cfg.op_status);
+	nla_put_u8(msg, CABDIAG_REQ_ATTR_PAIRS_MASK, cfg.pairs_bitmask);
+	nla_put_u8(msg, CABDIAG_REQ_ATTR_TIMEOUT, cfg.timeout_cnt);
+	val_64 = ((__be64)cfg.pairs[CABDIAG_PAIR_D].length << 56 |
+		  (__be64)cfg.pairs[CABDIAG_PAIR_D].status << 48 |
+		  (__be64)cfg.pairs[CABDIAG_PAIR_C].length << 40 |
+		  (__be64)cfg.pairs[CABDIAG_PAIR_C].status << 32 |
+		  (__be64)cfg.pairs[CABDIAG_PAIR_B].length << 24 |
+		  (__be64)cfg.pairs[CABDIAG_PAIR_B].status << 16 |
+		  (__be64)cfg.pairs[CABDIAG_PAIR_A].length << 8  |
+		  cfg.pairs[CABDIAG_PAIR_A].status);
+	nla_put_be64(msg, CABDIAG_REQ_ATTR_STATUS, val_64, 4);
+	nla_nest_end(msg, nest);
+
+	genlmsg_end(msg, hdr);
+	genlmsg_reply(msg, info);
+
+out_dev_put:
+	if (netdev)
+		dev_put(netdev);
+
+	return ret;
+}
+
+static int phynl_cabdiag_doit(struct sk_buff *skb, struct genl_info *info)
+{
+	struct nlattr *tb[CABDIAG_OP_ATTR_MAX + 1];
+	int ret;
+
+	ret = genlmsg_parse(info->nlhdr, &phynl_genl_family, tb,
+			    CABDIAG_OP_ATTR_MAX, cabdiag_op_policy,
+			    info ? info->extack : NULL);
+	if (ret < 0) {
+		pr_err("PHYNL: genlmsg_parse returns:%d\n", ret);
+		return ret;
+	}
+
+	if (tb[CABDIAG_OP_ATTR_REQUEST]) {
+		ret = phynl_cabdiag_request(info, tb[CABDIAG_OP_ATTR_REQUEST]);
+		if (ret < 0)
+			return ret;
+	}
+
+	return 0;
+}
+
+#define PHYNL_GENL_NAME	"phynl"
+#define	PHYNL_GENL_VERSION	1
+#define	PHYNL_MCGRP_MONITOR	"phy_monitor"
+static struct genl_multicast_group phynl_genl_mcgroups[] = {
+	{
+		.name = PHYNL_MCGRP_MONITOR
+	},
+};
+
+static const struct genl_ops phynl_genl_ops[] = {
+	{
+		.cmd    = PHYNL_CMD_CABDIAG,
+		.doit   = phynl_cabdiag_doit,
+	},
+};
+
+static struct genl_family phynl_genl_family = {
+	.hdrsize	= 0,
+	.name		= PHYNL_GENL_NAME,
+	.version	= PHYNL_GENL_VERSION,
+	.netnsok	= true,
+	.parallel_ops	= false,
+	.ops		= phynl_genl_ops,
+	.n_ops		= ARRAY_SIZE(phynl_genl_ops),
+	.mcgrps		= phynl_genl_mcgroups,
+	.n_mcgrps	= ARRAY_SIZE(phynl_genl_mcgroups),
+};
+
+static int __init phynl_genl_init(void)
+{
+	int ret;
+
+	ret = genl_register_family(&phynl_genl_family);
+	if (ret < 0)
+		panic("PHYNL: Could not register genetlink family\n");
+
+	return 0;
+}
+
+static void __exit phynl_genl_exit(void)
+{
+	genl_unregister_family(&phynl_genl_family);
+}
+
+module_init(phynl_genl_init);
+module_exit(phynl_genl_exit);
+
+MODULE_DESCRIPTION("PHY Netlink Interface driver");
+MODULE_AUTHOR("Nagaraju Lakkaraju");
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/phy.h b/include/linux/phy.h
index d0af7d3..b045ddf 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -320,6 +320,91 @@ struct phy_c45_device_ids {
 	u32 device_ids[8];
 };
 
+#define CABDIAG_PAIR_A_MASK 0x0001
+#define CABDIAG_PAIR_B_MASK 0x0002
+#define CABDIAG_PAIR_C_MASK 0x0004
+#define CABDIAG_PAIR_D_MASK 0x0008
+enum phy_cabdiag_pairs {
+	CABDIAG_PAIR_A,
+	CABDIAG_PAIR_B,
+	CABDIAG_PAIR_C,
+	CABDIAG_PAIR_D,
+
+	CABDIAG_PAIR_CNT,
+};
+
+/**
+ * phy_cabdiag_op_sta - Cable diagnostics operational status
+ * CD_REQ_NONE                    - Unknown
+ * CD_REQ_INVALID_PAIR_MASK       - Invalid pair mask
+ * CD_REQ_INVALID_TIMEOUT         - Invalid Timeout counter
+ * CD_REQ_REJECTED_BUSY           - Previous command execution busy
+ * CD_STATUS_SUCCESS              - Operation success
+ * CD_STATUS_FAILED_TIMEOUT       - Failed due to Timeout
+ * CD_STATUS_FAILED_INVALID       - Status results invalid
+ */
+enum phy_cabdiag_op_sta {
+	CD_REQ_NONE,
+	CD_REQ_INVALID_PAIR_MASK,
+	CD_REQ_INVALID_TIMEOUT,
+	CD_REQ_REJECTED_BUSY,
+	CD_STATUS_SUCCESS,
+	CD_STATUS_FAILED_TIMEOUT,
+	CD_STATUS_FAILED_INVALID
+};
+
+#define CD_LENGTH_NOT_SUPPORTED 0xFF
+#define CD_STATUS_NOT_SUPPORTED 0xFF
+
+/**
+ * phy_cabdiag_sta_code - Cable diagnostics fault codes
+ * b0000 - 0100: Individual cable pair fault codes
+ * b10xx       : Cross pair short to pair 'xx'
+ * b11xx       : Abnormal Cross pair coupling with pair 'xx'
+ *    xx       : b00 - Pair-A,
+ *             : b01 - Pair-B,
+ *             : b10 - Pair-C,
+ *             : b11 - Pair-D
+ */
+enum phy_cabdiag_sta_code {
+	CD_NORMAL_PAIR            = 0x0,
+	CD_OPEN_PAIR              = 0x1,
+	CD_SHORTED_PAIR           = 0x2,
+	CD_ABNORMAL_TERMINATION   = 0x4,
+	CD_X_PAIR_SHORT_TO_PAIR_A = 0x8,
+	CD_X_PAIR_SHORT_TO_PAIR_B = 0x9,
+	CD_X_PAIR_SHORT_TO_PAIR_C = 0xA,
+	CD_X_PAIR_SHORT_TO_PAIR_D = 0xB,
+	CD_ABNORMAL_X_PAIR_A      = 0xC,
+	CD_ABNORMAL_X_PAIR_B      = 0xD,
+	CD_ABNORMAL_X_PAIR_C      = 0xE,
+	CD_ABNORMAL_X_PAIR_D      = 0xF
+};
+
+/**
+ * struct phy_cabdiag_pair_sta - PHY diagnostics pair status
+ * @status: Fault codes
+ * @length: Length in meters
+ */
+struct phy_cabdiag_pair_sta {
+	enum phy_cabdiag_sta_code status;
+	u8 length;
+};
+
+/**
+ * struct phy_cabdiag_req - PHY diagnostics request/status command
+ * @op_status: Cable Diagnostics Operational status
+ * @pairs_bitmask: Allows settings diag request just for a pair
+ * @timeout_cnt: Timeout count (i.e. Multiples of driver wait time)
+ * @pairs[CABDIAG_PAIR_CNT]: Status of 4 pairs of cable
+ */
+struct phy_cabdiag_req {
+	enum phy_cabdiag_op_sta op_status;
+	u8 pairs_bitmask;
+	u8 timeout_cnt;
+	struct phy_cabdiag_pair_sta pairs[CABDIAG_PAIR_CNT];
+};
+
 /* phy_device: An instance of a PHY
  *
  * drv: Pointer to the driver for this PHY instance
@@ -621,6 +706,8 @@ struct phy_driver {
 			    struct ethtool_tunable *tuna,
 			    const void *data);
 	int (*set_loopback)(struct phy_device *dev, bool enable);
+	int (*request_cable_diag)(struct phy_device *dev,
+				  struct phy_cabdiag_req *cfg);
 };
 #define to_phy_driver(d) container_of(to_mdio_common_driver(d),		\
 				      struct phy_driver, mdiodrv)
@@ -1170,6 +1257,7 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev,
 int phy_ethtool_set_link_ksettings(struct net_device *ndev,
 				   const struct ethtool_link_ksettings *cmd);
 int phy_ethtool_nway_reset(struct net_device *ndev);
+int phy_cabdiag_request(struct net_device *ndev, struct phy_cabdiag_req *cfg);
 
 #if IS_ENABLED(CONFIG_PHYLIB)
 int __init mdio_bus_init(void);
diff --git a/include/linux/phy_netlink.h b/include/linux/phy_netlink.h
new file mode 100644
index 0000000..2823d67
--- /dev/null
+++ b/include/linux/phy_netlink.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */
+#ifndef __PHY_NETLINK_H
+#define __PHY_NETLINK_H
+
+#include <linux/rtnetlink.h>
+#include <linux/phy.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+
+/* Genetlink setup */
+enum {
+	PHYNL_CMD_NOOP,
+	PHYNL_CMD_CABDIAG,
+
+	__PHYNL_CMD_CNT,
+	PHYNL_CMD_MAX = (__PHYNL_CMD_CNT - 1)
+};
+
+enum {
+	CABDIAG_OP_ATTR_NOOP,
+	CABDIAG_OP_ATTR_REQUEST,
+
+	__CABDIAG_OP_ATTR_CNT,
+	CABDIAG_OP_ATTR_MAX = (__CABDIAG_OP_ATTR_CNT - 1)
+};
+
+enum {
+	CABDIAG_PAIR_STA_ATTR_NOOP,
+	CABDIAG_PAIR_STA_ATTR_STATUS,
+	CABDIAG_PAIR_STA_ATTR_LENGTH,
+
+	__CABDIAG_PAIR_STA_ATTR_CNT,
+	CABDIAG_PAIR_STA_ATTR_MAX = (__CABDIAG_PAIR_STA_ATTR_CNT - 1)
+};
+
+enum {
+	CABDIAG_REQ_ATTR_NOOP,
+	CABDIAG_REQ_ATTR_IFNAME,
+	CABDIAG_REQ_ATTR_OP_STA,
+	CABDIAG_REQ_ATTR_PAIRS_MASK,
+	CABDIAG_REQ_ATTR_TIMEOUT,
+	CABDIAG_REQ_ATTR_STATUS,
+
+	__CABDIAG_REQ_ATTR_CNT,
+	CABDIAG_REQ_ATTR_MAX = (__CABDIAG_REQ_ATTR_CNT - 1)
+};
+
+#endif /* __PHY_NETLINK_H */
-- 
2.7.4

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ