diff options
Diffstat (limited to 'net/ethtool/cmis_cdb.c')
| -rw-r--r-- | net/ethtool/cmis_cdb.c | 96 |
1 files changed, 80 insertions, 16 deletions
diff --git a/net/ethtool/cmis_cdb.c b/net/ethtool/cmis_cdb.c index 1bb08783b60d..3057576bc81e 100644 --- a/net/ethtool/cmis_cdb.c +++ b/net/ethtool/cmis_cdb.c @@ -11,25 +11,29 @@ * min(i, 15) byte octets where i specifies the allowable additional number of * byte octets in a READ or a WRITE. */ -u32 ethtool_cmis_get_max_payload_size(u8 num_of_byte_octs) +u32 ethtool_cmis_get_max_lpl_size(u8 num_of_byte_octs) { return 8 * (1 + min_t(u8, num_of_byte_octs, 15)); } void ethtool_cmis_cdb_compose_args(struct ethtool_cmis_cdb_cmd_args *args, - enum ethtool_cmis_cdb_cmd_id cmd, u8 *pl, - u8 lpl_len, u16 max_duration, - u8 read_write_len_ext, u16 msleep_pre_rpl, - u8 rpl_exp_len, u8 flags) + enum ethtool_cmis_cdb_cmd_id cmd, u8 *lpl, + u8 lpl_len, u8 *epl, u16 epl_len, + u16 max_duration, u8 read_write_len_ext, + u16 msleep_pre_rpl, u8 rpl_exp_len, u8 flags) { args->req.id = cpu_to_be16(cmd); args->req.lpl_len = lpl_len; - if (pl) - memcpy(args->req.payload, pl, args->req.lpl_len); + if (lpl) + memcpy(args->req.payload, lpl, args->req.lpl_len); + if (epl) { + args->req.epl_len = cpu_to_be16(epl_len); + args->req.epl = epl; + } args->max_duration = max_duration; args->read_write_len_ext = - ethtool_cmis_get_max_payload_size(read_write_len_ext); + ethtool_cmis_get_max_lpl_size(read_write_len_ext); args->msleep_pre_rpl = msleep_pre_rpl; args->rpl_exp_len = rpl_exp_len; args->flags = flags; @@ -100,7 +104,8 @@ static u8 cmis_cdb_advert_rpl_inst_supported(struct cmis_cdb_advert_rpl *rpl) } static int cmis_cdb_advertisement_get(struct ethtool_cmis_cdb *cdb, - struct net_device *dev) + struct net_device *dev, + struct ethnl_module_fw_flash_ntf_params *ntf_params) { const struct ethtool_ops *ops = dev->ethtool_ops; struct ethtool_module_eeprom page_data = {}; @@ -119,8 +124,12 @@ static int cmis_cdb_advertisement_get(struct ethtool_cmis_cdb *cdb, return err; } - if (!cmis_cdb_advert_rpl_inst_supported(&rpl)) + if (!cmis_cdb_advert_rpl_inst_supported(&rpl)) { + ethnl_module_fw_flash_ntf_err(dev, ntf_params, + "CDB functionality is not supported", + NULL); return -EOPNOTSUPP; + } cdb->read_write_len_ext = rpl.read_write_len_ext; @@ -178,7 +187,7 @@ cmis_cdb_validate_password(struct ethtool_cmis_cdb *cdb, } ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_QUERY_STATUS, - (u8 *)&qs_pl, sizeof(qs_pl), 0, + (u8 *)&qs_pl, sizeof(qs_pl), NULL, 0, 0, cdb->read_write_len_ext, 1000, sizeof(*rpl), CDB_F_COMPLETION_VALID | CDB_F_STATUS_VALID); @@ -240,8 +249,9 @@ static int cmis_cdb_module_features_get(struct ethtool_cmis_cdb *cdb, ethtool_cmis_cdb_check_completion_flag(cdb->cmis_rev, &flags); ethtool_cmis_cdb_compose_args(&args, ETHTOOL_CMIS_CDB_CMD_MODULE_FEATURES, - NULL, 0, 0, cdb->read_write_len_ext, - 1000, sizeof(*rpl), flags); + NULL, 0, NULL, 0, 0, + cdb->read_write_len_ext, 1000, + sizeof(*rpl), flags); err = ethtool_cmis_cdb_execute_cmd(dev, &args); if (err < 0) { @@ -282,7 +292,7 @@ ethtool_cmis_cdb_init(struct net_device *dev, goto err; } - err = cmis_cdb_advertisement_get(cdb, dev); + err = cmis_cdb_advertisement_get(cdb, dev, ntf_params); if (err < 0) goto err; @@ -341,7 +351,7 @@ ethtool_cmis_module_poll(struct net_device *dev, struct netlink_ext_ack extack = {}; int err; - ethtool_cmis_page_init(&page_data, 0, offset, sizeof(rpl)); + ethtool_cmis_page_init(&page_data, 0, offset, sizeof(*rpl)); page_data.data = (u8 *)rpl; err = ops->get_module_eeprom_by_page(dev, &page_data, &extack); @@ -444,6 +454,9 @@ static void cmis_cdb_status_fail_msg_get(u8 status, char **err_msg) case 0b01000101: *err_msg = "CDB status failed: CdbChkCode error"; break; + case 0b01000110: + *err_msg = "CDB status failed: Password error"; + break; default: *err_msg = "Unknown failure reason"; } @@ -538,6 +551,49 @@ __ethtool_cmis_cdb_execute_cmd(struct net_device *dev, return err; } +#define CMIS_CDB_EPL_PAGE_START 0xA0 +#define CMIS_CDB_EPL_PAGE_END 0xAF +#define CMIS_CDB_EPL_FW_BLOCK_OFFSET_START 128 +#define CMIS_CDB_EPL_FW_BLOCK_OFFSET_END 255 + +static int +ethtool_cmis_cdb_execute_epl_cmd(struct net_device *dev, + struct ethtool_cmis_cdb_cmd_args *args, + struct ethtool_module_eeprom *page_data) +{ + u16 epl_len = be16_to_cpu(args->req.epl_len); + u32 bytes_written = 0; + u8 page; + int err; + + for (page = CMIS_CDB_EPL_PAGE_START; + page <= CMIS_CDB_EPL_PAGE_END && bytes_written < epl_len; page++) { + u16 offset = CMIS_CDB_EPL_FW_BLOCK_OFFSET_START; + + while (offset <= CMIS_CDB_EPL_FW_BLOCK_OFFSET_END && + bytes_written < epl_len) { + u32 bytes_left = epl_len - bytes_written; + u16 space_left, bytes_to_write; + + space_left = CMIS_CDB_EPL_FW_BLOCK_OFFSET_END - offset + 1; + bytes_to_write = min_t(u16, bytes_left, + min_t(u16, space_left, + args->read_write_len_ext)); + + err = __ethtool_cmis_cdb_execute_cmd(dev, page_data, + page, offset, + bytes_to_write, + args->req.epl + bytes_written); + if (err < 0) + return err; + + offset += bytes_to_write; + bytes_written += bytes_to_write; + } + } + return 0; +} + static u8 cmis_cdb_calc_checksum(const void *data, size_t size) { const u8 *bytes = (const u8 *)data; @@ -559,7 +615,9 @@ int ethtool_cmis_cdb_execute_cmd(struct net_device *dev, int err; args->req.chk_code = - cmis_cdb_calc_checksum(&args->req, sizeof(args->req)); + cmis_cdb_calc_checksum(&args->req, + offsetof(struct ethtool_cmis_cdb_request, + epl)); if (args->req.lpl_len > args->read_write_len_ext) { args->err_msg = "LPL length is longer than CDB read write length extension allows"; @@ -581,6 +639,12 @@ int ethtool_cmis_cdb_execute_cmd(struct net_device *dev, if (err < 0) return err; + if (args->req.epl_len) { + err = ethtool_cmis_cdb_execute_epl_cmd(dev, args, &page_data); + if (err < 0) + return err; + } + offset = CMIS_CDB_CMD_ID_OFFSET + offsetof(struct ethtool_cmis_cdb_request, id); err = __ethtool_cmis_cdb_execute_cmd(dev, &page_data, |
