1 /* 2 * Copyright (c) 2009-2015 Solarflare Communications Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * The views and conclusions contained in the software and documentation are 27 * those of the authors and should not be interpreted as representing official 28 * policies, either expressed or implied, of the FreeBSD Project. 29 */ 30 31 #include "efx.h" 32 #include "efx_impl.h" 33 34 #if EFSYS_OPT_WOL 35 36 __checkReturn efx_rc_t 37 efx_wol_init( 38 __in efx_nic_t *enp) 39 { 40 efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 41 efx_rc_t rc; 42 43 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 44 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 45 EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_WOL)); 46 47 if (~(encp->enc_features) & EFX_FEATURE_WOL) { 48 rc = ENOTSUP; 49 goto fail1; 50 } 51 52 /* Current implementation is Siena specific */ 53 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 54 55 enp->en_mod_flags |= EFX_MOD_WOL; 56 57 return (0); 58 59 fail1: 60 EFSYS_PROBE1(fail1, efx_rc_t, rc); 61 62 return (rc); 63 } 64 65 __checkReturn efx_rc_t 66 efx_wol_filter_clear( 67 __in efx_nic_t *enp) 68 { 69 efx_mcdi_req_t req; 70 uint8_t payload[MAX(MC_CMD_WOL_FILTER_RESET_IN_LEN, 71 MC_CMD_WOL_FILTER_RESET_OUT_LEN)]; 72 efx_rc_t rc; 73 74 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 75 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 76 77 (void) memset(payload, 0, sizeof (payload)); 78 req.emr_cmd = MC_CMD_WOL_FILTER_RESET; 79 req.emr_in_buf = payload; 80 req.emr_in_length = MC_CMD_WOL_FILTER_RESET_IN_LEN; 81 req.emr_out_buf = payload; 82 req.emr_out_length = MC_CMD_WOL_FILTER_RESET_OUT_LEN; 83 84 MCDI_IN_SET_DWORD(req, WOL_FILTER_RESET_IN_MASK, 85 MC_CMD_WOL_FILTER_RESET_IN_WAKE_FILTERS | 86 MC_CMD_WOL_FILTER_RESET_IN_LIGHTSOUT_OFFLOADS); 87 88 efx_mcdi_execute(enp, &req); 89 90 if (req.emr_rc != 0) { 91 rc = req.emr_rc; 92 goto fail1; 93 } 94 95 return (0); 96 97 fail1: 98 EFSYS_PROBE1(fail1, efx_rc_t, rc); 99 100 return (rc); 101 } 102 103 __checkReturn efx_rc_t 104 efx_wol_filter_add( 105 __in efx_nic_t *enp, 106 __in efx_wol_type_t type, 107 __in efx_wol_param_t *paramp, 108 __out uint32_t *filter_idp) 109 { 110 efx_mcdi_req_t req; 111 uint8_t payload[MAX(MC_CMD_WOL_FILTER_SET_IN_LEN, 112 MC_CMD_WOL_FILTER_SET_OUT_LEN)]; 113 efx_byte_t link_mask; 114 efx_rc_t rc; 115 116 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 117 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 118 119 (void) memset(payload, 0, sizeof (payload)); 120 req.emr_cmd = MC_CMD_WOL_FILTER_SET; 121 req.emr_in_buf = payload; 122 req.emr_in_length = MC_CMD_WOL_FILTER_SET_IN_LEN; 123 req.emr_out_buf = payload; 124 req.emr_out_length = MC_CMD_WOL_FILTER_SET_OUT_LEN; 125 126 switch (type) { 127 case EFX_WOL_TYPE_MAGIC: 128 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 129 MC_CMD_FILTER_MODE_SIMPLE); 130 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 131 MC_CMD_WOL_TYPE_MAGIC); 132 EFX_MAC_ADDR_COPY( 133 MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_MAGIC_MAC), 134 paramp->ewp_magic.mac_addr); 135 break; 136 137 case EFX_WOL_TYPE_BITMAP: { 138 uint32_t swapped = 0; 139 efx_dword_t *dwordp; 140 unsigned int pos, bit; 141 142 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 143 MC_CMD_FILTER_MODE_SIMPLE); 144 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 145 MC_CMD_WOL_TYPE_BITMAP); 146 147 /* 148 * MC bitmask is supposed to be bit swapped 149 * amongst 32 bit words(!) 150 */ 151 152 dwordp = MCDI_IN2(req, efx_dword_t, 153 WOL_FILTER_SET_IN_BITMAP_MASK); 154 155 EFSYS_ASSERT3U(EFX_WOL_BITMAP_MASK_SIZE % 4, ==, 0); 156 157 for (pos = 0; pos < EFX_WOL_BITMAP_MASK_SIZE; ++pos) { 158 uint8_t native = paramp->ewp_bitmap.mask[pos]; 159 160 for (bit = 0; bit < 8; ++bit) { 161 swapped <<= 1; 162 swapped |= (native & 0x1); 163 native >>= 1; 164 } 165 166 if ((pos & 3) == 3) { 167 EFX_POPULATE_DWORD_1(dwordp[pos >> 2], 168 EFX_DWORD_0, swapped); 169 swapped = 0; 170 } 171 } 172 173 (void) memcpy(MCDI_IN2(req, uint8_t, 174 WOL_FILTER_SET_IN_BITMAP_BITMAP), 175 paramp->ewp_bitmap.value, 176 sizeof (paramp->ewp_bitmap.value)); 177 178 EFSYS_ASSERT3U(paramp->ewp_bitmap.value_len, <=, 179 sizeof (paramp->ewp_bitmap.value)); 180 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_BITMAP_LEN, 181 paramp->ewp_bitmap.value_len); 182 } 183 break; 184 185 case EFX_WOL_TYPE_LINK: 186 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 187 MC_CMD_FILTER_MODE_SIMPLE); 188 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 189 MC_CMD_WOL_TYPE_LINK); 190 191 EFX_ZERO_BYTE(link_mask); 192 EFX_SET_BYTE_FIELD(link_mask, MC_CMD_WOL_FILTER_SET_IN_LINK_UP, 193 1); 194 MCDI_IN_SET_BYTE(req, WOL_FILTER_SET_IN_LINK_MASK, 195 link_mask.eb_u8[0]); 196 break; 197 198 default: 199 EFSYS_ASSERT3U(type, !=, type); 200 } 201 202 efx_mcdi_execute(enp, &req); 203 204 if (req.emr_rc != 0) { 205 rc = req.emr_rc; 206 goto fail1; 207 } 208 209 if (req.emr_out_length_used < MC_CMD_WOL_FILTER_SET_OUT_LEN) { 210 rc = EMSGSIZE; 211 goto fail2; 212 } 213 214 *filter_idp = MCDI_OUT_DWORD(req, WOL_FILTER_SET_OUT_FILTER_ID); 215 216 return (0); 217 218 fail2: 219 EFSYS_PROBE(fail2); 220 fail1: 221 EFSYS_PROBE1(fail1, efx_rc_t, rc); 222 223 return (rc); 224 } 225 226 __checkReturn efx_rc_t 227 efx_wol_filter_remove( 228 __in efx_nic_t *enp, 229 __in uint32_t filter_id) 230 { 231 efx_mcdi_req_t req; 232 uint8_t payload[MAX(MC_CMD_WOL_FILTER_REMOVE_IN_LEN, 233 MC_CMD_WOL_FILTER_REMOVE_OUT_LEN)]; 234 efx_rc_t rc; 235 236 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 237 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 238 239 (void) memset(payload, 0, sizeof (payload)); 240 req.emr_cmd = MC_CMD_WOL_FILTER_REMOVE; 241 req.emr_in_buf = payload; 242 req.emr_in_length = MC_CMD_WOL_FILTER_REMOVE_IN_LEN; 243 req.emr_out_buf = payload; 244 req.emr_out_length = MC_CMD_WOL_FILTER_REMOVE_OUT_LEN; 245 246 MCDI_IN_SET_DWORD(req, WOL_FILTER_REMOVE_IN_FILTER_ID, filter_id); 247 248 efx_mcdi_execute(enp, &req); 249 250 if (req.emr_rc != 0) { 251 rc = req.emr_rc; 252 goto fail1; 253 } 254 255 return (0); 256 257 fail1: 258 EFSYS_PROBE1(fail1, efx_rc_t, rc); 259 260 return (rc); 261 } 262 263 264 __checkReturn efx_rc_t 265 efx_lightsout_offload_add( 266 __in efx_nic_t *enp, 267 __in efx_lightsout_offload_type_t type, 268 __in efx_lightsout_offload_param_t *paramp, 269 __out uint32_t *filter_idp) 270 { 271 efx_mcdi_req_t req; 272 uint8_t payload[MAX(MAX(MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN, 273 MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN), 274 MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN)]; 275 efx_rc_t rc; 276 277 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 278 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 279 280 (void) memset(payload, 0, sizeof (payload)); 281 req.emr_cmd = MC_CMD_ADD_LIGHTSOUT_OFFLOAD; 282 req.emr_in_buf = payload; 283 req.emr_in_length = sizeof (type); 284 req.emr_out_buf = payload; 285 req.emr_out_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN; 286 287 switch (type) { 288 case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP: 289 req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN; 290 291 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 292 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP); 293 EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t, 294 ADD_LIGHTSOUT_OFFLOAD_IN_ARP_MAC), 295 paramp->elop_arp.mac_addr); 296 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_ARP_IP, 297 paramp->elop_arp.ip); 298 break; 299 case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS: 300 req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN; 301 302 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 303 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS); 304 EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t, 305 ADD_LIGHTSOUT_OFFLOAD_IN_NS_MAC), 306 paramp->elop_ns.mac_addr); 307 (void) memcpy(MCDI_IN2(req, uint8_t, 308 ADD_LIGHTSOUT_OFFLOAD_IN_NS_SNIPV6), 309 paramp->elop_ns.solicited_node, 310 sizeof (paramp->elop_ns.solicited_node)); 311 (void) memcpy(MCDI_IN2(req, uint8_t, 312 ADD_LIGHTSOUT_OFFLOAD_IN_NS_IPV6), 313 paramp->elop_ns.ip, sizeof (paramp->elop_ns.ip)); 314 break; 315 default: 316 rc = EINVAL; 317 goto fail1; 318 } 319 320 efx_mcdi_execute(enp, &req); 321 322 if (req.emr_rc != 0) { 323 rc = req.emr_rc; 324 goto fail2; 325 } 326 327 if (req.emr_out_length_used < MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN) { 328 rc = EMSGSIZE; 329 goto fail3; 330 } 331 332 *filter_idp = MCDI_OUT_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID); 333 334 return (0); 335 336 fail3: 337 EFSYS_PROBE(fail3); 338 fail2: 339 EFSYS_PROBE(fail2); 340 fail1: 341 EFSYS_PROBE1(fail1, efx_rc_t, rc); 342 343 return (rc); 344 } 345 346 347 __checkReturn efx_rc_t 348 efx_lightsout_offload_remove( 349 __in efx_nic_t *enp, 350 __in efx_lightsout_offload_type_t type, 351 __in uint32_t filter_id) 352 { 353 efx_mcdi_req_t req; 354 uint8_t payload[MAX(MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN, 355 MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN)]; 356 efx_rc_t rc; 357 358 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 359 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 360 361 (void) memset(payload, 0, sizeof (payload)); 362 req.emr_cmd = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD; 363 req.emr_in_buf = payload; 364 req.emr_in_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN; 365 req.emr_out_buf = payload; 366 req.emr_out_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN; 367 368 switch (type) { 369 case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP: 370 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 371 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP); 372 break; 373 case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS: 374 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 375 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS); 376 break; 377 default: 378 rc = EINVAL; 379 goto fail1; 380 } 381 382 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID, 383 filter_id); 384 385 efx_mcdi_execute(enp, &req); 386 387 if (req.emr_rc != 0) { 388 rc = req.emr_rc; 389 goto fail2; 390 } 391 392 return (0); 393 394 fail2: 395 EFSYS_PROBE(fail2); 396 fail1: 397 EFSYS_PROBE1(fail1, efx_rc_t, rc); 398 399 return (rc); 400 } 401 402 403 void 404 efx_wol_fini( 405 __in efx_nic_t *enp) 406 { 407 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 408 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 409 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 410 411 enp->en_mod_flags &= ~EFX_MOD_WOL; 412 } 413 414 #endif /* EFSYS_OPT_WOL */