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