xref: /illumos-gate/usr/src/uts/common/io/i40e/i40e_gld.c (revision 88628b1b)
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  * Copyright (c) 2018, Joyent, Inc.
15  * Copyright 2017 Tegile Systems, Inc.  All rights reserved.
16  * Copyright 2020 Ryan Zezeski
17  */
18 
19 /*
20  * For more information, please see the big theory statement in i40e_main.c.
21  */
22 
23 #include "i40e_sw.h"
24 
25 #define	I40E_PROP_RX_DMA_THRESH	"_rx_dma_threshold"
26 #define	I40E_PROP_TX_DMA_THRESH	"_tx_dma_threshold"
27 #define	I40E_PROP_RX_ITR	"_rx_intr_throttle"
28 #define	I40E_PROP_TX_ITR	"_tx_intr_throttle"
29 #define	I40E_PROP_OTHER_ITR	"_other_intr_throttle"
30 
31 char *i40e_priv_props[] = {
32 	I40E_PROP_RX_DMA_THRESH,
33 	I40E_PROP_TX_DMA_THRESH,
34 	I40E_PROP_RX_ITR,
35 	I40E_PROP_TX_ITR,
36 	I40E_PROP_OTHER_ITR,
37 	NULL
38 };
39 
40 static int
41 i40e_group_remove_mac(void *arg, const uint8_t *mac_addr)
42 {
43 	i40e_rx_group_t *rxg = arg;
44 	i40e_t *i40e = rxg->irg_i40e;
45 	struct i40e_aqc_remove_macvlan_element_data filt;
46 	struct i40e_hw *hw = &i40e->i40e_hw_space;
47 	int ret, i, last;
48 	i40e_uaddr_t *iua;
49 
50 	if (I40E_IS_MULTICAST(mac_addr))
51 		return (EINVAL);
52 
53 	mutex_enter(&i40e->i40e_general_lock);
54 
55 	if (i40e->i40e_state & I40E_SUSPENDED) {
56 		ret = ECANCELED;
57 		goto done;
58 	}
59 
60 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
61 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
62 		    ETHERADDRL) == 0)
63 			break;
64 	}
65 
66 	if (i == i40e->i40e_resources.ifr_nmacfilt_used) {
67 		ret = ENOENT;
68 		goto done;
69 	}
70 
71 	iua = &i40e->i40e_uaddrs[i];
72 	ASSERT(i40e->i40e_resources.ifr_nmacfilt_used > 0);
73 
74 	bzero(&filt, sizeof (filt));
75 	bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
76 	filt.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
77 	    I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
78 
79 	if (i40e_aq_remove_macvlan(hw, iua->iua_vsi, &filt, 1, NULL) !=
80 	    I40E_SUCCESS) {
81 		i40e_error(i40e, "failed to remove mac address "
82 		    "%2x:%2x:%2x:%2x:%2x:%2x from unicast filter: %d",
83 		    mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
84 		    mac_addr[4], mac_addr[5], filt.error_code);
85 		ret = EIO;
86 		goto done;
87 	}
88 
89 	last = i40e->i40e_resources.ifr_nmacfilt_used - 1;
90 	if (i != last) {
91 		i40e_uaddr_t *src = &i40e->i40e_uaddrs[last];
92 		bcopy(src, iua, sizeof (i40e_uaddr_t));
93 	}
94 
95 	/*
96 	 * Set the multicast bit in the last one to indicate to ourselves that
97 	 * it's invalid.
98 	 */
99 	bzero(&i40e->i40e_uaddrs[last], sizeof (i40e_uaddr_t));
100 	i40e->i40e_uaddrs[last].iua_mac[0] = 0x01;
101 	i40e->i40e_resources.ifr_nmacfilt_used--;
102 	ret = 0;
103 done:
104 	mutex_exit(&i40e->i40e_general_lock);
105 
106 	return (ret);
107 }
108 
109 static int
110 i40e_group_add_mac(void *arg, const uint8_t *mac_addr)
111 {
112 	i40e_rx_group_t	*rxg = arg;
113 	i40e_t		*i40e = rxg->irg_i40e;
114 	struct i40e_hw	*hw = &i40e->i40e_hw_space;
115 	int		i, ret;
116 	i40e_uaddr_t	*iua;
117 	struct i40e_aqc_add_macvlan_element_data filt;
118 
119 	if (I40E_IS_MULTICAST(mac_addr))
120 		return (EINVAL);
121 
122 	mutex_enter(&i40e->i40e_general_lock);
123 	if (i40e->i40e_state & I40E_SUSPENDED) {
124 		ret = ECANCELED;
125 		goto done;
126 	}
127 
128 	if (i40e->i40e_resources.ifr_nmacfilt ==
129 	    i40e->i40e_resources.ifr_nmacfilt_used) {
130 		ret = ENOSPC;
131 		goto done;
132 	}
133 
134 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
135 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
136 		    ETHERADDRL) == 0) {
137 			ret = EEXIST;
138 			goto done;
139 		}
140 	}
141 
142 	bzero(&filt, sizeof (filt));
143 	bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
144 	filt.flags = I40E_AQC_MACVLAN_ADD_PERFECT_MATCH	|
145 	    I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
146 
147 	if ((ret = i40e_aq_add_macvlan(hw, rxg->irg_vsi_seid, &filt, 1,
148 	    NULL)) != I40E_SUCCESS) {
149 		i40e_error(i40e, "failed to add mac address "
150 		    "%2x:%2x:%2x:%2x:%2x:%2x to unicast filter: %d",
151 		    mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
152 		    mac_addr[4], mac_addr[5], ret);
153 		ret = EIO;
154 		goto done;
155 	}
156 
157 	iua = &i40e->i40e_uaddrs[i40e->i40e_resources.ifr_nmacfilt_used];
158 	bcopy(mac_addr, iua->iua_mac, ETHERADDRL);
159 	iua->iua_vsi = rxg->irg_vsi_seid;
160 	i40e->i40e_resources.ifr_nmacfilt_used++;
161 	ASSERT(i40e->i40e_resources.ifr_nmacfilt_used <=
162 	    i40e->i40e_resources.ifr_nmacfilt);
163 	ret = 0;
164 done:
165 	mutex_exit(&i40e->i40e_general_lock);
166 	return (ret);
167 }
168 
169 static int
170 i40e_m_start(void *arg)
171 {
172 	i40e_t *i40e = arg;
173 	int rc = 0;
174 
175 	mutex_enter(&i40e->i40e_general_lock);
176 	if (i40e->i40e_state & I40E_SUSPENDED) {
177 		rc = ECANCELED;
178 		goto done;
179 	}
180 
181 	if (!i40e_start(i40e, B_TRUE)) {
182 		rc = EIO;
183 		goto done;
184 	}
185 
186 	atomic_or_32(&i40e->i40e_state, I40E_STARTED);
187 done:
188 	mutex_exit(&i40e->i40e_general_lock);
189 
190 	return (rc);
191 }
192 
193 static void
194 i40e_m_stop(void *arg)
195 {
196 	i40e_t *i40e = arg;
197 
198 	mutex_enter(&i40e->i40e_general_lock);
199 
200 	if (i40e->i40e_state & I40E_SUSPENDED)
201 		goto done;
202 
203 	atomic_and_32(&i40e->i40e_state, ~I40E_STARTED);
204 	i40e_stop(i40e, B_TRUE);
205 done:
206 	mutex_exit(&i40e->i40e_general_lock);
207 }
208 
209 /*
210  * Enable and disable promiscuous mode as requested. We have to toggle both
211  * unicast and multicast. Note that multicast may already be enabled due to the
212  * i40e_m_multicast may toggle it itself. See i40e_main.c for more information
213  * on this.
214  */
215 static int
216 i40e_m_promisc(void *arg, boolean_t on)
217 {
218 	i40e_t *i40e = arg;
219 	struct i40e_hw *hw = &i40e->i40e_hw_space;
220 	int ret = 0, err = 0;
221 
222 	mutex_enter(&i40e->i40e_general_lock);
223 	if (i40e->i40e_state & I40E_SUSPENDED) {
224 		ret = ECANCELED;
225 		goto done;
226 	}
227 
228 
229 	ret = i40e_aq_set_vsi_unicast_promiscuous(hw, I40E_DEF_VSI_SEID(i40e),
230 	    on, NULL, B_FALSE);
231 	if (ret != I40E_SUCCESS) {
232 		i40e_error(i40e, "failed to %s unicast promiscuity on "
233 		    "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
234 		    ret);
235 		err = EIO;
236 		goto done;
237 	}
238 
239 	/*
240 	 * If we have a non-zero mcast_promisc_count, then it has already been
241 	 * enabled or we need to leave it that way and not touch it.
242 	 */
243 	if (i40e->i40e_mcast_promisc_count > 0) {
244 		i40e->i40e_promisc_on = on;
245 		goto done;
246 	}
247 
248 	ret = i40e_aq_set_vsi_multicast_promiscuous(hw, I40E_DEF_VSI_SEID(i40e),
249 	    on, NULL);
250 	if (ret != I40E_SUCCESS) {
251 		i40e_error(i40e, "failed to %s multicast promiscuity on "
252 		    "the default VSI: %d", on == B_TRUE ? "enable" : "disable",
253 		    ret);
254 
255 		/*
256 		 * Try our best to put us back into a state that MAC expects us
257 		 * to be in.
258 		 */
259 		ret = i40e_aq_set_vsi_unicast_promiscuous(hw,
260 		    I40E_DEF_VSI_SEID(i40e), !on, NULL, B_FALSE);
261 		if (ret != I40E_SUCCESS) {
262 			i40e_error(i40e, "failed to %s unicast promiscuity on "
263 			    "the default VSI after toggling multicast failed: "
264 			    "%d", on == B_TRUE ? "disable" : "enable", ret);
265 		}
266 
267 		err = EIO;
268 		goto done;
269 	} else {
270 		i40e->i40e_promisc_on = on;
271 	}
272 
273 done:
274 	mutex_exit(&i40e->i40e_general_lock);
275 	return (err);
276 }
277 
278 /*
279  * See the big theory statement in i40e_main.c for multicast address management.
280  */
281 static int
282 i40e_multicast_add(i40e_t *i40e, const uint8_t *multicast_address)
283 {
284 	struct i40e_hw *hw = &i40e->i40e_hw_space;
285 	struct i40e_aqc_add_macvlan_element_data filt;
286 	i40e_maddr_t *mc;
287 	int ret;
288 
289 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
290 
291 	if (i40e->i40e_resources.ifr_nmcastfilt_used ==
292 	    i40e->i40e_resources.ifr_nmcastfilt) {
293 		if (i40e->i40e_mcast_promisc_count == 0 &&
294 		    i40e->i40e_promisc_on == B_FALSE) {
295 			ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
296 			    I40E_DEF_VSI_SEID(i40e), B_TRUE, NULL);
297 			if (ret != I40E_SUCCESS) {
298 				i40e_error(i40e, "failed to enable multicast "
299 				    "promiscuous mode on VSI %d: %d",
300 				    I40E_DEF_VSI_SEID(i40e), ret);
301 				return (EIO);
302 			}
303 		}
304 		i40e->i40e_mcast_promisc_count++;
305 		return (0);
306 	}
307 
308 	mc = &i40e->i40e_maddrs[i40e->i40e_resources.ifr_nmcastfilt_used];
309 	bzero(&filt, sizeof (filt));
310 	bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
311 	filt.flags = I40E_AQC_MACVLAN_ADD_HASH_MATCH |
312 	    I40E_AQC_MACVLAN_ADD_IGNORE_VLAN;
313 
314 	if ((ret = i40e_aq_add_macvlan(hw, I40E_DEF_VSI_SEID(i40e), &filt, 1,
315 	    NULL)) != I40E_SUCCESS) {
316 		i40e_error(i40e, "failed to add mac address "
317 		    "%2x:%2x:%2x:%2x:%2x:%2x to multicast filter: %d",
318 		    multicast_address[0], multicast_address[1],
319 		    multicast_address[2], multicast_address[3],
320 		    multicast_address[4], multicast_address[5],
321 		    ret);
322 		return (EIO);
323 	}
324 
325 	bcopy(multicast_address, mc->ima_mac, ETHERADDRL);
326 	i40e->i40e_resources.ifr_nmcastfilt_used++;
327 	return (0);
328 }
329 
330 /*
331  * See the big theory statement in i40e_main.c for multicast address management.
332  */
333 static int
334 i40e_multicast_remove(i40e_t *i40e, const uint8_t *multicast_address)
335 {
336 	int i, ret;
337 	struct i40e_hw *hw = &i40e->i40e_hw_space;
338 
339 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
340 
341 	for (i = 0; i < i40e->i40e_resources.ifr_nmcastfilt_used; i++) {
342 		struct i40e_aqc_remove_macvlan_element_data filt;
343 		int last;
344 
345 		if (bcmp(multicast_address, i40e->i40e_maddrs[i].ima_mac,
346 		    ETHERADDRL) != 0) {
347 			continue;
348 		}
349 
350 		bzero(&filt, sizeof (filt));
351 		bcopy(multicast_address, filt.mac_addr, ETHERADDRL);
352 		filt.flags = I40E_AQC_MACVLAN_DEL_HASH_MATCH |
353 		    I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
354 
355 		if (i40e_aq_remove_macvlan(hw, I40E_DEF_VSI_SEID(i40e), &filt,
356 		    1, NULL) != I40E_SUCCESS) {
357 			i40e_error(i40e, "failed to remove mac address "
358 			    "%2x:%2x:%2x:%2x:%2x:%2x from multicast "
359 			    "filter: %d",
360 			    multicast_address[0], multicast_address[1],
361 			    multicast_address[2], multicast_address[3],
362 			    multicast_address[4], multicast_address[5],
363 			    filt.error_code);
364 			return (EIO);
365 		}
366 
367 		last = i40e->i40e_resources.ifr_nmcastfilt_used - 1;
368 		if (i != last) {
369 			bcopy(&i40e->i40e_maddrs[last], &i40e->i40e_maddrs[i],
370 			    sizeof (i40e_maddr_t));
371 			bzero(&i40e->i40e_maddrs[last], sizeof (i40e_maddr_t));
372 		}
373 
374 		ASSERT(i40e->i40e_resources.ifr_nmcastfilt_used > 0);
375 		i40e->i40e_resources.ifr_nmcastfilt_used--;
376 		return (0);
377 	}
378 
379 	if (i40e->i40e_mcast_promisc_count > 0) {
380 		if (i40e->i40e_mcast_promisc_count == 1 &&
381 		    i40e->i40e_promisc_on == B_FALSE) {
382 			ret = i40e_aq_set_vsi_multicast_promiscuous(hw,
383 			    I40E_DEF_VSI_SEID(i40e), B_FALSE, NULL);
384 			if (ret != I40E_SUCCESS) {
385 				i40e_error(i40e, "failed to disable "
386 				    "multicast promiscuous mode on VSI %d: %d",
387 				    I40E_DEF_VSI_SEID(i40e), ret);
388 				return (EIO);
389 			}
390 		}
391 		i40e->i40e_mcast_promisc_count--;
392 
393 		return (0);
394 	}
395 
396 	return (ENOENT);
397 }
398 
399 static int
400 i40e_m_multicast(void *arg, boolean_t add, const uint8_t *multicast_address)
401 {
402 	i40e_t *i40e = arg;
403 	int rc;
404 
405 	mutex_enter(&i40e->i40e_general_lock);
406 
407 	if (i40e->i40e_state & I40E_SUSPENDED) {
408 		mutex_exit(&i40e->i40e_general_lock);
409 		return (ECANCELED);
410 	}
411 
412 	if (add == B_TRUE) {
413 		rc = i40e_multicast_add(i40e, multicast_address);
414 	} else {
415 		rc = i40e_multicast_remove(i40e, multicast_address);
416 	}
417 
418 	mutex_exit(&i40e->i40e_general_lock);
419 	return (rc);
420 }
421 
422 /* ARGSUSED */
423 static void
424 i40e_m_ioctl(void *arg, queue_t *q, mblk_t *mp)
425 {
426 	/*
427 	 * At this time, we don't support toggling i40e into loopback mode. It's
428 	 * questionable how much value this has when there's no clear way to
429 	 * toggle this behavior from a supported way in userland.
430 	 */
431 	miocnak(q, mp, 0, EINVAL);
432 }
433 
434 static int
435 i40e_ring_start(mac_ring_driver_t rh, uint64_t gen_num)
436 {
437 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)rh;
438 
439 	/*
440 	 * GLDv3 requires we keep track of a generation number, as it uses
441 	 * that number to keep track of whether or not a ring is active.
442 	 */
443 	mutex_enter(&itrq->itrq_rx_lock);
444 	itrq->itrq_rxgen = gen_num;
445 	mutex_exit(&itrq->itrq_rx_lock);
446 	return (0);
447 }
448 
449 /* ARGSUSED */
450 static int
451 i40e_rx_ring_intr_enable(mac_intr_handle_t intrh)
452 {
453 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
454 
455 	mutex_enter(&itrq->itrq_rx_lock);
456 	ASSERT(itrq->itrq_intr_poll == B_TRUE);
457 	i40e_intr_rx_queue_enable(itrq);
458 	itrq->itrq_intr_poll = B_FALSE;
459 	mutex_exit(&itrq->itrq_rx_lock);
460 
461 	return (0);
462 }
463 
464 /* ARGSUSED */
465 static int
466 i40e_rx_ring_intr_disable(mac_intr_handle_t intrh)
467 {
468 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
469 
470 	mutex_enter(&itrq->itrq_rx_lock);
471 	i40e_intr_rx_queue_disable(itrq);
472 	itrq->itrq_intr_poll = B_TRUE;
473 	mutex_exit(&itrq->itrq_rx_lock);
474 
475 	return (0);
476 }
477 
478 /* ARGSUSED */
479 static void
480 i40e_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
481     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
482 {
483 	i40e_t *i40e = arg;
484 	mac_intr_t *mintr = &infop->mri_intr;
485 	i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
486 
487 	/*
488 	 * Note the group index here is expected to be -1 due to the fact that
489 	 * we're not actually grouping things tx-wise at this time.
490 	 */
491 	ASSERT(group_index == -1);
492 	ASSERT(ring_index < i40e->i40e_num_trqpairs_per_vsi);
493 
494 	itrq->itrq_mactxring = rh;
495 	infop->mri_driver = (mac_ring_driver_t)itrq;
496 	infop->mri_start = NULL;
497 	infop->mri_stop = NULL;
498 	infop->mri_tx = i40e_ring_tx;
499 	infop->mri_stat = i40e_tx_ring_stat;
500 
501 	/*
502 	 * We only provide the handle in cases where we have MSI-X interrupts,
503 	 * to indicate that we'd actually support retargetting.
504 	 */
505 	if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
506 		mintr->mi_ddi_handle =
507 		    i40e->i40e_intr_handles[itrq->itrq_tx_intrvec];
508 	}
509 }
510 
511 /* ARGSUSED */
512 static void
513 i40e_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
514     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
515 {
516 	i40e_t *i40e = arg;
517 	mac_intr_t *mintr = &infop->mri_intr;
518 	uint_t trqpair_index;
519 	i40e_trqpair_t *itrq;
520 
521 	/* This assumes static groups. */
522 	ASSERT3S(group_index, >=, 0);
523 	ASSERT3S(ring_index, >=, 0);
524 	trqpair_index = (group_index * i40e->i40e_num_trqpairs_per_vsi) +
525 	    ring_index;
526 	ASSERT3U(trqpair_index, <, i40e->i40e_num_trqpairs);
527 	itrq = &i40e->i40e_trqpairs[trqpair_index];
528 
529 	itrq->itrq_macrxring = rh;
530 	infop->mri_driver = (mac_ring_driver_t)itrq;
531 	infop->mri_start = i40e_ring_start;
532 	infop->mri_stop = NULL;
533 	infop->mri_poll = i40e_ring_rx_poll;
534 	infop->mri_stat = i40e_rx_ring_stat;
535 	mintr->mi_handle = (mac_intr_handle_t)itrq;
536 	mintr->mi_enable = i40e_rx_ring_intr_enable;
537 	mintr->mi_disable = i40e_rx_ring_intr_disable;
538 
539 	/*
540 	 * We only provide the handle in cases where we have MSI-X interrupts,
541 	 * to indicate that we'd actually support retargetting.
542 	 */
543 	if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
544 		mintr->mi_ddi_handle =
545 		    i40e->i40e_intr_handles[itrq->itrq_rx_intrvec];
546 	}
547 }
548 
549 /* ARGSUSED */
550 static void
551 i40e_fill_rx_group(void *arg, mac_ring_type_t rtype, const int index,
552     mac_group_info_t *infop, mac_group_handle_t gh)
553 {
554 	i40e_t *i40e = arg;
555 	i40e_rx_group_t *rxg;
556 
557 	if (rtype != MAC_RING_TYPE_RX)
558 		return;
559 
560 	rxg = &i40e->i40e_rx_groups[index];
561 	rxg->irg_grp_hdl = gh;
562 
563 	infop->mgi_driver = (mac_group_driver_t)rxg;
564 	infop->mgi_start = NULL;
565 	infop->mgi_stop = NULL;
566 	infop->mgi_addmac = i40e_group_add_mac;
567 	infop->mgi_remmac = i40e_group_remove_mac;
568 
569 	ASSERT3U(i40e->i40e_num_rx_groups, <=, I40E_MAX_NUM_RX_GROUPS);
570 	infop->mgi_count = i40e->i40e_num_trqpairs_per_vsi;
571 }
572 
573 static int
574 i40e_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
575 {
576 	boolean_t present, usable;
577 	i40e_t *i40e = arg;
578 
579 	if (id != 0 || infop == NULL)
580 		return (EINVAL);
581 
582 	mutex_enter(&i40e->i40e_general_lock);
583 	switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
584 	case I40E_MODULE_TYPE_SFP:
585 	case I40E_MODULE_TYPE_QSFP:
586 		break;
587 	default:
588 		mutex_exit(&i40e->i40e_general_lock);
589 		return (ENOTSUP);
590 	}
591 
592 	present = !!(i40e->i40e_hw_space.phy.link_info.link_info &
593 	    I40E_AQ_MEDIA_AVAILABLE);
594 	if (present) {
595 		usable = !!(i40e->i40e_hw_space.phy.link_info.an_info &
596 		    I40E_AQ_QUALIFIED_MODULE);
597 	} else {
598 		usable = B_FALSE;
599 	}
600 	mutex_exit(&i40e->i40e_general_lock);
601 
602 	mac_transceiver_info_set_usable(infop, usable);
603 	mac_transceiver_info_set_present(infop, present);
604 
605 	return (0);
606 }
607 
608 static int
609 i40e_transceiver_read(void *arg, uint_t id, uint_t page, void *buf,
610     size_t nbytes, off_t offset, size_t *nread)
611 {
612 	i40e_t *i40e = arg;
613 	struct i40e_hw *hw = &i40e->i40e_hw_space;
614 	uint8_t *buf8 = buf;
615 	size_t i;
616 
617 	if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL ||
618 	    (page != 0xa0 && page != 0xa2) || offset < 0)
619 		return (EINVAL);
620 
621 	/*
622 	 * Both supported pages have a length of 256 bytes, ensure nothing asks
623 	 * us to go beyond that.
624 	 */
625 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
626 		return (EINVAL);
627 	}
628 
629 	mutex_enter(&i40e->i40e_general_lock);
630 	switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
631 	case I40E_MODULE_TYPE_SFP:
632 	case I40E_MODULE_TYPE_QSFP:
633 		break;
634 	default:
635 		mutex_exit(&i40e->i40e_general_lock);
636 		return (ENOTSUP);
637 	}
638 
639 	/*
640 	 * Make sure we have a sufficiently new firmware version to run this
641 	 * command. This was introduced in firmware API 1.7. This is apparently
642 	 * only supported on the XL710 MAC, not the XL722.
643 	 */
644 	if (hw->mac.type != I40E_MAC_XL710 || hw->aq.api_maj_ver != 1 ||
645 	    hw->aq.api_min_ver < 7) {
646 		mutex_exit(&i40e->i40e_general_lock);
647 		return (ENOTSUP);
648 	}
649 
650 	for (i = 0; i < nbytes; i++, offset++) {
651 		enum i40e_status_code status;
652 		uint32_t val;
653 
654 		status = i40e_aq_get_phy_register(hw,
655 		    I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, page, offset,
656 		    &val, NULL);
657 		if (status != I40E_SUCCESS) {
658 			mutex_exit(&i40e->i40e_general_lock);
659 			return (EIO);
660 		}
661 
662 		buf8[i] = (uint8_t)val;
663 	}
664 
665 	mutex_exit(&i40e->i40e_general_lock);
666 	*nread = nbytes;
667 
668 	return (0);
669 }
670 
671 static int
672 i40e_gld_led_set(void *arg, mac_led_mode_t mode, uint_t flags)
673 {
674 	i40e_t *i40e = arg;
675 	struct i40e_hw *hw = &i40e->i40e_hw_space;
676 
677 	if (flags != 0)
678 		return (EINVAL);
679 
680 	if (mode != MAC_LED_DEFAULT &&
681 	    mode != MAC_LED_IDENT &&
682 	    mode != MAC_LED_OFF &&
683 	    mode != MAC_LED_ON)
684 		return (ENOTSUP);
685 
686 	if (mode != MAC_LED_DEFAULT && !i40e->i40e_led_saved) {
687 		i40e->i40e_led_status = i40e_led_get(hw);
688 		i40e->i40e_led_saved = B_TRUE;
689 	}
690 
691 	switch (mode) {
692 	case MAC_LED_DEFAULT:
693 		if (i40e->i40e_led_saved) {
694 			i40e_led_set(hw, i40e->i40e_led_status, B_FALSE);
695 			i40e->i40e_led_status = 0;
696 			i40e->i40e_led_saved = B_FALSE;
697 		}
698 		break;
699 	case MAC_LED_IDENT:
700 		i40e_led_set(hw, 0xf, B_TRUE);
701 		break;
702 	case MAC_LED_OFF:
703 		i40e_led_set(hw, 0x0, B_FALSE);
704 		break;
705 	case MAC_LED_ON:
706 		i40e_led_set(hw, 0xf, B_FALSE);
707 		break;
708 	default:
709 		return (ENOTSUP);
710 	}
711 
712 	return (0);
713 }
714 
715 static boolean_t
716 i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
717 {
718 	i40e_t *i40e = arg;
719 	mac_capab_rings_t *cap_rings;
720 	mac_capab_transceiver_t *mct;
721 	mac_capab_led_t *mcl;
722 
723 	switch (cap) {
724 	case MAC_CAPAB_HCKSUM: {
725 		uint32_t *txflags = cap_data;
726 
727 		*txflags = 0;
728 		if (i40e->i40e_tx_hcksum_enable == B_TRUE)
729 			*txflags = HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
730 		break;
731 	}
732 
733 	case MAC_CAPAB_LSO: {
734 		mac_capab_lso_t *cap_lso = cap_data;
735 
736 		if (i40e->i40e_tx_lso_enable == B_TRUE) {
737 			cap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4 |
738 			    LSO_TX_BASIC_TCP_IPV6;
739 			cap_lso->lso_basic_tcp_ipv4.lso_max = I40E_LSO_MAXLEN;
740 			cap_lso->lso_basic_tcp_ipv6.lso_max = I40E_LSO_MAXLEN;
741 		} else {
742 			return (B_FALSE);
743 		}
744 		break;
745 	}
746 
747 	case MAC_CAPAB_RINGS:
748 		cap_rings = cap_data;
749 		cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
750 		switch (cap_rings->mr_type) {
751 		case MAC_RING_TYPE_TX:
752 			/*
753 			 * Note, saying we have no groups, but some
754 			 * number of rings indicates to MAC that it
755 			 * should create psuedo-groups with one for
756 			 * each TX ring. This may not be the long term
757 			 * behavior we want, but it'll work for now.
758 			 */
759 			cap_rings->mr_gnum = 0;
760 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs_per_vsi;
761 			cap_rings->mr_rget = i40e_fill_tx_ring;
762 			cap_rings->mr_gget = NULL;
763 			cap_rings->mr_gaddring = NULL;
764 			cap_rings->mr_gremring = NULL;
765 			break;
766 		case MAC_RING_TYPE_RX:
767 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
768 			cap_rings->mr_rget = i40e_fill_rx_ring;
769 			cap_rings->mr_gnum = i40e->i40e_num_rx_groups;
770 			cap_rings->mr_gget = i40e_fill_rx_group;
771 			cap_rings->mr_gaddring = NULL;
772 			cap_rings->mr_gremring = NULL;
773 			break;
774 		default:
775 			return (B_FALSE);
776 		}
777 		break;
778 	case MAC_CAPAB_TRANSCEIVER:
779 		mct = cap_data;
780 
781 		/*
782 		 * Firmware doesn't have a great way of telling us in advance
783 		 * whether we'd expect a SFF transceiver. As such, we always
784 		 * advertise the support for this capability.
785 		 */
786 		mct->mct_flags = 0;
787 		mct->mct_ntransceivers = 1;
788 		mct->mct_info = i40e_transceiver_info;
789 		mct->mct_read = i40e_transceiver_read;
790 
791 		return (B_TRUE);
792 	case MAC_CAPAB_LED:
793 		mcl = cap_data;
794 
795 		mcl->mcl_flags = 0;
796 		mcl->mcl_modes = MAC_LED_DEFAULT | MAC_LED_IDENT | MAC_LED_OFF |
797 		    MAC_LED_ON;
798 		mcl->mcl_set = i40e_gld_led_set;
799 		break;
800 
801 	default:
802 		return (B_FALSE);
803 	}
804 
805 	return (B_TRUE);
806 }
807 
808 /* ARGSUSED */
809 static int
810 i40e_m_setprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
811     const void *pr_val)
812 {
813 	int ret;
814 	long val;
815 	char *eptr;
816 
817 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
818 
819 	if ((ret = ddi_strtol(pr_val, &eptr, 10, &val)) != 0 ||
820 	    *eptr != '\0') {
821 		return (ret);
822 	}
823 
824 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
825 		if (val < I40E_MIN_RX_DMA_THRESH ||
826 		    val > I40E_MAX_RX_DMA_THRESH) {
827 			return (EINVAL);
828 		}
829 		i40e->i40e_rx_dma_min = (uint32_t)val;
830 		return (0);
831 	}
832 
833 	if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
834 		if (val < I40E_MIN_TX_DMA_THRESH ||
835 		    val > I40E_MAX_TX_DMA_THRESH) {
836 			return (EINVAL);
837 		}
838 		i40e->i40e_tx_dma_min = (uint32_t)val;
839 		return (0);
840 	}
841 
842 	if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
843 		if (val < I40E_MIN_ITR ||
844 		    val > I40E_MAX_ITR) {
845 			return (EINVAL);
846 		}
847 		i40e->i40e_rx_itr = (uint32_t)val;
848 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_RX, i40e->i40e_rx_itr);
849 		return (0);
850 	}
851 
852 	if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
853 		if (val < I40E_MIN_ITR ||
854 		    val > I40E_MAX_ITR) {
855 			return (EINVAL);
856 		}
857 		i40e->i40e_tx_itr = (uint32_t)val;
858 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_TX, i40e->i40e_tx_itr);
859 		return (0);
860 	}
861 
862 	if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
863 		if (val < I40E_MIN_ITR ||
864 		    val > I40E_MAX_ITR) {
865 			return (EINVAL);
866 		}
867 		i40e->i40e_tx_itr = (uint32_t)val;
868 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_OTHER,
869 		    i40e->i40e_other_itr);
870 		return (0);
871 	}
872 
873 	return (ENOTSUP);
874 }
875 
876 static int
877 i40e_m_getprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
878     void *pr_val)
879 {
880 	uint32_t val;
881 
882 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
883 
884 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
885 		val = i40e->i40e_rx_dma_min;
886 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
887 		val = i40e->i40e_tx_dma_min;
888 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
889 		val = i40e->i40e_rx_itr;
890 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
891 		val = i40e->i40e_tx_itr;
892 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
893 		val = i40e->i40e_other_itr;
894 	} else {
895 		return (ENOTSUP);
896 	}
897 
898 	if (snprintf(pr_val, pr_valsize, "%d", val) >= pr_valsize)
899 		return (ERANGE);
900 	return (0);
901 }
902 
903 /*
904  * Annoyingly for private properties MAC seems to ignore default values that
905  * aren't strings. That means that we have to translate all of these into
906  * uint32_t's and instead we size the buffer to be large enough to hold a
907  * uint32_t.
908  */
909 /* ARGSUSED */
910 static void
911 i40e_m_propinfo_private(i40e_t *i40e, const char *pr_name,
912     mac_prop_info_handle_t prh)
913 {
914 	char buf[64];
915 	uint32_t def;
916 
917 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
918 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
919 		def = I40E_DEF_RX_DMA_THRESH;
920 		mac_prop_info_set_range_uint32(prh,
921 		    I40E_MIN_RX_DMA_THRESH,
922 		    I40E_MAX_RX_DMA_THRESH);
923 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
924 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
925 		def = I40E_DEF_TX_DMA_THRESH;
926 		mac_prop_info_set_range_uint32(prh,
927 		    I40E_MIN_TX_DMA_THRESH,
928 		    I40E_MAX_TX_DMA_THRESH);
929 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
930 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
931 		def = I40E_DEF_RX_ITR;
932 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
933 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
934 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
935 		def = I40E_DEF_TX_ITR;
936 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
937 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
938 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
939 		def = I40E_DEF_OTHER_ITR;
940 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
941 	} else {
942 		return;
943 	}
944 
945 	(void) snprintf(buf, sizeof (buf), "%d", def);
946 	mac_prop_info_set_default_str(prh, buf);
947 }
948 
949 static int
950 i40e_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
951     uint_t pr_valsize, const void *pr_val)
952 {
953 	uint32_t new_mtu;
954 	i40e_t *i40e = arg;
955 	int ret = 0;
956 
957 	mutex_enter(&i40e->i40e_general_lock);
958 	if (i40e->i40e_state & I40E_SUSPENDED) {
959 		mutex_exit(&i40e->i40e_general_lock);
960 		return (ECANCELED);
961 	}
962 
963 	switch (pr_num) {
964 	/*
965 	 * These properties are always read-only across every device.
966 	 */
967 	case MAC_PROP_DUPLEX:
968 	case MAC_PROP_SPEED:
969 	case MAC_PROP_STATUS:
970 	case MAC_PROP_ADV_100FDX_CAP:
971 	case MAC_PROP_ADV_1000FDX_CAP:
972 	case MAC_PROP_ADV_10GFDX_CAP:
973 	case MAC_PROP_ADV_25GFDX_CAP:
974 	case MAC_PROP_ADV_40GFDX_CAP:
975 		ret = ENOTSUP;
976 		break;
977 	/*
978 	 * These are read-only at this time as we don't support configuring
979 	 * auto-negotiation. See the theory statement in i40e_main.c.
980 	 */
981 	case MAC_PROP_EN_100FDX_CAP:
982 	case MAC_PROP_EN_1000FDX_CAP:
983 	case MAC_PROP_EN_10GFDX_CAP:
984 	case MAC_PROP_EN_25GFDX_CAP:
985 	case MAC_PROP_EN_40GFDX_CAP:
986 	case MAC_PROP_AUTONEG:
987 	case MAC_PROP_FLOWCTRL:
988 		ret = ENOTSUP;
989 		break;
990 
991 	case MAC_PROP_MTU:
992 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
993 		if (new_mtu == i40e->i40e_sdu)
994 			break;
995 
996 		if (new_mtu < I40E_MIN_MTU ||
997 		    new_mtu > I40E_MAX_MTU) {
998 			ret = EINVAL;
999 			break;
1000 		}
1001 
1002 		if (i40e->i40e_state & I40E_STARTED) {
1003 			ret = EBUSY;
1004 			break;
1005 		}
1006 
1007 		ret = mac_maxsdu_update(i40e->i40e_mac_hdl, new_mtu);
1008 		if (ret == 0) {
1009 			i40e->i40e_sdu = new_mtu;
1010 			i40e_update_mtu(i40e);
1011 		}
1012 		break;
1013 
1014 	case MAC_PROP_PRIVATE:
1015 		ret = i40e_m_setprop_private(i40e, pr_name, pr_valsize, pr_val);
1016 		break;
1017 	default:
1018 		ret = ENOTSUP;
1019 		break;
1020 	}
1021 
1022 	mutex_exit(&i40e->i40e_general_lock);
1023 	return (ret);
1024 }
1025 
1026 static int
1027 i40e_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1028     uint_t pr_valsize, void *pr_val)
1029 {
1030 	i40e_t *i40e = arg;
1031 	uint64_t speed;
1032 	int ret = 0;
1033 	uint8_t *u8;
1034 	link_flowctrl_t fctl;
1035 
1036 	mutex_enter(&i40e->i40e_general_lock);
1037 
1038 	switch (pr_num) {
1039 	case MAC_PROP_DUPLEX:
1040 		if (pr_valsize < sizeof (link_duplex_t)) {
1041 			ret = EOVERFLOW;
1042 			break;
1043 		}
1044 		bcopy(&i40e->i40e_link_duplex, pr_val, sizeof (link_duplex_t));
1045 		break;
1046 	case MAC_PROP_SPEED:
1047 		if (pr_valsize < sizeof (uint64_t)) {
1048 			ret = EOVERFLOW;
1049 			break;
1050 		}
1051 		speed = i40e->i40e_link_speed * 1000000ULL;
1052 		bcopy(&speed, pr_val, sizeof (speed));
1053 		break;
1054 	case MAC_PROP_STATUS:
1055 		if (pr_valsize < sizeof (link_state_t)) {
1056 			ret = EOVERFLOW;
1057 			break;
1058 		}
1059 		bcopy(&i40e->i40e_link_state, pr_val, sizeof (link_state_t));
1060 		break;
1061 	case MAC_PROP_AUTONEG:
1062 		if (pr_valsize < sizeof (uint8_t)) {
1063 			ret = EOVERFLOW;
1064 			break;
1065 		}
1066 		u8 = pr_val;
1067 		*u8 = 1;
1068 		break;
1069 	case MAC_PROP_FLOWCTRL:
1070 		/*
1071 		 * Because we don't currently support hardware flow control, we
1072 		 * just hardcode this to be none.
1073 		 */
1074 		if (pr_valsize < sizeof (link_flowctrl_t)) {
1075 			ret = EOVERFLOW;
1076 			break;
1077 		}
1078 		fctl = LINK_FLOWCTRL_NONE;
1079 		bcopy(&fctl, pr_val, sizeof (link_flowctrl_t));
1080 		break;
1081 	case MAC_PROP_MTU:
1082 		if (pr_valsize < sizeof (uint32_t)) {
1083 			ret = EOVERFLOW;
1084 			break;
1085 		}
1086 		bcopy(&i40e->i40e_sdu, pr_val, sizeof (uint32_t));
1087 		break;
1088 
1089 	/*
1090 	 * Because we don't let users control the speeds we may auto-negotiate
1091 	 * to, the values of the ADV_ and EN_ will always be the same.
1092 	 */
1093 	case MAC_PROP_ADV_100FDX_CAP:
1094 	case MAC_PROP_EN_100FDX_CAP:
1095 		if (pr_valsize < sizeof (uint8_t)) {
1096 			ret = EOVERFLOW;
1097 			break;
1098 		}
1099 		u8 = pr_val;
1100 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0;
1101 		break;
1102 	case MAC_PROP_ADV_1000FDX_CAP:
1103 	case MAC_PROP_EN_1000FDX_CAP:
1104 		if (pr_valsize < sizeof (uint8_t)) {
1105 			ret = EOVERFLOW;
1106 			break;
1107 		}
1108 		u8 = pr_val;
1109 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0;
1110 		break;
1111 	case MAC_PROP_ADV_10GFDX_CAP:
1112 	case MAC_PROP_EN_10GFDX_CAP:
1113 		if (pr_valsize < sizeof (uint8_t)) {
1114 			ret = EOVERFLOW;
1115 			break;
1116 		}
1117 		u8 = pr_val;
1118 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0;
1119 		break;
1120 	case MAC_PROP_ADV_25GFDX_CAP:
1121 	case MAC_PROP_EN_25GFDX_CAP:
1122 		if (pr_valsize < sizeof (uint8_t)) {
1123 			ret = EOVERFLOW;
1124 			break;
1125 		}
1126 		u8 = pr_val;
1127 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0;
1128 		break;
1129 	case MAC_PROP_ADV_40GFDX_CAP:
1130 	case MAC_PROP_EN_40GFDX_CAP:
1131 		if (pr_valsize < sizeof (uint8_t)) {
1132 			ret = EOVERFLOW;
1133 			break;
1134 		}
1135 		u8 = pr_val;
1136 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0;
1137 		break;
1138 	case MAC_PROP_PRIVATE:
1139 		ret = i40e_m_getprop_private(i40e, pr_name, pr_valsize, pr_val);
1140 		break;
1141 	default:
1142 		ret = ENOTSUP;
1143 		break;
1144 	}
1145 
1146 	mutex_exit(&i40e->i40e_general_lock);
1147 
1148 	return (ret);
1149 }
1150 
1151 static void
1152 i40e_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1153     mac_prop_info_handle_t prh)
1154 {
1155 	i40e_t *i40e = arg;
1156 
1157 	mutex_enter(&i40e->i40e_general_lock);
1158 
1159 	switch (pr_num) {
1160 	case MAC_PROP_DUPLEX:
1161 	case MAC_PROP_SPEED:
1162 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1163 		break;
1164 	case MAC_PROP_FLOWCTRL:
1165 		/*
1166 		 * At the moment, the driver doesn't support flow control, hence
1167 		 * why this is set to read-only and none.
1168 		 */
1169 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1170 		mac_prop_info_set_default_link_flowctrl(prh,
1171 		    LINK_FLOWCTRL_NONE);
1172 		break;
1173 	case MAC_PROP_MTU:
1174 		mac_prop_info_set_range_uint32(prh, I40E_MIN_MTU, I40E_MAX_MTU);
1175 		break;
1176 
1177 	/*
1178 	 * We set the defaults for these based upon the phy's ability to
1179 	 * support the speeds. Note, auto-negotiation is required for fiber,
1180 	 * hence it is read-only and always enabled. When we have access to
1181 	 * copper phys we can revisit this.
1182 	 */
1183 	case MAC_PROP_AUTONEG:
1184 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1185 		mac_prop_info_set_default_uint8(prh, 1);
1186 		break;
1187 	case MAC_PROP_ADV_100FDX_CAP:
1188 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1189 		mac_prop_info_set_default_uint8(prh,
1190 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1191 		break;
1192 	case MAC_PROP_EN_100FDX_CAP:
1193 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1194 		mac_prop_info_set_default_uint8(prh,
1195 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1196 		break;
1197 	case MAC_PROP_ADV_1000FDX_CAP:
1198 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1199 		mac_prop_info_set_default_uint8(prh,
1200 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1201 		break;
1202 	case MAC_PROP_EN_1000FDX_CAP:
1203 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1204 		mac_prop_info_set_default_uint8(prh,
1205 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1206 		break;
1207 	case MAC_PROP_ADV_10GFDX_CAP:
1208 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1209 		mac_prop_info_set_default_uint8(prh,
1210 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1211 		break;
1212 	case MAC_PROP_EN_10GFDX_CAP:
1213 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1214 		mac_prop_info_set_default_uint8(prh,
1215 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1216 		break;
1217 	case MAC_PROP_ADV_25GFDX_CAP:
1218 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1219 		mac_prop_info_set_default_uint8(prh,
1220 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0);
1221 		break;
1222 	case MAC_PROP_EN_25GFDX_CAP:
1223 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1224 		mac_prop_info_set_default_uint8(prh,
1225 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0);
1226 		break;
1227 	case MAC_PROP_ADV_40GFDX_CAP:
1228 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1229 		mac_prop_info_set_default_uint8(prh,
1230 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1231 		break;
1232 	case MAC_PROP_EN_40GFDX_CAP:
1233 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1234 		mac_prop_info_set_default_uint8(prh,
1235 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0);
1236 		break;
1237 	case MAC_PROP_PRIVATE:
1238 		i40e_m_propinfo_private(i40e, pr_name, prh);
1239 		break;
1240 	default:
1241 		break;
1242 	}
1243 
1244 	mutex_exit(&i40e->i40e_general_lock);
1245 }
1246 
1247 #define	I40E_M_CALLBACK_FLAGS \
1248 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
1249 
1250 static mac_callbacks_t i40e_m_callbacks = {
1251 	I40E_M_CALLBACK_FLAGS,
1252 	i40e_m_stat,
1253 	i40e_m_start,
1254 	i40e_m_stop,
1255 	i40e_m_promisc,
1256 	i40e_m_multicast,
1257 	NULL,
1258 	NULL,
1259 	NULL,
1260 	i40e_m_ioctl,
1261 	i40e_m_getcapab,
1262 	NULL,
1263 	NULL,
1264 	i40e_m_setprop,
1265 	i40e_m_getprop,
1266 	i40e_m_propinfo
1267 };
1268 
1269 boolean_t
1270 i40e_register_mac(i40e_t *i40e)
1271 {
1272 	struct i40e_hw *hw = &i40e->i40e_hw_space;
1273 	int status;
1274 	mac_register_t *mac = mac_alloc(MAC_VERSION);
1275 
1276 	if (mac == NULL)
1277 		return (B_FALSE);
1278 
1279 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
1280 	mac->m_driver = i40e;
1281 	mac->m_dip = i40e->i40e_dip;
1282 	mac->m_src_addr = hw->mac.addr;
1283 	mac->m_callbacks = &i40e_m_callbacks;
1284 	mac->m_min_sdu = 0;
1285 	mac->m_max_sdu = i40e->i40e_sdu;
1286 	mac->m_margin = VLAN_TAGSZ;
1287 	mac->m_priv_props = i40e_priv_props;
1288 	mac->m_v12n = MAC_VIRT_LEVEL1;
1289 
1290 	status = mac_register(mac, &i40e->i40e_mac_hdl);
1291 	if (status != 0)
1292 		i40e_error(i40e, "mac_register() returned %d", status);
1293 	mac_free(mac);
1294 
1295 	return (status == 0);
1296 }
1297