1227569Sphilip/*- 2227569Sphilip * Copyright 2009 Solarflare Communications Inc. All rights reserved. 3227569Sphilip * 4227569Sphilip * Redistribution and use in source and binary forms, with or without 5227569Sphilip * modification, are permitted provided that the following conditions 6227569Sphilip * are met: 7227569Sphilip * 1. Redistributions of source code must retain the above copyright 8227569Sphilip * notice, this list of conditions and the following disclaimer. 9227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright 10227569Sphilip * notice, this list of conditions and the following disclaimer in the 11227569Sphilip * documentation and/or other materials provided with the distribution. 12227569Sphilip * 13227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND 14227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16227569Sphilip * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23227569Sphilip * SUCH DAMAGE. 24227569Sphilip */ 25227569Sphilip 26228078Sphilip#include <sys/cdefs.h> 27228078Sphilip__FBSDID("$FreeBSD$"); 28228078Sphilip 29227569Sphilip#include "efsys.h" 30227569Sphilip#include "efx.h" 31227569Sphilip#include "efx_types.h" 32227569Sphilip#include "efx_impl.h" 33227569Sphilip 34227569Sphilip#if EFSYS_OPT_WOL 35227569Sphilip 36227569Sphilip __checkReturn int 37227569Sphilipefx_wol_init( 38227569Sphilip __in efx_nic_t *enp) 39227569Sphilip{ 40227569Sphilip efx_nic_cfg_t *encp = &(enp->en_nic_cfg); 41227569Sphilip int rc; 42227569Sphilip 43227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 44227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 45227569Sphilip EFSYS_ASSERT(!(enp->en_mod_flags & EFX_MOD_WOL)); 46227569Sphilip 47227569Sphilip if (~(encp->enc_features) & EFX_FEATURE_WOL) { 48227569Sphilip rc = ENOTSUP; 49227569Sphilip goto fail1; 50227569Sphilip } 51227569Sphilip 52227569Sphilip /* Current implementation is Siena specific */ 53227569Sphilip EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA); 54227569Sphilip 55227569Sphilip enp->en_mod_flags |= EFX_MOD_WOL; 56227569Sphilip 57227569Sphilip return (0); 58227569Sphilip 59227569Sphilipfail1: 60227569Sphilip EFSYS_PROBE1(fail1, int, rc); 61227569Sphilip 62227569Sphilip return (rc); 63227569Sphilip} 64227569Sphilip 65227569Sphilip __checkReturn int 66227569Sphilipefx_wol_filter_clear( 67227569Sphilip __in efx_nic_t *enp) 68227569Sphilip{ 69227569Sphilip efx_mcdi_req_t req; 70227569Sphilip uint8_t payload[MC_CMD_WOL_FILTER_RESET_IN_LEN]; 71227569Sphilip int rc; 72227569Sphilip 73227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 74227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 75227569Sphilip 76227569Sphilip req.emr_cmd = MC_CMD_WOL_FILTER_RESET; 77227569Sphilip req.emr_in_buf = payload; 78227569Sphilip req.emr_in_length = MC_CMD_WOL_FILTER_RESET_IN_LEN; 79227569Sphilip req.emr_out_buf = NULL; 80227569Sphilip req.emr_out_length = 0; 81227569Sphilip 82227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_RESET_IN_MASK, 83227569Sphilip MC_CMD_WOL_FILTER_RESET_IN_WAKE_FILTERS | 84227569Sphilip MC_CMD_WOL_FILTER_RESET_IN_LIGHTSOUT_OFFLOADS); 85227569Sphilip 86227569Sphilip efx_mcdi_execute(enp, &req); 87227569Sphilip 88227569Sphilip if (req.emr_rc != 0) { 89227569Sphilip rc = req.emr_rc; 90227569Sphilip goto fail1; 91227569Sphilip } 92227569Sphilip 93227569Sphilip return (0); 94227569Sphilip 95227569Sphilipfail1: 96227569Sphilip EFSYS_PROBE1(fail1, int, rc); 97227569Sphilip 98227569Sphilip return (rc); 99227569Sphilip} 100227569Sphilip 101227569Sphilip __checkReturn int 102227569Sphilipefx_wol_filter_add( 103227569Sphilip __in efx_nic_t *enp, 104227569Sphilip __in efx_wol_type_t type, 105227569Sphilip __in efx_wol_param_t *paramp, 106227569Sphilip __out uint32_t *filter_idp) 107227569Sphilip{ 108227569Sphilip efx_mcdi_req_t req; 109227569Sphilip uint8_t payload[MAX(MC_CMD_WOL_FILTER_SET_IN_LEN, 110227569Sphilip MC_CMD_WOL_FILTER_SET_OUT_LEN)]; 111227569Sphilip efx_byte_t link_mask; 112227569Sphilip int rc; 113227569Sphilip 114227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 115227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 116227569Sphilip 117227569Sphilip req.emr_cmd = MC_CMD_WOL_FILTER_SET; 118227569Sphilip (void) memset(payload, '\0', sizeof (payload)); 119227569Sphilip req.emr_in_buf = payload; 120227569Sphilip req.emr_in_length = MC_CMD_WOL_FILTER_SET_IN_LEN; 121227569Sphilip req.emr_out_buf = payload; 122227569Sphilip req.emr_out_length = MC_CMD_WOL_FILTER_SET_OUT_LEN; 123227569Sphilip 124227569Sphilip switch (type) { 125227569Sphilip case EFX_WOL_TYPE_MAGIC: 126227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 127227569Sphilip MC_CMD_FILTER_MODE_SIMPLE); 128227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 129227569Sphilip MC_CMD_WOL_TYPE_MAGIC); 130227569Sphilip EFX_MAC_ADDR_COPY( 131227569Sphilip MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_MAGIC_MAC), 132227569Sphilip paramp->ewp_magic.mac_addr); 133227569Sphilip break; 134227569Sphilip 135227569Sphilip case EFX_WOL_TYPE_BITMAP: { 136227569Sphilip uint32_t swapped = 0; 137227569Sphilip efx_dword_t *dwordp; 138227569Sphilip unsigned int pos, bit; 139227569Sphilip 140227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 141227569Sphilip MC_CMD_FILTER_MODE_SIMPLE); 142227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 143227569Sphilip MC_CMD_WOL_TYPE_BITMAP); 144227569Sphilip 145227569Sphilip /* 146227569Sphilip * MC bitmask is supposed to be bit swapped 147227569Sphilip * amongst 32 bit words(!) 148227569Sphilip */ 149227569Sphilip 150227569Sphilip dwordp = MCDI_IN2(req, efx_dword_t, 151227569Sphilip WOL_FILTER_SET_IN_BITMAP_MASK); 152227569Sphilip 153227569Sphilip EFSYS_ASSERT3U(EFX_WOL_BITMAP_MASK_SIZE % 4, ==, 0); 154227569Sphilip 155227569Sphilip for (pos = 0; pos < EFX_WOL_BITMAP_MASK_SIZE; ++pos) { 156227569Sphilip uint8_t native = paramp->ewp_bitmap.mask[pos]; 157227569Sphilip 158227569Sphilip for (bit = 0; bit < 8; ++bit) { 159227569Sphilip swapped <<= 1; 160227569Sphilip swapped |= (native & 0x1); 161227569Sphilip native >>= 1; 162227569Sphilip } 163227569Sphilip 164227569Sphilip if ((pos & 3) == 3) { 165227569Sphilip EFX_POPULATE_DWORD_1(dwordp[pos >> 2], 166227569Sphilip EFX_DWORD_0, swapped); 167227569Sphilip swapped = 0; 168227569Sphilip } 169227569Sphilip } 170227569Sphilip 171227569Sphilip memcpy(MCDI_IN2(req, uint8_t, WOL_FILTER_SET_IN_BITMAP_BITMAP), 172227569Sphilip paramp->ewp_bitmap.value, 173227569Sphilip sizeof (paramp->ewp_bitmap.value)); 174227569Sphilip 175227569Sphilip EFSYS_ASSERT3U(paramp->ewp_bitmap.value_len, <=, 176227569Sphilip sizeof (paramp->ewp_bitmap.value)); 177227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_BITMAP_LEN, 178227569Sphilip paramp->ewp_bitmap.value_len); 179227569Sphilip } 180227569Sphilip break; 181227569Sphilip 182227569Sphilip case EFX_WOL_TYPE_LINK: 183227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_FILTER_MODE, 184227569Sphilip MC_CMD_FILTER_MODE_SIMPLE); 185227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_SET_IN_WOL_TYPE, 186227569Sphilip MC_CMD_WOL_TYPE_LINK); 187227569Sphilip 188227569Sphilip EFX_ZERO_BYTE(link_mask); 189227569Sphilip EFX_SET_BYTE_FIELD(link_mask, MC_CMD_WOL_FILTER_SET_IN_LINK_UP, 190227569Sphilip 1); 191227569Sphilip MCDI_IN_SET_BYTE(req, WOL_FILTER_SET_IN_LINK_MASK, 192227569Sphilip link_mask.eb_u8[0]); 193227569Sphilip break; 194227569Sphilip 195227569Sphilip default: 196227569Sphilip EFSYS_ASSERT3U(type, !=, type); 197227569Sphilip } 198227569Sphilip 199227569Sphilip efx_mcdi_execute(enp, &req); 200227569Sphilip 201227569Sphilip if (req.emr_rc != 0) { 202227569Sphilip rc = req.emr_rc; 203227569Sphilip goto fail1; 204227569Sphilip } 205227569Sphilip 206227569Sphilip if (req.emr_out_length_used < MC_CMD_WOL_FILTER_SET_OUT_LEN) { 207227569Sphilip rc = EMSGSIZE; 208227569Sphilip goto fail2; 209227569Sphilip } 210227569Sphilip 211227569Sphilip *filter_idp = MCDI_OUT_DWORD(req, WOL_FILTER_SET_OUT_FILTER_ID); 212227569Sphilip 213227569Sphilip return (0); 214227569Sphilip 215227569Sphilipfail2: 216227569Sphilip EFSYS_PROBE(fail2); 217227569Sphilipfail1: 218227569Sphilip EFSYS_PROBE1(fail1, int, rc); 219227569Sphilip 220227569Sphilip return (rc); 221227569Sphilip} 222227569Sphilip 223227569Sphilip __checkReturn int 224227569Sphilipefx_wol_filter_remove( 225227569Sphilip __in efx_nic_t *enp, 226227569Sphilip __in uint32_t filter_id) 227227569Sphilip{ 228227569Sphilip efx_mcdi_req_t req; 229227569Sphilip uint8_t payload[MC_CMD_WOL_FILTER_REMOVE_IN_LEN]; 230227569Sphilip int rc; 231227569Sphilip 232227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 233227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 234227569Sphilip 235227569Sphilip req.emr_cmd = MC_CMD_WOL_FILTER_REMOVE; 236227569Sphilip req.emr_in_buf = payload; 237227569Sphilip req.emr_in_length = MC_CMD_WOL_FILTER_REMOVE_IN_LEN; 238227569Sphilip EFX_STATIC_ASSERT(MC_CMD_WOL_FILTER_REMOVE_OUT_LEN == 0); 239227569Sphilip req.emr_out_buf = NULL; 240227569Sphilip req.emr_out_length = 0; 241227569Sphilip 242227569Sphilip MCDI_IN_SET_DWORD(req, WOL_FILTER_REMOVE_IN_FILTER_ID, filter_id); 243227569Sphilip 244227569Sphilip efx_mcdi_execute(enp, &req); 245227569Sphilip 246227569Sphilip if (req.emr_rc != 0) { 247227569Sphilip rc = req.emr_rc; 248227569Sphilip goto fail1; 249227569Sphilip } 250227569Sphilip 251227569Sphilip return (0); 252227569Sphilip 253227569Sphilipfail1: 254227569Sphilip EFSYS_PROBE1(fail1, int, rc); 255227569Sphilip 256227569Sphilip return (rc); 257227569Sphilip} 258227569Sphilip 259227569Sphilip 260227569Sphilip __checkReturn int 261227569Sphilipefx_lightsout_offload_add( 262227569Sphilip __in efx_nic_t *enp, 263227569Sphilip __in efx_lightsout_offload_type_t type, 264227569Sphilip __in efx_lightsout_offload_param_t *paramp, 265227569Sphilip __out uint32_t *filter_idp) 266227569Sphilip{ 267227569Sphilip efx_mcdi_req_t req; 268227569Sphilip uint8_t payload[MAX(MAX(MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN, 269227569Sphilip MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN), 270227569Sphilip MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN)]; 271227569Sphilip int rc; 272227569Sphilip 273227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 274227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 275227569Sphilip 276227569Sphilip req.emr_cmd = MC_CMD_ADD_LIGHTSOUT_OFFLOAD; 277227569Sphilip req.emr_in_buf = payload; 278227569Sphilip req.emr_in_length = sizeof (type); 279227569Sphilip req.emr_out_buf = payload; 280227569Sphilip req.emr_out_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN; 281227569Sphilip 282227569Sphilip switch (type) { 283227569Sphilip case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP: 284227569Sphilip req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_ARP_LEN; 285227569Sphilip MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 286227569Sphilip MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP); 287227569Sphilip EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t, 288227569Sphilip ADD_LIGHTSOUT_OFFLOAD_IN_ARP_MAC), 289227569Sphilip paramp->elop_arp.mac_addr); 290227569Sphilip MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_ARP_IP, 291227569Sphilip paramp->elop_arp.ip); 292227569Sphilip break; 293227569Sphilip case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS: 294227569Sphilip req.emr_in_length = MC_CMD_ADD_LIGHTSOUT_OFFLOAD_IN_NS_LEN; 295227569Sphilip MCDI_IN_SET_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 296227569Sphilip MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS); 297227569Sphilip EFX_MAC_ADDR_COPY(MCDI_IN2(req, uint8_t, 298227569Sphilip ADD_LIGHTSOUT_OFFLOAD_IN_NS_MAC), 299227569Sphilip paramp->elop_ns.mac_addr); 300227569Sphilip memcpy(MCDI_IN2(req, uint8_t, 301227569Sphilip ADD_LIGHTSOUT_OFFLOAD_IN_NS_SNIPV6), 302227569Sphilip paramp->elop_ns.solicited_node, 303227569Sphilip sizeof (paramp->elop_ns.solicited_node)); 304227569Sphilip memcpy(MCDI_IN2(req, uint8_t, ADD_LIGHTSOUT_OFFLOAD_IN_NS_IPV6), 305227569Sphilip paramp->elop_ns.ip, sizeof (paramp->elop_ns.ip)); 306227569Sphilip break; 307227569Sphilip default: 308227569Sphilip EFSYS_ASSERT3U(type, !=, type); 309227569Sphilip } 310227569Sphilip 311227569Sphilip efx_mcdi_execute(enp, &req); 312227569Sphilip 313227569Sphilip if (req.emr_rc != 0) { 314227569Sphilip rc = req.emr_rc; 315227569Sphilip goto fail1; 316227569Sphilip } 317227569Sphilip 318227569Sphilip if (req.emr_out_length_used < MC_CMD_ADD_LIGHTSOUT_OFFLOAD_OUT_LEN) { 319227569Sphilip rc = EMSGSIZE; 320227569Sphilip goto fail2; 321227569Sphilip } 322227569Sphilip 323227569Sphilip *filter_idp = MCDI_OUT_DWORD(req, ADD_LIGHTSOUT_OFFLOAD_OUT_FILTER_ID); 324227569Sphilip 325227569Sphilip return (0); 326227569Sphilip 327227569Sphilipfail2: 328227569Sphilip EFSYS_PROBE(fail2); 329227569Sphilipfail1: 330227569Sphilip EFSYS_PROBE1(fail1, int, rc); 331227569Sphilip 332227569Sphilip return (rc); 333227569Sphilip} 334227569Sphilip 335227569Sphilip 336227569Sphilip __checkReturn int 337227569Sphilipefx_lightsout_offload_remove( 338227569Sphilip __in efx_nic_t *enp, 339227569Sphilip __in efx_lightsout_offload_type_t type, 340227569Sphilip __in uint32_t filter_id) 341227569Sphilip{ 342227569Sphilip efx_mcdi_req_t req; 343227569Sphilip uint8_t payload[MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_IN_LEN]; 344227569Sphilip int rc; 345227569Sphilip 346227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 347227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 348227569Sphilip 349227569Sphilip req.emr_cmd = MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD; 350227569Sphilip req.emr_in_buf = payload; 351227569Sphilip req.emr_in_length = sizeof (payload); 352227569Sphilip EFX_STATIC_ASSERT(MC_CMD_REMOVE_LIGHTSOUT_OFFLOAD_OUT_LEN == 0); 353227569Sphilip req.emr_out_buf = NULL; 354227569Sphilip req.emr_out_length = 0; 355227569Sphilip 356227569Sphilip switch (type) { 357227569Sphilip case EFX_LIGHTSOUT_OFFLOAD_TYPE_ARP: 358227569Sphilip MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 359227569Sphilip MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_ARP); 360227569Sphilip break; 361227569Sphilip case EFX_LIGHTSOUT_OFFLOAD_TYPE_NS: 362227569Sphilip MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_PROTOCOL, 363227569Sphilip MC_CMD_LIGHTSOUT_OFFLOAD_PROTOCOL_NS); 364227569Sphilip break; 365227569Sphilip default: 366227569Sphilip EFSYS_ASSERT3U(type, !=, type); 367227569Sphilip } 368227569Sphilip 369227569Sphilip MCDI_IN_SET_DWORD(req, REMOVE_LIGHTSOUT_OFFLOAD_IN_FILTER_ID, 370227569Sphilip filter_id); 371227569Sphilip 372227569Sphilip efx_mcdi_execute(enp, &req); 373227569Sphilip 374227569Sphilip if (req.emr_rc != 0) { 375227569Sphilip rc = req.emr_rc; 376227569Sphilip goto fail1; 377227569Sphilip } 378227569Sphilip 379227569Sphilip return (0); 380227569Sphilip 381227569Sphilipfail1: 382227569Sphilip EFSYS_PROBE1(fail1, int, rc); 383227569Sphilip 384227569Sphilip return (rc); 385227569Sphilip} 386227569Sphilip 387227569Sphilip 388227569Sphilip void 389227569Sphilipefx_wol_fini( 390227569Sphilip __in efx_nic_t *enp) 391227569Sphilip{ 392227569Sphilip EFSYS_ASSERT3U(enp->en_magic, ==, EFX_NIC_MAGIC); 393227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_PROBE); 394227569Sphilip EFSYS_ASSERT3U(enp->en_mod_flags, &, EFX_MOD_WOL); 395227569Sphilip 396227569Sphilip enp->en_mod_flags &= ~EFX_MOD_WOL; 397227569Sphilip} 398227569Sphilip 399227569Sphilip#endif /* EFSYS_OPT_WOL */ 400