1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2015 OmniTI Computer Consulting, Inc. All rights reserved.
  14  */
  15 
  16 #include "i40e_sw.h"
  17 
  18 /*
  19  * Add a MAC address on an already-general-locked i40e instance.
  20  */
  21 static int
  22 i40e_add_mac_locked(i40e_t *i40e, const uint8_t *mac_addr)
  23 {
  24         i40e_hw_t *hw = &i40e->i40e_hw_space;
  25         int i;
  26 
  27         ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
  28 
  29         if (i40e->i40e_state & I40E_SUSPENDED)
  30                 return (ECANCELED);
  31 
  32 
  33         if (i40e->i40e_mac_used == i40e->i40e_mac_total)
  34                 return (ENOSPC);
  35 
  36         /*
  37          * The array of i40e_mac_addrs is ordered from
  38          * 0..(i40e_mac_used - 1) to make insertion/deletion rather
  39          * simple.  WE don't care what order they're in, which costs O(N)
  40          * search for both insertion (collision checking) and deletion
  41          * (location), but this doesn't happen much.  If we eliminate
  42          * collision checking (like ixgbe seems to), we go to O(1) for
  43          * insertion.
  44          */
  45 
  46         for (i = 0; i < i40e->i40e_mac_used; i++) {
  47                 if (bcmp(mac_addr, i40e->i40e_mac_addrs[i].i40eth_mac,
  48                     ETHERADDRL) == 0) {
  49                         /* Ooops, adding an ethernet we already know about. */
  50                         return (EEXIST);
  51                 }
  52         }
  53         ASSERT(i == i40e->i40e_mac_used);
  54         ASSERT(!(i40e->i40e_mac_addrs[i].i40eth_used));
  55 
  56         bcopy(mac_addr, i40e->i40e_mac_addrs[i].i40eth_mac, ETHERADDRL);
  57         i40e->i40e_mac_used++;
  58 
  59         return (i40e_hwadd_mac(i40e, hw, mac_addr) ? 0 : EIO);
  60 }
  61 
  62 /*
  63  * Remove a MAC address on an already-general-locked i40e instance.
  64  */
  65 static int
  66 i40e_remove_mac_locked(i40e_t *i40e, const uint8_t *mac_addr)
  67 {
  68         i40e_hw_t *hw = &i40e->i40e_hw_space;
  69         struct i40e_aqc_remove_macvlan_element_data mvlist;
  70         int i;
  71         i40e_ether_addr_t *nukeme;
  72 
  73         ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
  74 
  75         if (i40e->i40e_state & I40E_SUSPENDED)
  76                 return (ECANCELED);
  77 
  78         for (i = 0; i < i40e->i40e_mac_used; i++) {
  79                 if (bcmp(mac_addr, i40e->i40e_mac_addrs[i].i40eth_mac,
  80                     ETHERADDRL) == 0) {
  81                         break;
  82                 }
  83         }
  84 
  85         if (i == i40e->i40e_mac_used)
  86                 return (ENOENT);        /* ixgbe uses EINVAL... why? */
  87 
  88         nukeme = &(i40e->i40e_mac_addrs[i]);
  89 
  90         /*
  91          * Start by making sure the common-code/HW is okay with it.
  92          */
  93         bzero(&mvlist, sizeof (mvlist));
  94         bcopy(mac_addr, mvlist.mac_addr, ETHERADDRL);
  95         mvlist.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
  96             I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
  97         if (i40e_aq_remove_macvlan(hw, i40e->i40e_vsi_id, &mvlist, 1, NULL) !=
  98             I40E_SUCCESS) {
  99                 return (EIO);   /* XXX KEBE ASKS - better idea? */
 100         }
 101 
 102         /*
 103          * Okay!  Now that the board thinks it's good, we need to update
 104          * ourselves.
 105          */
 106         i40e->i40e_mac_used--;
 107         *nukeme = i40e->i40e_mac_addrs[i40e->i40e_mac_used];
 108         i40e->i40e_mac_addrs[i40e->i40e_mac_used].i40eth_used = 0;
 109 
 110         return (0);
 111 }
 112 
 113 /*
 114  * GLDv3 receive group method: Add a MAC address.
 115  */
 116 static int
 117 i40e_addmac(void *arg, const uint8_t *mac_addr)
 118 {
 119         i40e_t *i40e = arg;
 120         int rc;
 121 
 122         mutex_enter(&i40e->i40e_general_lock);
 123         rc = i40e_add_mac_locked(i40e, mac_addr);
 124         mutex_exit(&i40e->i40e_general_lock);
 125         return (rc);
 126 }
 127 
 128 /*
 129  * GLDv3 receive group method: Remove a MAC address.
 130  */
 131 static int
 132 i40e_remmac(void *arg, const uint8_t *mac_addr)
 133 {
 134         i40e_t *i40e = arg;
 135         int rc;
 136 
 137         mutex_enter(&i40e->i40e_general_lock);
 138         rc = i40e_remove_mac_locked(i40e, mac_addr);
 139         mutex_exit(&i40e->i40e_general_lock);
 140         return (rc);
 141 }
 142 
 143 /*
 144  * GLDv3 method: Bring the device out of the reset/quiesced state that it was
 145  * in when the interface was registered.
 146  */
 147 static int
 148 i40e_m_start(void *arg)
 149 {
 150         i40e_t *i40e = arg;
 151         int rc = 0;
 152 
 153         mutex_enter(&i40e->i40e_general_lock);
 154         if (i40e->i40e_state & I40E_SUSPENDED) {
 155                 rc = ECANCELED;
 156                 goto done;
 157         }
 158 
 159         if (!i40e_start(i40e, B_TRUE)) {
 160                 rc = EIO;
 161                 goto done;
 162         }
 163 
 164         atomic_or_32(&i40e->i40e_state, I40E_STARTED);
 165 
 166 done:
 167         mutex_exit(&i40e->i40e_general_lock);
 168         if (rc == 0)
 169                 i40e_enable_watchdog_timer(i40e);
 170         return (rc);
 171 }
 172 
 173 /*
 174  * GLDv3 method: Stop the device and put it in a reset/quiesced state such
 175  * that the interface can be unregistered.
 176  */
 177 static void
 178 i40e_m_stop(void *arg)
 179 {
 180         i40e_t *i40e = arg;
 181 
 182         mutex_enter(&i40e->i40e_general_lock);
 183 
 184         if (i40e->i40e_state & I40E_SUSPENDED)
 185                 goto done;
 186 
 187         atomic_and_32(&i40e->i40e_state, ~I40E_STARTED);
 188 
 189         i40e_stop(i40e, B_TRUE);
 190 
 191 done:
 192         mutex_exit(&i40e->i40e_general_lock);
 193 
 194         i40e_disable_watchdog_timer(i40e);
 195 }
 196 
 197 /*
 198  * GLDv3 method: Enable or disable promiscuous mode.
 199  */
 200 static int
 201 i40e_m_promisc(void *arg, boolean_t on)
 202 {
 203         i40e_t *i40e = arg;
 204         struct i40e_hw *hw = &i40e->i40e_hw_space;
 205         int rc = 0;
 206 
 207         mutex_enter(&i40e->i40e_general_lock);
 208         if (i40e->i40e_state & I40E_SUSPENDED) {
 209                 rc = ECANCELED;
 210                 goto done;
 211         }
 212 
 213         /*
 214          * Turn on or off promiscuous.  The i40e common code has two switches:
 215          * unicast & multicast.  Turn them BOTH on or off.
 216          */
 217         rc = i40e_aq_set_vsi_unicast_promiscuous(hw, i40e->i40e_vsi_id,
 218             on, NULL);
 219         switch (rc) {
 220         case I40E_SUCCESS:
 221                 rc = 0; /* May be redundant... */
 222                 break;
 223                 /* XXX KEBE SAYS MORE VALUES to map here... */
 224         default:
 225                 rc = EINVAL;
 226                 goto done;
 227         }
 228         rc = i40e_aq_set_vsi_multicast_promiscuous(hw, i40e->i40e_vsi_id,
 229             on, NULL);
 230         /* NOTE:  If this fails, the unicast one has still taken, however. */
 231         switch (rc) {
 232         case I40E_SUCCESS:
 233                 rc = 0; /* May be redundant... */
 234                 break;
 235                 /* XXX KEBE SAYS MORE VALUES to map here... */
 236         default:
 237                 rc = EINVAL;
 238                 break;
 239         }
 240 
 241 done:
 242         mutex_exit(&i40e->i40e_general_lock);
 243         return (rc);
 244 }
 245 
 246 /*
 247  * Support functions for GLDv3 add/delete multicast address method.
 248  *
 249  * NOTE:  It appears that the i40e HW doesn't differentiate between unicast and
 250  * multicast in its filters.  For now, act as a front-end to i40e_{add,rem}mac.
 251  * IF we overrun with multicast, these functions should instead:
 252  * 1.) Keep track of multicasts separately and
 253  * 2.) Send a all-multicast indicator to i40e_{add,rem}mac() to produce a
 254  * single HW filter that accepts ALL multicast traffic (but not promisc. per se
 255  * if that's the case) and opens up more filter space.
 256  *
 257  * For now, however, these are VERY TINY functions.  If we need to get smarter
 258  * per above, we have the scaffolding in place now.
 259  */
 260 static int
 261 i40e_multicast_add(i40e_t *i40e, const uint8_t *multicast_address)
 262 {
 263         return (i40e_add_mac_locked(i40e, multicast_address));
 264 }
 265 
 266 static int
 267 i40e_multicast_remove(i40e_t *i40e, const uint8_t *multicast_address)
 268 {
 269         return (i40e_remove_mac_locked(i40e, multicast_address));
 270 }
 271 
 272 /*
 273  * GLDv3 method: Add or delete a multicast address.
 274  */
 275 static int
 276 i40e_m_multicast(void *arg, boolean_t add, const uint8_t *multicast_address)
 277 {
 278         i40e_t *i40e = arg;
 279         int rc;
 280 
 281         mutex_enter(&i40e->i40e_general_lock);
 282 
 283         /*
 284          * NOTE: This is redundant unless the above tiny support functions
 285          * grow more sophisticated.
 286          */
 287         if (i40e->i40e_state & I40E_SUSPENDED) {
 288                 rc = ECANCELED;
 289                 goto done;
 290         }
 291 
 292         rc = (add) ? i40e_multicast_add(i40e, multicast_address) :
 293             i40e_multicast_remove(i40e, multicast_address);
 294 
 295 done:
 296         mutex_exit(&i40e->i40e_general_lock);
 297         return (rc);
 298 }
 299 
 300 /*
 301  * GLDv3 method: Pass on M_IOCTL messages passed to the DLD, and support
 302  * private IOCTLs for debugging and ndd.  Yes this looks a little STREAMS-ish
 303  * with its queue_t and mblk_t arguments.
 304  */
 305 static void
 306 i40e_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
 307 {
 308         i40e_t *i40e = arg;
 309         struct iocblk *iocp;
 310         boolean_t send_nak;
 311 
 312         iocp = (struct iocblk *)(uintptr_t)mp->b_rptr;
 313         iocp->ioc_error = 0;
 314 
 315         mutex_enter(&i40e->i40e_general_lock);
 316         if (i40e->i40e_state & I40E_SUSPENDED) {
 317                 mutex_exit(&i40e->i40e_general_lock);
 318                 /*
 319                  * ixgbe returns EINVAL here, but other SUSPENDED cases
 320                  * in other methods return ECANCELED.  Why different here?
 321                  * Because of STREAMS?
 322                  */
 323                 miocnak(q, mp, 0, EINVAL);
 324                 return;
 325         }
 326         mutex_exit(&i40e->i40e_general_lock);
 327 
 328         switch (iocp->ioc_cmd) {
 329         case LB_GET_INFO_SIZE:
 330         case LB_GET_INFO:
 331         case LB_GET_MODE:
 332         case LB_SET_MODE:
 333                 /* XXX KEBE SAYS FILL ME IN with loopback ioctl handling. */
 334                 iocp->ioc_error = ENOTSUP;
 335                 send_nak = B_TRUE;  /* send_nak = i40e_loopback_ioctl(...) */
 336                 break;
 337 
 338         default:
 339                 send_nak = B_TRUE;
 340                 break;
 341         }
 342 
 343         /* Using STREAMS status, figure out what next. */
 344         if (send_nak) {
 345                 /* NAK with a specified error or EINVAL. */
 346                 miocnak(q, mp, 0,
 347                     iocp->ioc_error == 0 ? EINVAL : iocp->ioc_error);
 348         } else {
 349                 /* Callee prepared reply as ACK or NAK). */
 350                 mp->b_datap->db_type =
 351                     iocp->ioc_error == 0 ? M_IOCACK : M_IOCNAK;
 352                 qreply(q, mp);
 353         }
 354 }
 355 
 356 /*
 357  * Support functions for GLDv3 capabilities (ring fill-in functions).
 358  */
 359 
 360 /* Enable a ring interrupt. */
 361 /* ARGSUSED */
 362 int
 363 i40e_rx_ring_intr_enable(mac_intr_handle_t intrh)
 364 {
 365         return (EINVAL);
 366 }
 367 
 368 /* Disable a ring interrupt. */
 369 /* ARGSUSED */
 370 int
 371 i40e_rx_ring_intr_disable(mac_intr_handle_t intrh)
 372 {
 373         return (EINVAL);
 374 }
 375 
 376 /* Fill in transmit ring. */
 377 static void
 378 i40e_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
 379     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
 380 {
 381         i40e_t *i40e = arg;
 382         mac_intr_t *mintr = &infop->mri_intr;
 383         i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
 384 
 385         ASSERT(group_index == -1);
 386         ASSERT(ring_index < i40e->i40e_num_trqpairs);
 387 
 388         /*
 389          * Basically, we want to map the ring_index & ring handle handed to us
 390          * from GLDv3 and bidirectionally map it to an i40e_tx_ring_t.  We put
 391          * "rh" into the txr_ring_handle (one direction) and put the
 392          * i40e_tx_ring_t into the infop->mri_driver (other direction).
 393          */
 394 
 395         /* Map the appropriate itrq. */
 396         itrq->itrq_mactxring = rh;
 397 
 398         infop->mri_driver = (mac_ring_driver_t)itrq;
 399         infop->mri_start = NULL;
 400         infop->mri_stop = NULL;
 401         infop->mri_tx = i40e_ring_tx;
 402         infop->mri_stat = i40e_tx_ring_stat;
 403 
 404         /*
 405          * Fill in the one lone mintr field, only if MSI or MSIX, the
 406          * mi_ddi_handle.
 407          */
 408         if (i40e->i40e_intr_type & (DDI_INTR_TYPE_MSIX | DDI_INTR_TYPE_MSI)) {
 409                 mintr->mi_ddi_handle =
 410                     i40e->i40e_intr_handles[itrq->itrq_tx_intrvec];
 411         }
 412 }
 413 
 414 /*
 415  * Receive ring start function for GLDv3
 416  */
 417 static int
 418 i40e_ring_start(mac_ring_driver_t rh, uint64_t gen_num)
 419 {
 420         i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
 421 
 422         /*
 423          * GLDv3 requires we keep track of a generation number, as it uses
 424          * that number to keep track of whether or not a ring is active.
 425          */
 426         mutex_enter(&itrq->itrq_rx_lock);
 427         itrq->itrq_rxgen = gen_num;
 428         mutex_exit(&itrq->itrq_rx_lock);
 429         return (0);
 430 }
 431 
 432 /* Fill in receive ring. */
 433 static void
 434 i40e_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
 435     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
 436 {
 437         i40e_t *i40e = arg;
 438         mac_intr_t *mintr = &infop->mri_intr;
 439         i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
 440 
 441         /*
 442          * Basically, we want to map the ring_index & ring handle handed to us
 443          * from GLDv3 and bidirectionally map it to an i40e qpair.  We put
 444          * "rh" into the macrxring (one direction) and put the i40e_trqpair_t
 445          * into the infop->mri_driver (other direction).
 446          */
 447         ASSERT3S(group_index, ==, 0);
 448         ASSERT3S(ring_index, <, i40e->i40e_num_trqpairs);
 449 
 450         /* Map the appropriate rxr, use ring_index as an index for now... */
 451         itrq->itrq_macrxring = rh;
 452 
 453         infop->mri_driver = (mac_ring_driver_t)itrq;
 454         /* XXX KEBE SAYS FILL IN THESE WITH REAL FUNCTIONS... */
 455         infop->mri_start = i40e_ring_start;
 456         infop->mri_stop = NULL;
 457         infop->mri_poll = i40e_ring_rx_poll;
 458         infop->mri_stat = i40e_rx_ring_stat;
 459 
 460         mintr->mi_handle = (mac_intr_handle_t)itrq;
 461         mintr->mi_enable = i40e_rx_ring_intr_enable;
 462         mintr->mi_disable = i40e_rx_ring_intr_disable;
 463         /*
 464          * Fill in the one lone mintr field, only if MSI or MSIX, the
 465          * mi_ddi_handle.
 466          */
 467         if (i40e->i40e_intr_type & (DDI_INTR_TYPE_MSIX | DDI_INTR_TYPE_MSI)) {
 468                 mintr->mi_ddi_handle =
 469                     i40e->i40e_intr_handles[itrq->itrq_rx_intrvec];
 470         }
 471 }
 472 
 473 /* Fill in receive group. */
 474 void
 475 i40e_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
 476     mac_group_info_t *infop, mac_group_handle_t gh)
 477 {
 478         i40e_t *i40e = arg;
 479 
 480         /* It *IS* called i40e_fill_*RX*_group().... */
 481         if (rtype != MAC_RING_TYPE_RX)
 482                 return;
 483 
 484         i40e->i40e_rx_group_handle = gh;
 485 
 486         /*
 487          * NOTE: If we map rx groups to VSIs OR trqpairs, the following may
 488          * need to change.
 489          */
 490         infop->mgi_driver = (mac_group_driver_t)i40e;
 491         infop->mgi_start = NULL;
 492         infop->mgi_stop = NULL;
 493         infop->mgi_addmac = i40e_addmac;
 494         infop->mgi_remmac = i40e_remmac;
 495 
 496         ASSERT(i40e->i40e_num_rx_groups == 1);       /* XXX KEBE SAYS FOR NOW... */
 497         infop->mgi_count = i40e->i40e_num_trqpairs;
 498 }
 499 
 500 /*
 501  * GLDv3 method: Express as GLDv3 capabilities what this driver can do.
 502  */
 503 static boolean_t
 504 i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
 505 {
 506         i40e_t *i40e = arg;
 507         mac_capab_rings_t *cap_rings;
 508 
 509         switch (cap) {
 510         case MAC_CAPAB_HCKSUM:
 511                 /* Hardware checksumming info. */
 512                 if (!i40e->i40e_tx_hcksum_enable)
 513                         return (B_FALSE);
 514                 /* cap_data points to a uint32_t, needing flags filled in. */
 515                 *((uint32_t *)cap_data) =
 516                     HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
 517                 break;
 518         case MAC_CAPAB_LSO:
 519                 /* Large-segment offload (LSO) info. */
 520                 if (i40e->i40e_lso_enable) {
 521                         mac_capab_lso_t *cap_lso = cap_data;
 522 
 523                         /*
 524                          * Table 1-5 says LSO works for both IPv4 and IPv6,
 525                          * BUT IPv6 LSO isn't supported in GLDv3/MAC yet.
 526                          */
 527                         cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4;
 528                         cap_lso->lso_basic_tcp_ipv4.lso_max = I40E_LSO_MAXLEN;
 529                 } else {
 530                         return (B_FALSE);
 531                 }
 532                 break;
 533         case MAC_CAPAB_RINGS:
 534                 /* XXX KEBE SAYS FILL ME IN with ring info, incl. functions. */
 535                 cap_rings = cap_data;
 536                 /* Anything common across both TX and RX?  Fill that in here. */
 537                 cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
 538                 switch (cap_rings->mr_type) {
 539                 case MAC_RING_TYPE_TX:
 540                         /* Fill in transmit ring info. */
 541                         cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
 542                         cap_rings->mr_gnum = 0;
 543                         cap_rings->mr_rget = i40e_fill_tx_ring;
 544                         cap_rings->mr_gget = NULL;
 545                         /* XXX KEBE ASKS -> make these functions?!? */
 546                         cap_rings->mr_gaddring = NULL;
 547                         cap_rings->mr_gremring = NULL;
 548                         break;
 549                 case MAC_RING_TYPE_RX:
 550                         /* Fill in receive ring info. */
 551                         cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
 552                         cap_rings->mr_rget = i40e_fill_rx_ring;
 553                         /* XXX KEBE SAYS FIX FOR rx groups... */
 554                         cap_rings->mr_gnum = 1;
 555                         cap_rings->mr_gget = i40e_fill_rx_group;
 556                         /* XXX KEBE ASKS -> make these functions?!? */
 557                         cap_rings->mr_gaddring = NULL;
 558                         cap_rings->mr_gremring = NULL;
 559                         break;
 560                 default:
 561                         /* XXX KEBE ASKS - ixgbe does nothing here, why? */
 562                         return (B_FALSE);
 563                 }
 564                 break;
 565         default:
 566                 return (B_FALSE);
 567         }
 568 
 569         return (B_TRUE);
 570 }
 571 
 572 /*
 573  * GLDv3 method: Set the NIC's properties.
 574  */
 575 static int
 576 i40e_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
 577     uint_t pr_valsize, const void *pr_val)
 578 {
 579         i40e_t *i40e = arg;
 580         int err = 0;
 581 
 582         mutex_enter(&i40e->i40e_general_lock);
 583         if (i40e->i40e_state & I40E_SUSPENDED) {
 584                 err = ECANCELED;
 585                 goto done;
 586         }
 587 
 588         /* XXX KEBE SAYS FILL ME IN */
 589         err = ENOTSUP;
 590 
 591 done:
 592         mutex_exit(&i40e->i40e_general_lock);
 593         return (err);
 594 }
 595 
 596 /*
 597  * GLDv3 method: Get the NIC's properties.
 598  */
 599 static int
 600 i40e_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
 601     uint_t pr_valsize, void *pr_val)
 602 {
 603         /* i40e_t *i40e = arg; */
 604         int err = 0;
 605 
 606         switch (pr_num) {
 607         case MAC_PROP_PRIVATE:
 608                 /* XXX KEBE SAYS FILL ME IN  */
 609                 break;
 610         default:
 611                 err = EINVAL;
 612                 break;
 613         }
 614 
 615         return (err);
 616 }
 617 
 618 /*
 619  * GLDv3 method: Get the read/write data about a given property.
 620  */
 621 static void
 622 i40e_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
 623     mac_prop_info_handle_t prh)
 624 {
 625         /* XXX KEBE SAYS FILL ME IN */
 626 }
 627 
 628 #define I40E_M_CALLBACK_FLAGS \
 629         (MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
 630 
 631 static mac_callbacks_t i40e_m_callbacks = {
 632         I40E_M_CALLBACK_FLAGS,
 633         i40e_m_stat,
 634         i40e_m_start,
 635         i40e_m_stop,
 636         i40e_m_promisc,
 637         i40e_m_multicast,
 638         NULL,
 639         NULL,
 640         NULL,
 641         i40e_m_ioctl,
 642         i40e_m_getcapab,
 643         NULL,
 644         NULL,
 645         i40e_m_setprop,
 646         i40e_m_getprop,
 647         i40e_m_propinfo
 648 };
 649 
 650 boolean_t
 651 i40e_register_mac(i40e_t *i40e)
 652 {
 653         struct i40e_hw *hw = &i40e->i40e_hw_space;
 654         int status;
 655         mac_register_t *mac = mac_alloc(MAC_VERSION);
 656 
 657         if (mac == NULL)
 658                 return (B_FALSE);
 659 
 660         mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
 661         mac->m_driver = i40e;
 662         mac->m_dip = i40e->i40e_dip;
 663         mac->m_src_addr = hw->mac.addr;
 664         mac->m_callbacks = &i40e_m_callbacks;
 665         mac->m_min_sdu = 0;
 666         mac->m_max_sdu = i40e->i40e_default_mtu;
 667         mac->m_margin = VLAN_TAGSZ;
 668         mac->m_priv_props = NULL;    /* XXX KEBE SAYS FIX ME */
 669         mac->m_v12n = MAC_VIRT_LEVEL1;
 670 
 671         status = mac_register(mac, &i40e->i40e_mac_hdl);
 672         if (status != 0)
 673                 i40e_error(i40e, "mac_register() returned %d", status);
 674         mac_free(mac);
 675 
 676         return (status == 0);
 677 }