1 // SPDX-License-Identifier: GPL-2.0-only 2 3 #include "netlink.h" 4 #include "common.h" 5 #include "bitset.h" 6 7 #define EEE_MODES_COUNT \ 8 (sizeof_field(struct ethtool_eee, supported) * BITS_PER_BYTE) 9 10 struct eee_req_info { 11 struct ethnl_req_info base; 12 }; 13 14 struct eee_reply_data { 15 struct ethnl_reply_data base; 16 struct ethtool_eee eee; 17 }; 18 19 #define EEE_REPDATA(__reply_base) \ 20 container_of(__reply_base, struct eee_reply_data, base) 21 22 static const struct nla_policy 23 eee_get_policy[ETHTOOL_A_EEE_MAX + 1] = { 24 [ETHTOOL_A_EEE_UNSPEC] = { .type = NLA_REJECT }, 25 [ETHTOOL_A_EEE_HEADER] = { .type = NLA_NESTED }, 26 [ETHTOOL_A_EEE_MODES_OURS] = { .type = NLA_REJECT }, 27 [ETHTOOL_A_EEE_MODES_PEER] = { .type = NLA_REJECT }, 28 [ETHTOOL_A_EEE_ACTIVE] = { .type = NLA_REJECT }, 29 [ETHTOOL_A_EEE_ENABLED] = { .type = NLA_REJECT }, 30 [ETHTOOL_A_EEE_TX_LPI_ENABLED] = { .type = NLA_REJECT }, 31 [ETHTOOL_A_EEE_TX_LPI_TIMER] = { .type = NLA_REJECT }, 32 }; 33 34 static int eee_prepare_data(const struct ethnl_req_info *req_base, 35 struct ethnl_reply_data *reply_base, 36 struct genl_info *info) 37 { 38 struct eee_reply_data *data = EEE_REPDATA(reply_base); 39 struct net_device *dev = reply_base->dev; 40 int ret; 41 42 if (!dev->ethtool_ops->get_eee) 43 return -EOPNOTSUPP; 44 ret = ethnl_ops_begin(dev); 45 if (ret < 0) 46 return ret; 47 ret = dev->ethtool_ops->get_eee(dev, &data->eee); 48 ethnl_ops_complete(dev); 49 50 return ret; 51 } 52 53 static int eee_reply_size(const struct ethnl_req_info *req_base, 54 const struct ethnl_reply_data *reply_base) 55 { 56 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 57 const struct eee_reply_data *data = EEE_REPDATA(reply_base); 58 const struct ethtool_eee *eee = &data->eee; 59 int len = 0; 60 int ret; 61 62 BUILD_BUG_ON(sizeof(eee->advertised) * BITS_PER_BYTE != 63 EEE_MODES_COUNT); 64 BUILD_BUG_ON(sizeof(eee->lp_advertised) * BITS_PER_BYTE != 65 EEE_MODES_COUNT); 66 67 /* MODES_OURS */ 68 ret = ethnl_bitset32_size(&eee->advertised, &eee->supported, 69 EEE_MODES_COUNT, link_mode_names, compact); 70 if (ret < 0) 71 return ret; 72 len += ret; 73 /* MODES_PEERS */ 74 ret = ethnl_bitset32_size(&eee->lp_advertised, NULL, 75 EEE_MODES_COUNT, link_mode_names, compact); 76 if (ret < 0) 77 return ret; 78 len += ret; 79 80 len += nla_total_size(sizeof(u8)) + /* _EEE_ACTIVE */ 81 nla_total_size(sizeof(u8)) + /* _EEE_ENABLED */ 82 nla_total_size(sizeof(u8)) + /* _EEE_TX_LPI_ENABLED */ 83 nla_total_size(sizeof(u32)); /* _EEE_TX_LPI_TIMER */ 84 85 return len; 86 } 87 88 static int eee_fill_reply(struct sk_buff *skb, 89 const struct ethnl_req_info *req_base, 90 const struct ethnl_reply_data *reply_base) 91 { 92 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS; 93 const struct eee_reply_data *data = EEE_REPDATA(reply_base); 94 const struct ethtool_eee *eee = &data->eee; 95 int ret; 96 97 ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_OURS, 98 &eee->advertised, &eee->supported, 99 EEE_MODES_COUNT, link_mode_names, compact); 100 if (ret < 0) 101 return ret; 102 ret = ethnl_put_bitset32(skb, ETHTOOL_A_EEE_MODES_PEER, 103 &eee->lp_advertised, NULL, EEE_MODES_COUNT, 104 link_mode_names, compact); 105 if (ret < 0) 106 return ret; 107 108 if (nla_put_u8(skb, ETHTOOL_A_EEE_ACTIVE, !!eee->eee_active) || 109 nla_put_u8(skb, ETHTOOL_A_EEE_ENABLED, !!eee->eee_enabled) || 110 nla_put_u8(skb, ETHTOOL_A_EEE_TX_LPI_ENABLED, 111 !!eee->tx_lpi_enabled) || 112 nla_put_u32(skb, ETHTOOL_A_EEE_TX_LPI_TIMER, eee->tx_lpi_timer)) 113 return -EMSGSIZE; 114 115 return 0; 116 } 117 118 const struct ethnl_request_ops ethnl_eee_request_ops = { 119 .request_cmd = ETHTOOL_MSG_EEE_GET, 120 .reply_cmd = ETHTOOL_MSG_EEE_GET_REPLY, 121 .hdr_attr = ETHTOOL_A_EEE_HEADER, 122 .max_attr = ETHTOOL_A_EEE_MAX, 123 .req_info_size = sizeof(struct eee_req_info), 124 .reply_data_size = sizeof(struct eee_reply_data), 125 .request_policy = eee_get_policy, 126 127 .prepare_data = eee_prepare_data, 128 .reply_size = eee_reply_size, 129 .fill_reply = eee_fill_reply, 130 }; 131 132 /* EEE_SET */ 133 134 static const struct nla_policy 135 eee_set_policy[ETHTOOL_A_EEE_MAX + 1] = { 136 [ETHTOOL_A_EEE_UNSPEC] = { .type = NLA_REJECT }, 137 [ETHTOOL_A_EEE_HEADER] = { .type = NLA_NESTED }, 138 [ETHTOOL_A_EEE_MODES_OURS] = { .type = NLA_NESTED }, 139 [ETHTOOL_A_EEE_MODES_PEER] = { .type = NLA_REJECT }, 140 [ETHTOOL_A_EEE_ACTIVE] = { .type = NLA_REJECT }, 141 [ETHTOOL_A_EEE_ENABLED] = { .type = NLA_U8 }, 142 [ETHTOOL_A_EEE_TX_LPI_ENABLED] = { .type = NLA_U8 }, 143 [ETHTOOL_A_EEE_TX_LPI_TIMER] = { .type = NLA_U32 }, 144 }; 145 146 int ethnl_set_eee(struct sk_buff *skb, struct genl_info *info) 147 { 148 struct nlattr *tb[ETHTOOL_A_EEE_MAX + 1]; 149 struct ethtool_eee eee = {}; 150 struct ethnl_req_info req_info = {}; 151 const struct ethtool_ops *ops; 152 struct net_device *dev; 153 bool mod = false; 154 int ret; 155 156 ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb, ETHTOOL_A_EEE_MAX, 157 eee_set_policy, info->extack); 158 if (ret < 0) 159 return ret; 160 ret = ethnl_parse_header_dev_get(&req_info, 161 tb[ETHTOOL_A_EEE_HEADER], 162 genl_info_net(info), info->extack, 163 true); 164 if (ret < 0) 165 return ret; 166 dev = req_info.dev; 167 ops = dev->ethtool_ops; 168 ret = -EOPNOTSUPP; 169 if (!ops->get_eee || !ops->set_eee) 170 goto out_dev; 171 172 rtnl_lock(); 173 ret = ethnl_ops_begin(dev); 174 if (ret < 0) 175 goto out_rtnl; 176 ret = ops->get_eee(dev, &eee); 177 if (ret < 0) 178 goto out_ops; 179 180 ret = ethnl_update_bitset32(&eee.advertised, EEE_MODES_COUNT, 181 tb[ETHTOOL_A_EEE_MODES_OURS], 182 link_mode_names, info->extack, &mod); 183 if (ret < 0) 184 goto out_ops; 185 ethnl_update_bool32(&eee.eee_enabled, tb[ETHTOOL_A_EEE_ENABLED], &mod); 186 ethnl_update_bool32(&eee.tx_lpi_enabled, 187 tb[ETHTOOL_A_EEE_TX_LPI_ENABLED], &mod); 188 ethnl_update_bool32(&eee.tx_lpi_timer, tb[ETHTOOL_A_EEE_TX_LPI_TIMER], 189 &mod); 190 ret = 0; 191 if (!mod) 192 goto out_ops; 193 194 ret = dev->ethtool_ops->set_eee(dev, &eee); 195 if (ret < 0) 196 goto out_ops; 197 ethtool_notify(dev, ETHTOOL_MSG_EEE_NTF, NULL); 198 199 out_ops: 200 ethnl_ops_complete(dev); 201 out_rtnl: 202 rtnl_unlock(); 203 out_dev: 204 dev_put(dev); 205 return ret; 206 } 207