@@ -184,7 +184,7 @@ according to message purpose:
Userspace to kernel:
- ===================================== =================================
+ ===================================== ====================================
``ETHTOOL_MSG_STRSET_GET`` get string set
``ETHTOOL_MSG_LINKINFO_GET`` get link settings
``ETHTOOL_MSG_LINKINFO_SET`` set link settings
@@ -220,7 +220,8 @@ Userspace to kernel:
``ETHTOOL_MSG_PHC_VCLOCKS_GET`` get PHC virtual clocks info
``ETHTOOL_MSG_MODULE_SET`` set transceiver module parameters
``ETHTOOL_MSG_MODULE_GET`` get transceiver module parameters
- ===================================== =================================
+ ``ETHTOOL_MSG_MODULE_FW_INFO_GET`` get transceiver module firmware info
+ ===================================== ====================================
Kernel to userspace:
@@ -260,6 +261,7 @@ Kernel to userspace:
``ETHTOOL_MSG_STATS_GET_REPLY`` standard statistics
``ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY`` PHC virtual clocks info
``ETHTOOL_MSG_MODULE_GET_REPLY`` transceiver module parameters
+ ``ETHTOOL_MSG_MODULE_FW_INFO_GET_REPLY`` transceiver module firmware info
======================================== =================================
``GET`` requests are sent by userspace applications to retrieve device
@@ -1598,6 +1600,67 @@ For SFF-8636 modules, low power mode is forced by the host according to table
For CMIS modules, low power mode is forced by the host according to table 6-12
in revision 5.0 of the specification.
+MODULE_FW_INFO_GET
+==================
+
+Gets transceiver module firmware information.
+
+Request contents:
+
+ ====================================== ====== ==========================
+ ``ETHTOOL_A_MODULE_FW_INFO_HEADER`` nested request header
+ ====================================== ====== ==========================
+
+Kernel response contents:
+
+ +---------------------------------------------------+--------+----------------+
+ | ``ETHTOOL_A_MODULE_FW_INFO_HEADER`` | nested | reply header |
+ +---------------------------------------------------+--------+----------------+
+ | ``ETHTOOL_A_MODULE_FW_INFO_IMAGE`` | nested | firmware image |
+ +-+-------------------------------------------------+--------+----------------+
+ | | ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_NAME`` | string | image name |
+ +-+-------------------------------------------------+--------+----------------+
+ | | ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_RUNNING`` | bool | running |
+ +-+-------------------------------------------------+--------+----------------+
+ | | ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_COMMITTED`` | bool | committed |
+ +-+-------------------------------------------------+--------+----------------+
+ | | ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_VALID`` | bool | valid |
+ +-+-------------------------------------------------+--------+----------------+
+ | | ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_VERSION`` | string | image version |
+ +-+-------------------------------------------------+--------+----------------+
+
+The ``ETHTOOL_A_MODULE_FW_INFO_IMAGE`` nested attribute may appear multiple
+times in the response, according to the number of firmware images stored in the
+transceiver module. CMIS modules, for example, may have up to three images: Two
+host updateable images stored in up to two storage banks (A and B) and an
+internal factory image. The following paragraphs describe various image
+attributes.
+
+The ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_NAME`` attribute encodes the image's name.
+The name is significant as a vendor may provide two firmware images, each one
+intended for a different bank.
+
+The ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_RUNNING`` attribute indicates if the image
+is currently running or not.
+
+The ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_COMMITTED`` attribute indicates if the
+image is to be run upon reset or not.
+
+The last two attributes can be used by user space to determine which firmware
+image should be used for the firmware update. In CMIS modules, for example, if
+image A is running and committed, image B should be used.
+
+The ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_VALID`` attribute encodes the validity of
+the image. A valid image is runnable and persistently stored completely
+undamaged.
+
+The ``ETHTOOL_A_MODULE_FW_INFO_IMAGE_VERSION`` attribute encodes the version of
+the image.
+
+For CMIS modules, the above mentioned information can be queried from the
+module using CDB CMD 0100h (Get Firmware Info). See section 9.7.1 in revision
+5.0 of the specification.
+
Request translation
===================
@@ -1699,4 +1762,5 @@ are netlink only.
n/a ``ETHTOOL_MSG_PHC_VCLOCKS_GET``
n/a ``ETHTOOL_MSG_MODULE_GET``
n/a ``ETHTOOL_MSG_MODULE_SET``
+ n/a ``ETHTOOL_MSG_MODULE_FW_INFO_GET``
=================================== =====================================
@@ -443,6 +443,71 @@ struct ethtool_module_power_mode_params {
enum ethtool_module_power_mode mode;
};
+#define ETH_MODULE_FW_VER_LEN 48
+
+/**
+ * struct ethtool_module_fw_info_image - Module firmware image information
+ * @running: Whether the image is currently running or not.
+ * @committed: Whether the image is run upon resets.
+ * @valid: Whether the image is runnable and persistently stored completely
+ * undamaged.
+ * @ver_major: Firmware image major revision.
+ * @ver_minor: Firmware image minor revision.
+ * @ver_build: Firmware image build number.
+ * @ver_extra_str: Firmware image additional information.
+ */
+struct ethtool_module_fw_info_image {
+ u8 running:1,
+ committed:1,
+ valid:1;
+ u8 ver_major;
+ u8 ver_minor;
+ u8 ver_build;
+ char ver_extra_str[ETH_MODULE_FW_VER_LEN];
+};
+
+/**
+ * struct ethtool_module_fw_info_cmis - CMIS module firmware information
+ * @a_present: Whether image A is present or not.
+ * @b_present: Whether image B is present or not.
+ * @factory_present: Whether factory image is present or not.
+ * @a: Image A firmware information.
+ * @b: Image B firmware information.
+ * @factory: Factory image firmware information.
+ *
+ * CMIS modules can have up to two host updateable images stored in up to two
+ * firmware banks, called A and B. In addition, the module may also have an
+ * internal factory image.
+ */
+struct ethtool_module_fw_info_cmis {
+ u8 a_present:1,
+ b_present:1,
+ factory_present:1;
+ struct ethtool_module_fw_info_image a;
+ struct ethtool_module_fw_info_image b;
+ struct ethtool_module_fw_info_image factory;
+};
+
+/**
+ * enum ethtool_module_fw_info_type - Module firmware information type
+ * @ETHTOOL_MODULE_FW_INFO_TYPE_CMIS: CMIS module firmware information type.
+ */
+enum ethtool_module_fw_info_type {
+ ETHTOOL_MODULE_FW_INFO_TYPE_CMIS = 1,
+};
+
+/**
+ * struct ethtool_module_fw_info - module firmware information
+ * @type: Module firmware information type.
+ * @cmis: CMIS module firmware information.
+ */
+struct ethtool_module_fw_info {
+ enum ethtool_module_fw_info_type type;
+ union {
+ struct ethtool_module_fw_info_cmis cmis;
+ };
+};
+
/**
* struct ethtool_ops - optional netdev operations
* @cap_link_lanes_supported: indicates if the driver supports lanes
@@ -614,6 +679,8 @@ struct ethtool_module_power_mode_params {
* plugged-in.
* @set_module_power_mode: Set the power mode policy for the plug-in module
* used by the network device.
+ * @get_module_fw_info: Get the firmware information of the plug-in module
+ * used by the network device.
*
* All operations are optional (i.e. the function pointer may be set
* to %NULL) and callers must take this into account. Callers must
@@ -750,6 +817,9 @@ struct ethtool_ops {
int (*set_module_power_mode)(struct net_device *dev,
const struct ethtool_module_power_mode_params *params,
struct netlink_ext_ack *extack);
+ int (*get_module_fw_info)(struct net_device *dev,
+ struct ethtool_module_fw_info *info,
+ struct netlink_ext_ack *extack);
};
int ethtool_check_ops(const struct ethtool_ops *ops);
@@ -49,6 +49,7 @@ enum {
ETHTOOL_MSG_PHC_VCLOCKS_GET,
ETHTOOL_MSG_MODULE_GET,
ETHTOOL_MSG_MODULE_SET,
+ ETHTOOL_MSG_MODULE_FW_INFO_GET,
/* add new constants above here */
__ETHTOOL_MSG_USER_CNT,
@@ -94,6 +95,7 @@ enum {
ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY,
ETHTOOL_MSG_MODULE_GET_REPLY,
ETHTOOL_MSG_MODULE_NTF,
+ ETHTOOL_MSG_MODULE_FW_INFO_GET_REPLY,
/* add new constants above here */
__ETHTOOL_MSG_KERNEL_CNT,
@@ -853,6 +855,32 @@ enum {
ETHTOOL_A_MODULE_MAX = (__ETHTOOL_A_MODULE_CNT - 1)
};
+/* MODULE_FW_INFO */
+
+enum {
+ ETHTOOL_A_MODULE_FW_INFO_UNSPEC,
+ ETHTOOL_A_MODULE_FW_INFO_HEADER, /* nest - _A_HEADER_* */
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE, /* nest */
+
+ /* add new constants above here */
+ __ETHTOOL_A_MODULE_FW_INFO_CNT,
+ ETHTOOL_A_MODULE_FW_INFO_MAX = (__ETHTOOL_A_MODULE_FW_INFO_CNT - 1)
+};
+
+enum {
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE_UNSPEC,
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE_NAME, /* string */
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE_RUNNING, /* u8 */
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE_COMMITTED, /* u8 */
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE_VALID, /* u8 */
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE_VERSION, /* string */
+
+ /* add new constants above here */
+ __ETHTOOL_A_MODULE_FW_INFO_IMAGE_CNT,
+ ETHTOOL_A_MODULE_FW_INFO_IMAGE_MAX
+ = (__ETHTOOL_A_MODULE_FW_INFO_IMAGE_CNT - 1)
+};
+
/* generic netlink info */
#define ETHTOOL_GENL_NAME "ethtool"
#define ETHTOOL_GENL_VERSION 1
@@ -18,6 +18,18 @@ struct module_reply_data {
#define MODULE_REPDATA(__reply_base) \
container_of(__reply_base, struct module_reply_data, base)
+struct module_fw_info_req_info {
+ struct ethnl_req_info base;
+};
+
+struct module_fw_info_reply_data {
+ struct ethnl_reply_data base;
+ struct ethtool_module_fw_info fw_info;
+};
+
+#define MODULE_FW_INFO_REPDATA(__reply_base) \
+ container_of(__reply_base, struct module_fw_info_reply_data, base)
+
/* MODULE_GET */
const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + 1] = {
@@ -178,3 +190,193 @@ int ethnl_set_module(struct sk_buff *skb, struct genl_info *info)
dev_put(dev);
return ret;
}
+
+/* MODULE_FW_INFO_GET */
+
+const struct nla_policy ethnl_module_fw_info_get_policy[ETHTOOL_A_MODULE_FW_INFO_HEADER + 1] = {
+ [ETHTOOL_A_MODULE_FW_INFO_HEADER] =
+ NLA_POLICY_NESTED(ethnl_header_policy),
+};
+
+static int module_get_fw_info(struct net_device *dev,
+ struct ethtool_module_fw_info *fw_info,
+ struct netlink_ext_ack *extack)
+{
+ int ret;
+
+ ret = dev->ethtool_ops->get_module_fw_info(dev, fw_info, extack);
+ if (ret < 0)
+ return ret;
+
+ if (!fw_info->type) {
+ NL_SET_ERR_MSG(extack, "Module firmware info type was not set");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int module_fw_info_prepare_data(const struct ethnl_req_info *req_base,
+ struct ethnl_reply_data *reply_base,
+ struct genl_info *info)
+{
+ struct netlink_ext_ack *extack = info ? info->extack : NULL;
+ struct net_device *dev = reply_base->dev;
+ struct module_fw_info_reply_data *data;
+ int ret;
+
+ if (!dev->ethtool_ops->get_module_fw_info)
+ return -EOPNOTSUPP;
+
+ ret = ethnl_ops_begin(dev);
+ if (ret < 0)
+ return ret;
+
+ data = MODULE_FW_INFO_REPDATA(reply_base);
+ ret = module_get_fw_info(dev, &data->fw_info, extack);
+ if (ret < 0)
+ goto out_complete;
+
+out_complete:
+ ethnl_ops_complete(dev);
+ return ret;
+}
+
+static int
+module_fw_info_reply_size_image(const struct ethtool_module_fw_info_image *image,
+ int name_len)
+{
+ /* _MODULE_FW_INFO_IMAGE */
+ return nla_total_size(0) +
+ /* _MODULE_FW_INFO_IMAGE_NAME */
+ nla_total_size(name_len + 1) +
+ /* _MODULE_FW_INFO_IMAGE_RUNNING */
+ nla_total_size(sizeof(u8)) +
+ /* _MODULE_FW_INFO_IMAGE_COMMITTED */
+ nla_total_size(sizeof(u8)) +
+ /* _MODULE_FW_INFO_IMAGE_VALID */
+ nla_total_size(sizeof(u8)) +
+ /* _MODULE_FW_INFO_IMAGE_VERSION */
+ nla_total_size(ETH_MODULE_FW_VER_LEN + 1);
+}
+
+static int
+module_fw_info_reply_size_cmis(const struct ethtool_module_fw_info_cmis *cmis)
+{
+ int len = 0;
+
+ if (cmis->a_present)
+ len += module_fw_info_reply_size_image(&cmis->a, strlen("a"));
+ if (cmis->b_present)
+ len += module_fw_info_reply_size_image(&cmis->b, strlen("b"));
+ if (cmis->factory_present)
+ len += module_fw_info_reply_size_image(&cmis->factory,
+ strlen("factory"));
+
+ return len;
+}
+
+static int module_fw_info_reply_size(const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ struct module_fw_info_reply_data *data;
+
+ data = MODULE_FW_INFO_REPDATA(reply_base);
+
+ switch (data->fw_info.type) {
+ case ETHTOOL_MODULE_FW_INFO_TYPE_CMIS:
+ return module_fw_info_reply_size_cmis(&data->fw_info.cmis);
+ default:
+ /* Module firmware information type was already validated to be
+ * set in prepare_data() callback.
+ */
+ WARN_ON(1);
+ return -EINVAL;
+ }
+}
+
+static int
+module_fw_info_fill_reply_image(struct sk_buff *skb,
+ const struct ethtool_module_fw_info_image *image,
+ const char *image_name)
+{
+ char buf[ETH_MODULE_FW_VER_LEN];
+ struct nlattr *nest;
+
+ if (strlen(image->ver_extra_str))
+ snprintf(buf, ETH_MODULE_FW_VER_LEN, "%d.%d.%d-%s",
+ image->ver_major, image->ver_minor, image->ver_build,
+ image->ver_extra_str);
+ else
+ snprintf(buf, ETH_MODULE_FW_VER_LEN, "%d.%d.%d",
+ image->ver_major, image->ver_minor, image->ver_build);
+
+ nest = nla_nest_start(skb, ETHTOOL_A_MODULE_FW_INFO_IMAGE);
+ if (!nest)
+ return -EMSGSIZE;
+
+ if (nla_put_string(skb, ETHTOOL_A_MODULE_FW_INFO_IMAGE_NAME,
+ image_name) ||
+ nla_put_u8(skb, ETHTOOL_A_MODULE_FW_INFO_IMAGE_RUNNING,
+ image->running) ||
+ nla_put_u8(skb, ETHTOOL_A_MODULE_FW_INFO_IMAGE_COMMITTED,
+ image->committed) ||
+ nla_put_u8(skb, ETHTOOL_A_MODULE_FW_INFO_IMAGE_VALID,
+ image->valid) ||
+ nla_put_string(skb, ETHTOOL_A_MODULE_FW_INFO_IMAGE_VERSION, buf))
+ goto err_cancel;
+
+ nla_nest_end(skb, nest);
+
+ return 0;
+
+err_cancel:
+ nla_nest_cancel(skb, nest);
+ return -EMSGSIZE;
+}
+
+static int
+module_fw_info_fill_reply_cmis(struct sk_buff *skb,
+ const struct ethtool_module_fw_info_cmis *cmis)
+{
+ if (cmis->a_present &&
+ module_fw_info_fill_reply_image(skb, &cmis->a, "a"))
+ return -EMSGSIZE;
+ if (cmis->b_present &&
+ module_fw_info_fill_reply_image(skb, &cmis->b, "b"))
+ return -EMSGSIZE;
+ if (cmis->factory_present &&
+ module_fw_info_fill_reply_image(skb, &cmis->factory, "factory"))
+ return -EMSGSIZE;
+
+ return 0;
+}
+
+static int module_fw_info_fill_reply(struct sk_buff *skb,
+ const struct ethnl_req_info *req_base,
+ const struct ethnl_reply_data *reply_base)
+{
+ const struct module_fw_info_reply_data *data;
+
+ data = MODULE_FW_INFO_REPDATA(reply_base);
+
+ switch (data->fw_info.type) {
+ case ETHTOOL_MODULE_FW_INFO_TYPE_CMIS:
+ return module_fw_info_fill_reply_cmis(skb, &data->fw_info.cmis);
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+}
+
+const struct ethnl_request_ops ethnl_module_fw_info_request_ops = {
+ .request_cmd = ETHTOOL_MSG_MODULE_FW_INFO_GET,
+ .reply_cmd = ETHTOOL_MSG_MODULE_FW_INFO_GET_REPLY,
+ .hdr_attr = ETHTOOL_A_MODULE_FW_INFO_HEADER,
+ .req_info_size = sizeof(struct module_fw_info_req_info),
+ .reply_data_size = sizeof(struct module_fw_info_reply_data),
+
+ .prepare_data = module_fw_info_prepare_data,
+ .reply_size = module_fw_info_reply_size,
+ .fill_reply = module_fw_info_fill_reply,
+};
@@ -283,6 +283,7 @@ ethnl_default_requests[__ETHTOOL_MSG_USER_CNT] = {
[ETHTOOL_MSG_STATS_GET] = ðnl_stats_request_ops,
[ETHTOOL_MSG_PHC_VCLOCKS_GET] = ðnl_phc_vclocks_request_ops,
[ETHTOOL_MSG_MODULE_GET] = ðnl_module_request_ops,
+ [ETHTOOL_MSG_MODULE_FW_INFO_GET] = ðnl_module_fw_info_request_ops,
};
static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb)
@@ -1018,6 +1019,15 @@ static const struct genl_ops ethtool_genl_ops[] = {
.policy = ethnl_module_set_policy,
.maxattr = ARRAY_SIZE(ethnl_module_set_policy) - 1,
},
+ {
+ .cmd = ETHTOOL_MSG_MODULE_FW_INFO_GET,
+ .doit = ethnl_default_doit,
+ .start = ethnl_default_start,
+ .dumpit = ethnl_default_dumpit,
+ .done = ethnl_default_done,
+ .policy = ethnl_module_fw_info_get_policy,
+ .maxattr = ARRAY_SIZE(ethnl_module_fw_info_get_policy) - 1,
+ },
};
static const struct genl_multicast_group ethtool_nl_mcgrps[] = {
@@ -338,6 +338,7 @@ extern const struct ethnl_request_ops ethnl_module_eeprom_request_ops;
extern const struct ethnl_request_ops ethnl_stats_request_ops;
extern const struct ethnl_request_ops ethnl_phc_vclocks_request_ops;
extern const struct ethnl_request_ops ethnl_module_request_ops;
+extern const struct ethnl_request_ops ethnl_module_fw_info_request_ops;
extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1];
extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1];
@@ -376,6 +377,7 @@ extern const struct nla_policy ethnl_stats_get_policy[ETHTOOL_A_STATS_GROUPS + 1
extern const struct nla_policy ethnl_phc_vclocks_get_policy[ETHTOOL_A_PHC_VCLOCKS_HEADER + 1];
extern const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + 1];
extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1];
+extern const struct nla_policy ethnl_module_fw_info_get_policy[ETHTOOL_A_MODULE_FW_INFO_HEADER + 1];
int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info);
int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info);