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