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