0001
0002
0003 #include <linux/ethtool.h>
0004 #include <linux/sfp.h>
0005 #include "netlink.h"
0006 #include "common.h"
0007
0008 struct eeprom_req_info {
0009 struct ethnl_req_info base;
0010 u32 offset;
0011 u32 length;
0012 u8 page;
0013 u8 bank;
0014 u8 i2c_address;
0015 };
0016
0017 struct eeprom_reply_data {
0018 struct ethnl_reply_data base;
0019 u32 length;
0020 u8 *data;
0021 };
0022
0023 #define MODULE_EEPROM_REQINFO(__req_base) \
0024 container_of(__req_base, struct eeprom_req_info, base)
0025
0026 #define MODULE_EEPROM_REPDATA(__reply_base) \
0027 container_of(__reply_base, struct eeprom_reply_data, base)
0028
0029 static int fallback_set_params(struct eeprom_req_info *request,
0030 struct ethtool_modinfo *modinfo,
0031 struct ethtool_eeprom *eeprom)
0032 {
0033 u32 offset = request->offset;
0034 u32 length = request->length;
0035
0036 if (request->page)
0037 offset = request->page * ETH_MODULE_EEPROM_PAGE_LEN + offset;
0038
0039 if (modinfo->type == ETH_MODULE_SFF_8472 &&
0040 request->i2c_address == 0x51)
0041 offset += ETH_MODULE_EEPROM_PAGE_LEN * 2;
0042
0043 if (offset >= modinfo->eeprom_len)
0044 return -EINVAL;
0045
0046 eeprom->cmd = ETHTOOL_GMODULEEEPROM;
0047 eeprom->len = length;
0048 eeprom->offset = offset;
0049
0050 return 0;
0051 }
0052
0053 static int eeprom_fallback(struct eeprom_req_info *request,
0054 struct eeprom_reply_data *reply,
0055 struct genl_info *info)
0056 {
0057 struct net_device *dev = reply->base.dev;
0058 struct ethtool_modinfo modinfo = {0};
0059 struct ethtool_eeprom eeprom = {0};
0060 u8 *data;
0061 int err;
0062
0063 modinfo.cmd = ETHTOOL_GMODULEINFO;
0064 err = ethtool_get_module_info_call(dev, &modinfo);
0065 if (err < 0)
0066 return err;
0067
0068 err = fallback_set_params(request, &modinfo, &eeprom);
0069 if (err < 0)
0070 return err;
0071
0072 data = kmalloc(eeprom.len, GFP_KERNEL);
0073 if (!data)
0074 return -ENOMEM;
0075 err = ethtool_get_module_eeprom_call(dev, &eeprom, data);
0076 if (err < 0)
0077 goto err_out;
0078
0079 reply->data = data;
0080 reply->length = eeprom.len;
0081
0082 return 0;
0083
0084 err_out:
0085 kfree(data);
0086 return err;
0087 }
0088
0089 static int get_module_eeprom_by_page(struct net_device *dev,
0090 struct ethtool_module_eeprom *page_data,
0091 struct netlink_ext_ack *extack)
0092 {
0093 const struct ethtool_ops *ops = dev->ethtool_ops;
0094
0095 if (dev->sfp_bus)
0096 return sfp_get_module_eeprom_by_page(dev->sfp_bus, page_data, extack);
0097
0098 if (ops->get_module_eeprom_by_page)
0099 return ops->get_module_eeprom_by_page(dev, page_data, extack);
0100
0101 return -EOPNOTSUPP;
0102 }
0103
0104 static int eeprom_prepare_data(const struct ethnl_req_info *req_base,
0105 struct ethnl_reply_data *reply_base,
0106 struct genl_info *info)
0107 {
0108 struct eeprom_reply_data *reply = MODULE_EEPROM_REPDATA(reply_base);
0109 struct eeprom_req_info *request = MODULE_EEPROM_REQINFO(req_base);
0110 struct ethtool_module_eeprom page_data = {0};
0111 struct net_device *dev = reply_base->dev;
0112 int ret;
0113
0114 page_data.offset = request->offset;
0115 page_data.length = request->length;
0116 page_data.i2c_address = request->i2c_address;
0117 page_data.page = request->page;
0118 page_data.bank = request->bank;
0119 page_data.data = kmalloc(page_data.length, GFP_KERNEL);
0120 if (!page_data.data)
0121 return -ENOMEM;
0122
0123 ret = ethnl_ops_begin(dev);
0124 if (ret)
0125 goto err_free;
0126
0127 ret = get_module_eeprom_by_page(dev, &page_data, info->extack);
0128 if (ret < 0)
0129 goto err_ops;
0130
0131 reply->length = ret;
0132 reply->data = page_data.data;
0133
0134 ethnl_ops_complete(dev);
0135 return 0;
0136
0137 err_ops:
0138 ethnl_ops_complete(dev);
0139 err_free:
0140 kfree(page_data.data);
0141
0142 if (ret == -EOPNOTSUPP)
0143 return eeprom_fallback(request, reply, info);
0144 return ret;
0145 }
0146
0147 static int eeprom_parse_request(struct ethnl_req_info *req_info, struct nlattr **tb,
0148 struct netlink_ext_ack *extack)
0149 {
0150 struct eeprom_req_info *request = MODULE_EEPROM_REQINFO(req_info);
0151
0152 if (!tb[ETHTOOL_A_MODULE_EEPROM_OFFSET] ||
0153 !tb[ETHTOOL_A_MODULE_EEPROM_LENGTH] ||
0154 !tb[ETHTOOL_A_MODULE_EEPROM_PAGE] ||
0155 !tb[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS])
0156 return -EINVAL;
0157
0158 request->i2c_address = nla_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS]);
0159 request->offset = nla_get_u32(tb[ETHTOOL_A_MODULE_EEPROM_OFFSET]);
0160 request->length = nla_get_u32(tb[ETHTOOL_A_MODULE_EEPROM_LENGTH]);
0161
0162
0163
0164
0165
0166
0167
0168 request->page = nla_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_PAGE]);
0169 if (request->page && request->offset < ETH_MODULE_EEPROM_PAGE_LEN) {
0170 NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MODULE_EEPROM_PAGE],
0171 "reading from lower half page is allowed for page 0 only");
0172 return -EINVAL;
0173 }
0174
0175 if (request->offset < ETH_MODULE_EEPROM_PAGE_LEN &&
0176 request->offset + request->length > ETH_MODULE_EEPROM_PAGE_LEN) {
0177 NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MODULE_EEPROM_LENGTH],
0178 "reading cross half page boundary is illegal");
0179 return -EINVAL;
0180 } else if (request->offset + request->length > ETH_MODULE_EEPROM_PAGE_LEN * 2) {
0181 NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_MODULE_EEPROM_LENGTH],
0182 "reading cross page boundary is illegal");
0183 return -EINVAL;
0184 }
0185
0186 if (tb[ETHTOOL_A_MODULE_EEPROM_BANK])
0187 request->bank = nla_get_u8(tb[ETHTOOL_A_MODULE_EEPROM_BANK]);
0188
0189 return 0;
0190 }
0191
0192 static int eeprom_reply_size(const struct ethnl_req_info *req_base,
0193 const struct ethnl_reply_data *reply_base)
0194 {
0195 const struct eeprom_req_info *request = MODULE_EEPROM_REQINFO(req_base);
0196
0197 return nla_total_size(sizeof(u8) * request->length);
0198 }
0199
0200 static int eeprom_fill_reply(struct sk_buff *skb,
0201 const struct ethnl_req_info *req_base,
0202 const struct ethnl_reply_data *reply_base)
0203 {
0204 struct eeprom_reply_data *reply = MODULE_EEPROM_REPDATA(reply_base);
0205
0206 return nla_put(skb, ETHTOOL_A_MODULE_EEPROM_DATA, reply->length, reply->data);
0207 }
0208
0209 static void eeprom_cleanup_data(struct ethnl_reply_data *reply_base)
0210 {
0211 struct eeprom_reply_data *reply = MODULE_EEPROM_REPDATA(reply_base);
0212
0213 kfree(reply->data);
0214 }
0215
0216 const struct ethnl_request_ops ethnl_module_eeprom_request_ops = {
0217 .request_cmd = ETHTOOL_MSG_MODULE_EEPROM_GET,
0218 .reply_cmd = ETHTOOL_MSG_MODULE_EEPROM_GET_REPLY,
0219 .hdr_attr = ETHTOOL_A_MODULE_EEPROM_HEADER,
0220 .req_info_size = sizeof(struct eeprom_req_info),
0221 .reply_data_size = sizeof(struct eeprom_reply_data),
0222
0223 .parse_request = eeprom_parse_request,
0224 .prepare_data = eeprom_prepare_data,
0225 .reply_size = eeprom_reply_size,
0226 .fill_reply = eeprom_fill_reply,
0227 .cleanup_data = eeprom_cleanup_data,
0228 };
0229
0230 const struct nla_policy ethnl_module_eeprom_get_policy[] = {
0231 [ETHTOOL_A_MODULE_EEPROM_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
0232 [ETHTOOL_A_MODULE_EEPROM_OFFSET] =
0233 NLA_POLICY_MAX(NLA_U32, ETH_MODULE_EEPROM_PAGE_LEN * 2 - 1),
0234 [ETHTOOL_A_MODULE_EEPROM_LENGTH] =
0235 NLA_POLICY_RANGE(NLA_U32, 1, ETH_MODULE_EEPROM_PAGE_LEN),
0236 [ETHTOOL_A_MODULE_EEPROM_PAGE] = { .type = NLA_U8 },
0237 [ETHTOOL_A_MODULE_EEPROM_BANK] = { .type = NLA_U8 },
0238 [ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS] =
0239 NLA_POLICY_RANGE(NLA_U8, 0, ETH_MODULE_MAX_I2C_ADDRESS),
0240 };
0241