Messages in this thread | | | Date | Tue, 7 Mar 2023 11:30:39 -0500 | Subject | Re: [PATCH net-next] net: mdio: Add netlink interface | From | Sean Anderson <> |
| |
On 3/7/23 07:26, Tobias Waldekranz wrote: > 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).
Yes, I agree with this. It's always seemed silly to me to exclude a good debugging interface on the grounds that it could be used for userspace drivers when a vendor can just as easily supply their own proprietary module implementing the same thing.
Last time, the discussion seemed to get hung up on this topic, so I wanted to start off with an approach which would obviously prevent misuse (albeit rather draconian).
--Sean
> 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
| |