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 */