From: Tobias Waldekranz <tobias@waldekranz.com>
To: Sean Anderson <sean.anderson@seco.com>,
Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
netdev@vger.kernel.org
Cc: "David S . Miller" <davem@davemloft.net>,
Vladimir Oltean <olteanv@gmail.com>,
linux-kernel@vger.kernel.org, Paolo Abeni <pabeni@redhat.com>,
Eric Dumazet <edumazet@google.com>,
Florian Fainelli <f.fainelli@gmail.com>,
Jakub Kicinski <kuba@kernel.org>,
Sean Anderson <sean.anderson@seco.com>
Subject: Re: [PATCH net-next] net: mdio: Add netlink interface
Date: Tue, 07 Mar 2023 13:26:30 +0100 [thread overview]
Message-ID: <874jqwkart.fsf@waldekranz.com> (raw)
In-Reply-To: <20230306204517.1953122-1-sean.anderson@seco.com>
On mån, mar 06, 2023 at 15:45, Sean Anderson <sean.anderson@seco.com> wrote:
> This adds a netlink interface to make reads/writes to mdio buses. This
> makes it easier to debug devices. This is especially useful when there
> is a PCS involved (and the ethtool reads are faked), when there is no
> MAC associated with a PHY, or when the MDIO device is not a PHY.
>
> The closest existing in-kernel interfaces are the SIOCG/SMIIREG ioctls, but
> they have several drawbacks:
>
> 1. "Write register" is not always exactly that. The kernel will try to
> be extra helpful and do things behind the scenes if it detects a
> write to the reset bit of a PHY for example.
>
> 2. Only one op per syscall. This means that is impossible to implement
> many operations in a safe manner. Something as simple as a
> read/mask/write cycle can race against an in-kernel driver.
>
> 3. Addressing is awkward since you don't address the MDIO bus
> directly, rather "the MDIO bus to which this netdev's PHY is
> connected". This makes it hard to talk to devices on buses to which
> no PHYs are connected, the typical case being Ethernet switches.
>
> To address these shortcomings, this adds a GENL interface with which a user
> can interact with an MDIO bus directly. The user sends a program that
> mdio-netlink executes, possibly emitting data back to the user. I.e. it
> implements a very simple VM. Read/mask/write operations could be
> implemented by dedicated commands, but when you start looking at more
> advanced things like reading out the VLAN database of a switch you need
> state and branching.
>
> To prevent userspace phy drivers, writes are disabled by default, and can
> only be enabled by editing the source. This is the same strategy used by
> regmap for debugfs writes. Unfortunately, this disallows several useful
> features, including
>
> - Register writes (obviously)
> - C45-over-C22
> - Atomic access to paged registers
> - Better MDIO emulation for e.g. QEMU
>
> However, the read-only interface remains broadly useful for debugging.
> Users who want to use the above features can re-enable them by defining
> MDIO_NETLINK_ALLOW_WRITE and recompiling their kernel.
What about taking a page from the BPF playbook and require all loaded
programs (MDIO_GENL_XFERs) to be licensed under GPL? That would mean
that the userspace program that generated it would also have to be
GPLed.
My view has always been that a vendor looking to build a userspace SDK
won't be deterred by this limitation. They can easily build
mdio-netlink.ko from mdio-tools and use that to drive it, or (more
likely) they already have their own implementation that they are stuck
with for legacy reasons. In other words: we are only punishing
legitimate users (mdio-tools being one of them, IMO).
Perhaps with this approach we could have our cake and eat it too.
> Signed-off-by: Sean Anderson <sean.anderson@seco.com>
> ---
> This driver was written by Tobias Waldekranz. It is adapted from the
> version he released with mdio-tools [1]. This was last discussed 2.5
> years ago [2], and I have incorperated his cover letter into this commit
> message. The discussion mainly centered around the write capability
> allowing for userspace phy drivers. Although it comes with reduced
> functionality, I hope this new approach satisfies Andrew. I have also
> made several minor changes for style and to stay abrest of changing
> APIs.
>
> Tobias, I've taken the liberty of adding some copyright notices
> attributing this to you.
Fine by me :)
> [1] https://github.com/wkz/mdio-tools
> [2] https://lore.kernel.org/netdev/C42DZQLTPHM5.2THDSRK84BI3T@wkz-x280/
>
> drivers/net/mdio/Kconfig | 8 +
> drivers/net/mdio/Makefile | 1 +
> drivers/net/mdio/mdio-netlink.c | 464 ++++++++++++++++++++++++++++++
> include/uapi/linux/mdio-netlink.h | 61 ++++
> 4 files changed, 534 insertions(+)
> create mode 100644 drivers/net/mdio/mdio-netlink.c
> create mode 100644 include/uapi/linux/mdio-netlink.h
>
> diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig
> index 90309980686e..8a01978e5b51 100644
> --- a/drivers/net/mdio/Kconfig
> +++ b/drivers/net/mdio/Kconfig
> @@ -43,6 +43,14 @@ config ACPI_MDIO
>
> if MDIO_BUS
>
> +config MDIO_NETLINK
> + tristate "Netlink interface for MDIO buses"
> + help
> + Enable a netlink interface to allow reading MDIO buses from
> + userspace. A small virtual machine allows implementing complex
> + operations, such as conditional reads or polling. All operations
> + submitted in the same program are evaluated atomically.
> +
> config MDIO_DEVRES
> tristate
>
> diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile
> index 7d4cb4c11e4e..5583d5b8d174 100644
> --- a/drivers/net/mdio/Makefile
> +++ b/drivers/net/mdio/Makefile
> @@ -4,6 +4,7 @@
> obj-$(CONFIG_ACPI_MDIO) += acpi_mdio.o
> obj-$(CONFIG_FWNODE_MDIO) += fwnode_mdio.o
> obj-$(CONFIG_OF_MDIO) += of_mdio.o
> +obj-$(CONFIG_MDIO_NETLINK) += mdio-netlink.o
>
> obj-$(CONFIG_MDIO_ASPEED) += mdio-aspeed.o
> obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o
> diff --git a/drivers/net/mdio/mdio-netlink.c b/drivers/net/mdio/mdio-netlink.c
> new file mode 100644
> index 000000000000..3e32d1a9bab3
> --- /dev/null
> +++ b/drivers/net/mdio/mdio-netlink.c
> @@ -0,0 +1,464 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2022-23 Sean Anderson <sean.anderson@seco.com>
> + * Copyright (C) 2020-22 Tobias Waldekranz <tobias@waldekranz.com>
> + */
> +
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/netlink.h>
> +#include <linux/phy.h>
> +#include <net/genetlink.h>
> +#include <net/netlink.h>
> +#include <uapi/linux/mdio-netlink.h>
> +
> +struct mdio_nl_xfer {
> + struct genl_info *info;
> + struct sk_buff *msg;
> + void *hdr;
> + struct nlattr *data;
> +
> + struct mii_bus *mdio;
> + int timeout_ms;
> +
> + int prog_len;
> + struct mdio_nl_insn *prog;
> +};
> +
> +static int mdio_nl_open(struct mdio_nl_xfer *xfer);
> +static int mdio_nl_close(struct mdio_nl_xfer *xfer, bool last, int xerr);
> +
> +static int mdio_nl_flush(struct mdio_nl_xfer *xfer)
> +{
> + int err;
> +
> + err = mdio_nl_close(xfer, false, 0);
> + if (err)
> + return err;
> +
> + return mdio_nl_open(xfer);
> +}
> +
> +static int mdio_nl_emit(struct mdio_nl_xfer *xfer, u32 datum)
> +{
> + int err = 0;
> +
> + if (!nla_put_nohdr(xfer->msg, sizeof(datum), &datum))
> + return 0;
> +
> + err = mdio_nl_flush(xfer);
> + if (err)
> + return err;
> +
> + return nla_put_nohdr(xfer->msg, sizeof(datum), &datum);
> +}
> +
> +static inline u16 *__arg_r(u32 arg, u16 *regs)
> +{
> + WARN_ON_ONCE(arg >> 16 != MDIO_NL_ARG_REG);
> +
> + return ®s[arg & 0x7];
> +}
> +
> +static inline u16 __arg_i(u32 arg)
> +{
> + WARN_ON_ONCE(arg >> 16 != MDIO_NL_ARG_IMM);
> +
> + return arg & 0xffff;
> +}
> +
> +static inline u16 __arg_ri(u32 arg, u16 *regs)
> +{
> + switch ((enum mdio_nl_argmode)(arg >> 16)) {
> + case MDIO_NL_ARG_IMM:
> + return arg & 0xffff;
> + case MDIO_NL_ARG_REG:
> + return regs[arg & 7];
> + default:
> + WARN_ON_ONCE(1);
> + return 0;
> + }
> +}
> +
> +/* To prevent out-of-tree drivers from being implemented through this
> + * interface, disallow writes by default. This does disallow read-only uses,
> + * such as c45-over-c22 or reading phys with pages. However, with a such a
> + * flexible interface, we must use a big hammer. People who want to use this
> + * will need to modify the source code directly.
> + */
> +#undef MDIO_NETLINK_ALLOW_WRITE
> +
> +static int mdio_nl_eval(struct mdio_nl_xfer *xfer)
> +{
> + struct mdio_nl_insn *insn;
> + unsigned long timeout;
> + u16 regs[8] = { 0 };
> + int pc, ret = 0;
> + int phy_id, reg, prtad, devad, val;
> +
> + timeout = jiffies + msecs_to_jiffies(xfer->timeout_ms);
> +
> + mutex_lock(&xfer->mdio->mdio_lock);
> +
> + for (insn = xfer->prog, pc = 0;
> + pc < xfer->prog_len;
> + insn = &xfer->prog[++pc]) {
> + if (time_after(jiffies, timeout)) {
> + ret = -ETIMEDOUT;
> + break;
> + }
> +
> + switch ((enum mdio_nl_op)insn->op) {
> + case MDIO_NL_OP_READ:
> + phy_id = __arg_ri(insn->arg0, regs);
> + prtad = mdio_phy_id_prtad(phy_id);
> + devad = mdio_phy_id_devad(phy_id);
> + reg = __arg_ri(insn->arg1, regs);
> +
> + if (mdio_phy_id_is_c45(phy_id))
> + ret = __mdiobus_c45_read(xfer->mdio, prtad,
> + devad, reg);
> + else
> + ret = __mdiobus_read(xfer->mdio, phy_id, reg);
> +
> + if (ret < 0)
> + goto exit;
> + *__arg_r(insn->arg2, regs) = ret;
> + ret = 0;
> + break;
> +
> + case MDIO_NL_OP_WRITE:
> + phy_id = __arg_ri(insn->arg0, regs);
> + prtad = mdio_phy_id_prtad(phy_id);
> + devad = mdio_phy_id_devad(phy_id);
> + reg = __arg_ri(insn->arg1, regs);
> + val = __arg_ri(insn->arg2, regs);
> +
> +#ifdef MDIO_NETLINK_ALLOW_WRITE
> + add_taint(TAINT_USER, LOCKDEP_STILL_OK);
> + if (mdio_phy_id_is_c45(phy_id))
> + ret = __mdiobus_c45_write(xfer->mdio, prtad,
> + devad, reg, val
> + else
> + ret = __mdiobus_write(xfer->mdio, dev, reg,
> + val);
> +#else
> + ret = -EPERM;
> +#endif
> + if (ret < 0)
> + goto exit;
> + ret = 0;
> + break;
> +
> + case MDIO_NL_OP_AND:
> + *__arg_r(insn->arg2, regs) =
> + __arg_ri(insn->arg0, regs) &
> + __arg_ri(insn->arg1, regs);
> + break;
> +
> + case MDIO_NL_OP_OR:
> + *__arg_r(insn->arg2, regs) =
> + __arg_ri(insn->arg0, regs) |
> + __arg_ri(insn->arg1, regs);
> + break;
> +
> + case MDIO_NL_OP_ADD:
> + *__arg_r(insn->arg2, regs) =
> + __arg_ri(insn->arg0, regs) +
> + __arg_ri(insn->arg1, regs);
> + break;
> +
> + case MDIO_NL_OP_JEQ:
> + if (__arg_ri(insn->arg0, regs) ==
> + __arg_ri(insn->arg1, regs))
> + pc += (s16)__arg_i(insn->arg2);
> + break;
> +
> + case MDIO_NL_OP_JNE:
> + if (__arg_ri(insn->arg0, regs) !=
> + __arg_ri(insn->arg1, regs))
> + pc += (s16)__arg_i(insn->arg2);
> + break;
> +
> + case MDIO_NL_OP_EMIT:
> + ret = mdio_nl_emit(xfer, __arg_ri(insn->arg0, regs));
> + if (ret < 0)
> + goto exit;
> + ret = 0;
> + break;
> +
> + case MDIO_NL_OP_UNSPEC:
> + default:
> + ret = -EINVAL;
> + goto exit;
> + }
> + }
> +exit:
> + mutex_unlock(&xfer->mdio->mdio_lock);
> + return ret;
> +}
> +
> +struct mdio_nl_op_proto {
> + u8 arg0;
> + u8 arg1;
> + u8 arg2;
> +};
> +
> +static const struct mdio_nl_op_proto mdio_nl_op_protos[MDIO_NL_OP_MAX + 1] = {
> + [MDIO_NL_OP_READ] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg2 = BIT(MDIO_NL_ARG_REG),
> + },
> + [MDIO_NL_OP_WRITE] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg2 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + },
> + [MDIO_NL_OP_AND] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg2 = BIT(MDIO_NL_ARG_REG),
> + },
> + [MDIO_NL_OP_OR] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg2 = BIT(MDIO_NL_ARG_REG),
> + },
> + [MDIO_NL_OP_ADD] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg2 = BIT(MDIO_NL_ARG_REG),
> + },
> + [MDIO_NL_OP_JEQ] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg2 = BIT(MDIO_NL_ARG_IMM),
> + },
> + [MDIO_NL_OP_JNE] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg2 = BIT(MDIO_NL_ARG_IMM),
> + },
> + [MDIO_NL_OP_EMIT] = {
> + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM),
> + .arg1 = BIT(MDIO_NL_ARG_NONE),
> + .arg2 = BIT(MDIO_NL_ARG_NONE),
> + },
> +};
> +
> +static int mdio_nl_validate_insn(const struct nlattr *attr,
> + struct netlink_ext_ack *extack,
> + const struct mdio_nl_insn *insn)
> +{
> + const struct mdio_nl_op_proto *proto;
> +
> + if (insn->op > MDIO_NL_OP_MAX) {
> + NL_SET_ERR_MSG_ATTR(extack, attr, "Illegal instruction");
> + return -EINVAL;
> + }
> +
> + proto = &mdio_nl_op_protos[insn->op];
> +
> + if (!(BIT(insn->arg0 >> 16) & proto->arg0)) {
> + NL_SET_ERR_MSG_ATTR(extack, attr, "Argument 0 invalid");
> + return -EINVAL;
> + }
> +
> + if (!(BIT(insn->arg1 >> 16) & proto->arg1)) {
> + NL_SET_ERR_MSG_ATTR(extack, attr, "Argument 1 invalid");
> + return -EINVAL;
> + }
> +
> + if (!(BIT(insn->arg2 >> 16) & proto->arg2)) {
> + NL_SET_ERR_MSG_ATTR(extack, attr, "Argument 2 invalid");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int mdio_nl_validate_prog(const struct nlattr *attr,
> + struct netlink_ext_ack *extack)
> +{
> + const struct mdio_nl_insn *prog = nla_data(attr);
> + int len = nla_len(attr);
> + int i, err = 0;
> +
> + if (len % sizeof(*prog)) {
> + NL_SET_ERR_MSG_ATTR(extack, attr, "Unaligned instruction");
> + return -EINVAL;
> + }
> +
> + len /= sizeof(*prog);
> + for (i = 0; i < len; i++) {
> + err = mdio_nl_validate_insn(attr, extack, &prog[i]);
> + if (err)
> + break;
> + }
> +
> + return err;
> +}
> +
> +static const struct nla_policy mdio_nl_policy[MDIO_NLA_MAX + 1] = {
> + [MDIO_NLA_UNSPEC] = { .type = NLA_UNSPEC, },
> + [MDIO_NLA_BUS_ID] = { .type = NLA_STRING, .len = MII_BUS_ID_SIZE },
> + [MDIO_NLA_TIMEOUT] = NLA_POLICY_MAX(NLA_U16, 10 * MSEC_PER_SEC),
> + [MDIO_NLA_PROG] = NLA_POLICY_VALIDATE_FN(NLA_BINARY,
> + mdio_nl_validate_prog,
> + 0x1000),
> + [MDIO_NLA_DATA] = { .type = NLA_NESTED },
> + [MDIO_NLA_ERROR] = { .type = NLA_S32, },
> +};
> +
> +static struct genl_family mdio_nl_family;
> +
> +static int mdio_nl_open(struct mdio_nl_xfer *xfer)
> +{
> + int err;
> +
> + xfer->msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
> + if (!xfer->msg) {
> + err = -ENOMEM;
> + goto err;
> + }
> +
> + xfer->hdr = genlmsg_put(xfer->msg, xfer->info->snd_portid,
> + xfer->info->snd_seq, &mdio_nl_family,
> + NLM_F_ACK | NLM_F_MULTI, MDIO_GENL_XFER);
> + if (!xfer->hdr) {
> + err = -EMSGSIZE;
> + goto err_free;
> + }
> +
> + xfer->data = nla_nest_start(xfer->msg, MDIO_NLA_DATA);
> + if (!xfer->data) {
> + err = -EMSGSIZE;
> + goto err_free;
> + }
> +
> + return 0;
> +
> +err_free:
> + nlmsg_free(xfer->msg);
> +err:
> + return err;
> +}
> +
> +static int mdio_nl_close(struct mdio_nl_xfer *xfer, bool last, int xerr)
> +{
> + struct nlmsghdr *end;
> + int err;
> +
> + nla_nest_end(xfer->msg, xfer->data);
> +
> + if (xerr && nla_put_s32(xfer->msg, MDIO_NLA_ERROR, xerr)) {
> + err = mdio_nl_flush(xfer);
> + if (err)
> + goto err_free;
> +
> + if (nla_put_s32(xfer->msg, MDIO_NLA_ERROR, xerr)) {
> + err = -EMSGSIZE;
> + goto err_free;
> + }
> + }
> +
> + genlmsg_end(xfer->msg, xfer->hdr);
> +
> + if (last) {
> + end = nlmsg_put(xfer->msg, xfer->info->snd_portid,
> + xfer->info->snd_seq, NLMSG_DONE, 0,
> + NLM_F_ACK | NLM_F_MULTI);
> + if (!end) {
> + err = mdio_nl_flush(xfer);
> + if (err)
> + goto err_free;
> +
> + end = nlmsg_put(xfer->msg, xfer->info->snd_portid,
> + xfer->info->snd_seq, NLMSG_DONE, 0,
> + NLM_F_ACK | NLM_F_MULTI);
> + if (!end) {
> + err = -EMSGSIZE;
> + goto err_free;
> + }
> + }
> + }
> +
> + return genlmsg_unicast(genl_info_net(xfer->info), xfer->msg,
> + xfer->info->snd_portid);
> +
> +err_free:
> + nlmsg_free(xfer->msg);
> + return err;
> +}
> +
> +static int mdio_nl_cmd_xfer(struct sk_buff *skb, struct genl_info *info)
> +{
> + struct mdio_nl_xfer xfer;
> + int err;
> +
> + if (!info->attrs[MDIO_NLA_BUS_ID] ||
> + !info->attrs[MDIO_NLA_PROG] ||
> + info->attrs[MDIO_NLA_DATA] ||
> + info->attrs[MDIO_NLA_ERROR])
> + return -EINVAL;
> +
> + xfer.mdio = mdio_find_bus(nla_data(info->attrs[MDIO_NLA_BUS_ID]));
> + if (!xfer.mdio)
> + return -ENODEV;
> +
> + if (info->attrs[MDIO_NLA_TIMEOUT])
> + xfer.timeout_ms = nla_get_u32(info->attrs[MDIO_NLA_TIMEOUT]);
> + else
> + xfer.timeout_ms = 100;
> +
> + xfer.info = info;
> + xfer.prog_len = nla_len(info->attrs[MDIO_NLA_PROG]) / sizeof(*xfer.prog);
> + xfer.prog = nla_data(info->attrs[MDIO_NLA_PROG]);
> +
> + err = mdio_nl_open(&xfer);
> + if (err)
> + return err;
> +
> + err = mdio_nl_eval(&xfer);
> +
> + err = mdio_nl_close(&xfer, true, err);
> + return err;
> +}
> +
> +static const struct genl_ops mdio_nl_ops[] = {
> + {
> + .cmd = MDIO_GENL_XFER,
> + .doit = mdio_nl_cmd_xfer,
> + .flags = GENL_ADMIN_PERM,
> + },
> +};
> +
> +static struct genl_family mdio_nl_family = {
> + .name = "mdio",
> + .version = 1,
> + .maxattr = MDIO_NLA_MAX,
> + .netnsok = false,
> + .module = THIS_MODULE,
> + .ops = mdio_nl_ops,
> + .n_ops = ARRAY_SIZE(mdio_nl_ops),
> + .policy = mdio_nl_policy,
> +};
> +
> +static int __init mdio_nl_init(void)
> +{
> + return genl_register_family(&mdio_nl_family);
> +}
> +
> +static void __exit mdio_nl_exit(void)
> +{
> + genl_unregister_family(&mdio_nl_family);
> +}
> +
> +MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>");
> +MODULE_DESCRIPTION("MDIO Netlink Interface");
> +MODULE_LICENSE("GPL");
> +
> +module_init(mdio_nl_init);
> +module_exit(mdio_nl_exit);
> diff --git a/include/uapi/linux/mdio-netlink.h b/include/uapi/linux/mdio-netlink.h
> new file mode 100644
> index 000000000000..bebd3b45c882
> --- /dev/null
> +++ b/include/uapi/linux/mdio-netlink.h
> @@ -0,0 +1,61 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +/*
> + * Copyright (C) 2020-22 Tobias Waldekranz <tobias@waldekranz.com>
> + */
> +
> +#ifndef _UAPI_LINUX_MDIO_NETLINK_H
> +#define _UAPI_LINUX_MDIO_NETLINK_H
> +
> +#include <linux/types.h>
> +
> +enum {
> + MDIO_GENL_UNSPEC,
> + MDIO_GENL_XFER,
> +
> + __MDIO_GENL_MAX,
> + MDIO_GENL_MAX = __MDIO_GENL_MAX - 1
> +};
> +
> +enum {
> + MDIO_NLA_UNSPEC,
> + MDIO_NLA_BUS_ID, /* string */
> + MDIO_NLA_TIMEOUT, /* u32 */
> + MDIO_NLA_PROG, /* struct mdio_nl_insn[] */
> + MDIO_NLA_DATA, /* nest */
> + MDIO_NLA_ERROR, /* s32 */
> +
> + __MDIO_NLA_MAX,
> + MDIO_NLA_MAX = __MDIO_NLA_MAX - 1
> +};
> +
> +enum mdio_nl_op {
> + MDIO_NL_OP_UNSPEC,
> + MDIO_NL_OP_READ, /* read dev(RI), port(RI), dst(R) */
> + MDIO_NL_OP_WRITE, /* write dev(RI), port(RI), src(RI) */
> + MDIO_NL_OP_AND, /* and a(RI), b(RI), dst(R) */
> + MDIO_NL_OP_OR, /* or a(RI), b(RI), dst(R) */
> + MDIO_NL_OP_ADD, /* add a(RI), b(RI), dst(R) */
> + MDIO_NL_OP_JEQ, /* jeq a(RI), b(RI), jmp(I) */
> + MDIO_NL_OP_JNE, /* jeq a(RI), b(RI), jmp(I) */
> + MDIO_NL_OP_EMIT, /* emit src(RI) */
> +
> + __MDIO_NL_OP_MAX,
> + MDIO_NL_OP_MAX = __MDIO_NL_OP_MAX - 1
> +};
> +
> +enum mdio_nl_argmode {
> + MDIO_NL_ARG_NONE,
> + MDIO_NL_ARG_REG,
> + MDIO_NL_ARG_IMM,
> + MDIO_NL_ARG_RESERVED
> +};
> +
> +struct mdio_nl_insn {
> + __u64 op:8;
> + __u64 reserved:2;
> + __u64 arg0:18;
> + __u64 arg1:18;
> + __u64 arg2:18;
> +};
> +
> +#endif /* _UAPI_LINUX_MDIO_NETLINK_H */
> --
> 2.35.1.1320.gc452695387.dirty
next prev parent reply other threads:[~2023-03-07 12:26 UTC|newest]
Thread overview: 18+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-03-06 20:45 [PATCH net-next] net: mdio: Add netlink interface Sean Anderson
2023-03-06 22:48 ` Russell King (Oracle)
2023-03-06 23:39 ` Sean Anderson
2023-03-07 13:47 ` Andrew Lunn
2023-03-07 16:41 ` Sean Anderson
2023-03-07 0:05 ` kernel test robot
2023-03-07 11:23 ` Michael Walle
2023-03-07 13:49 ` Andrew Lunn
2023-03-07 14:05 ` Vladimir Oltean
2023-03-07 14:33 ` Andrew Lunn
2023-03-07 15:00 ` Russell King (Oracle)
2023-03-07 12:26 ` Tobias Waldekranz [this message]
2023-03-07 16:30 ` Sean Anderson
2023-03-07 14:22 ` Andrew Lunn
2023-03-07 14:50 ` Russell King (Oracle)
2023-03-07 16:16 ` Sean Anderson
2023-03-07 17:23 ` Andrew Lunn
2023-03-07 17:42 ` Sean Anderson
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=874jqwkart.fsf@waldekranz.com \
--to=tobias@waldekranz.com \
--cc=andrew@lunn.ch \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=f.fainelli@gmail.com \
--cc=hkallweit1@gmail.com \
--cc=kuba@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
--cc=olteanv@gmail.com \
--cc=pabeni@redhat.com \
--cc=sean.anderson@seco.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.