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