1/*- 2 * Copyright (c) 2009-2016 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 <sys/cdefs.h> 32__FBSDID("$FreeBSD: releng/11.0/sys/dev/sfxge/common/efx_wol.c 300607 2016-05-24 12:16:57Z arybchik $"); 33 34#include "efx.h" 35#include "efx_impl.h" 36 37#if EFSYS_OPT_WOL 38 39 __checkReturn efx_rc_t 40efx_wol_init( 41 __in efx_nic_t *enp) 42{ 43 efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 44 efx_rc_t rc; 45 46 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 47 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 48 EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_WOL)); 49 50 if (~(encp->enc_features) & EFX_FEATURE_WOL) { 51 rc = ENOTSUP; 52 goto fail1; 53 } 54 55 /* Current implementation is Siena specific */ 56 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 57 58 enp->en_mod_flags |= EFX_MOD_WOL; 59 60 return (0); 61 62fail1: 63 EFSYS_PROBE1(fail1, efx_rc_t, rc); 64 65 return (rc); 66} 67 68 __checkReturn efx_rc_t 69efx_wol_filter_clear( 70 __in efx_nic_t *enp) 71{ 72 efx_mcdi_req_t req; 73 uint8_t payload[MAX(MC_CMD_WOL_FILTER_RESET_IN_LEN, 74 MC_CMD_WOL_FILTER_RESET_OUT_LEN)]; 75 efx_rc_t rc; 76 77 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 78 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 79 80 (void) memset(payload, 0, sizeof (payload)); 81 req.emr_cmd = MC_CMD_WOL_FILTER_RESET; 82 req.emr_in_buf = payload; 83 req.emr_in_length = MC_CMD_WOL_FILTER_RESET_IN_LEN; 84 req.emr_out_buf = payload; 85 req.emr_out_length = MC_CMD_WOL_FILTER_RESET_OUT_LEN; 86 87 MCDI_IN_SET_DWORD(req, WOL_FILTER_RESET_IN_MASK, 88 MC_CMD_WOL_FILTER_RESET_IN_WAKE_FILTERS | 89 MC_CMD_WOL_FILTER_RESET_IN_LIGHTSOUT_OFFLOADS); 90 91 efx_mcdi_execute(enp, &req); 92 93 if (req.emr_rc != 0) { 94 rc = req.emr_rc; 95 goto fail1; 96 } 97 98 return (0); 99 100fail1: 101 EFSYS_PROBE1(fail1, efx_rc_t, rc); 102 103 return (rc); 104} 105 106 __checkReturn efx_rc_t 107efx_wol_filter_add( 108 __in efx_nic_t *enp, 109 __in efx_wol_type_t type, 110 __in efx_wol_param_t *paramp, 111 __out uint32_t *filter_idp) 112{ 113 efx_mcdi_req_t req; 114 uint8_t payload[MAX(MC_CMD_WOL_FILTER_SET_IN_LEN, 115 MC_CMD_WOL_FILTER_SET_OUT_LEN)]; 116 efx_byte_t link_mask; 117 efx_rc_t rc; 118 119 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 120 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 121 122 (void) memset(payload, 0, sizeof (payload)); 123 req.emr_cmd = MC_CMD_WOL_FILTER_SET; 124 req.emr_in_buf = payload; 125 req.emr_in_length = MC_CMD_WOL_FILTER_SET_IN_LEN; 126 req.emr_out_buf = payload; 127 req.emr_out_length = MC_CMD_WOL_FILTER_SET_OUT_LEN; 128 129 switch (type) { 130 case EFX_WOL_TYPE_MAGIC: 131 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 132 MC_CMD_FILTER_MODE_SIMPLE); 133 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 134 MC_CMD_WOL_TYPE_MAGIC); 135 EFX_MAC_ADDR_COPY( 136 MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_MAGIC_MAC), 137 paramp->ewp_magic.mac_addr); 138 break; 139 140 case EFX_WOL_TYPE_BITMAP: { 141 uint32_t swapped = 0; 142 efx_dword_t *dwordp; 143 unsigned int pos, bit; 144 145 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 146 MC_CMD_FILTER_MODE_SIMPLE); 147 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 148 MC_CMD_WOL_TYPE_BITMAP); 149 150 /* 151 * MC bitmask is supposed to be bit swapped 152 * amongst 32 bit words(!) 153 */ 154 155 dwordp = MCDI_IN2(req, efx_dword_t, 156 WOL_FILTER_SET_IN_BITMAP_MASK); 157 158 EFSYS_ASSERT3U(EFX_WOL_BITMAP_MASK_SIZE % 4, ==, 0); 159 160 for (pos = 0; pos < EFX_WOL_BITMAP_MASK_SIZE; ++pos) { 161 uint8_t native = paramp->ewp_bitmap.mask[pos]; 162 163 for (bit = 0; bit < 8; ++bit) { 164 swapped <<= 1; 165 swapped |= (native & 0x1); 166 native >>= 1; 167 } 168 169 if ((pos & 3) == 3) { 170 EFX_POPULATE_DWORD_1(dwordp[pos >> 2], 171 EFX_DWORD_0, swapped); 172 swapped = 0; 173 } 174 } 175 176 memcpy(MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_BITMAP_BITMAP), 177 paramp->ewp_bitmap.value, 178 sizeof (paramp->ewp_bitmap.value)); 179 180 EFSYS_ASSERT3U(paramp->ewp_bitmap.value_len, <=, 181 sizeof (paramp->ewp_bitmap.value)); 182 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_BITMAP_LEN, 183 paramp->ewp_bitmap.value_len); 184 } 185 break; 186 187 case EFX_WOL_TYPE_LINK: 188 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 189 MC_CMD_FILTER_MODE_SIMPLE); 190 MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 191 MC_CMD_WOL_TYPE_LINK); 192 193 EFX_ZERO_BYTE(link_mask); 194 EFX_SET_BYTE_FIELD(link_mask, MC_CMD_WOL_FILTER_SET_IN_LINK_UP, 195 1); 196 MCDI_IN_SET_BYTE(req, WOL_FILTER_SET_IN_LINK_MASK, 197 link_mask.eb_u8[0]); 198 break; 199 200 default: 201 EFSYS_ASSERT3U(type, !=, type); 202 } 203 204 efx_mcdi_execute(enp, &req); 205 206 if (req.emr_rc != 0) { 207 rc = req.emr_rc; 208 goto fail1; 209 } 210 211 if (req.emr_out_length_used < MC_CMD_WOL_FILTER_SET_OUT_LEN) { 212 rc = EMSGSIZE; 213 goto fail2; 214 } 215 216 *filter_idp = MCDI_OUT_DWORD(req, WOL_FILTER_SET_OUT_FILTER_ID); 217 218 return (0); 219 220fail2: 221 EFSYS_PROBE(fail2); 222fail1: 223 EFSYS_PROBE1(fail1, efx_rc_t, rc); 224 225 return (rc); 226} 227 228 __checkReturn efx_rc_t 229efx_wol_filter_remove( 230 __in efx_nic_t *enp, 231 __in uint32_t filter_id) 232{ 233 efx_mcdi_req_t req; 234 uint8_t payload[MAX(MC_CMD_WOL_FILTER_REMOVE_IN_LEN, 235 MC_CMD_WOL_FILTER_REMOVE_OUT_LEN)]; 236 efx_rc_t rc; 237 238 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 239 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 240 241 (void) memset(payload, 0, sizeof (payload)); 242 req.emr_cmd = MC_CMD_WOL_FILTER_REMOVE; 243 req.emr_in_buf = payload; 244 req.emr_in_length = MC_CMD_WOL_FILTER_REMOVE_IN_LEN; 245 req.emr_out_buf = payload; 246 req.emr_out_length = MC_CMD_WOL_FILTER_REMOVE_OUT_LEN; 247 248 MCDI_IN_SET_DWORD(req, WOL_FILTER_REMOVE_IN_FILTER_ID, filter_id); 249 250 efx_mcdi_execute(enp, &req); 251 252 if (req.emr_rc != 0) { 253 rc = req.emr_rc; 254 goto fail1; 255 } 256 257 return (0); 258 259fail1: 260 EFSYS_PROBE1(fail1, efx_rc_t, rc); 261 262 return (rc); 263} 264 265 266 __checkReturn efx_rc_t 267efx_lightsout_offload_add( 268 __in efx_nic_t *enp, 269 __in efx_lightsout_offload_type_t type, 270 __in efx_lightsout_offload_param_t *paramp, 271 __out uint32_t *filter_idp) 272{ 273 efx_mcdi_req_t req; 274 uint8_t payload[MAX(MAX(MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN, 275 MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN), 276 MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN)]; 277 efx_rc_t rc; 278 279 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 280 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 281 282 (void) memset(payload, 0, sizeof (payload)); 283 req.emr_cmd = MC_CMD_ADD_LIGHTSOUT_OFFLOAD; 284 req.emr_in_buf = payload; 285 req.emr_in_length = sizeof (type); 286 req.emr_out_buf = payload; 287 req.emr_out_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN; 288 289 switch (type) { 290 case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP: 291 req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN; 292 293 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 294 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP); 295 EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t, 296 ADD_LIGHTSOUT_OFFLOAD_IN_ARP_MAC), 297 paramp->elop_arp.mac_addr); 298 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_ARP_IP, 299 paramp->elop_arp.ip); 300 break; 301 case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS: 302 req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN; 303 304 MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 305 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS); 306 EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t, 307 ADD_LIGHTSOUT_OFFLOAD_IN_NS_MAC), 308 paramp->elop_ns.mac_addr); 309 memcpy(MCDI_IN2(req, uint8_t, 310 ADD_LIGHTSOUT_OFFLOAD_IN_NS_SNIPV6), 311 paramp->elop_ns.solicited_node, 312 sizeof (paramp->elop_ns.solicited_node)); 313 memcpy(MCDI_IN2(req, uint8_t, ADD_LIGHTSOUT_OFFLOAD_IN_NS_IPV6), 314 paramp->elop_ns.ip, sizeof (paramp->elop_ns.ip)); 315 break; 316 default: 317 rc = EINVAL; 318 goto fail1; 319 } 320 321 efx_mcdi_execute(enp, &req); 322 323 if (req.emr_rc != 0) { 324 rc = req.emr_rc; 325 goto fail2; 326 } 327 328 if (req.emr_out_length_used < MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN) { 329 rc = EMSGSIZE; 330 goto fail3; 331 } 332 333 *filter_idp = MCDI_OUT_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID); 334 335 return (0); 336 337fail3: 338 EFSYS_PROBE(fail3); 339fail2: 340 EFSYS_PROBE(fail2); 341fail1: 342 EFSYS_PROBE1(fail1, efx_rc_t, rc); 343 344 return (rc); 345} 346 347 348 __checkReturn efx_rc_t 349efx_lightsout_offload_remove( 350 __in efx_nic_t *enp, 351 __in efx_lightsout_offload_type_t type, 352 __in uint32_t filter_id) 353{ 354 efx_mcdi_req_t req; 355 uint8_t payload[MAX(MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN, 356 MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN)]; 357 efx_rc_t rc; 358 359 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 360 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 361 362 (void) memset(payload, 0, sizeof (payload)); 363 req.emr_cmd = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD; 364 req.emr_in_buf = payload; 365 req.emr_in_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN; 366 req.emr_out_buf = payload; 367 req.emr_out_length = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN; 368 369 switch (type) { 370 case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP: 371 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 372 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP); 373 break; 374 case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS: 375 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 376 MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS); 377 break; 378 default: 379 rc = EINVAL; 380 goto fail1; 381 } 382 383 MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID, 384 filter_id); 385 386 efx_mcdi_execute(enp, &req); 387 388 if (req.emr_rc != 0) { 389 rc = req.emr_rc; 390 goto fail2; 391 } 392 393 return (0); 394 395fail2: 396 EFSYS_PROBE(fail2); 397fail1: 398 EFSYS_PROBE1(fail1, efx_rc_t, rc); 399 400 return (rc); 401} 402 403 404 void 405efx_wol_fini( 406 __in efx_nic_t *enp) 407{ 408 EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 409 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 410 EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 411 412 enp->en_mod_flags &= ~EFX_MOD_WOL; 413} 414 415#endif /* EFSYS_OPT_WOL */ 416