xref: /illumos-gate/usr/src/uts/common/io/i40e/i40e_gld.c (revision bde334a8dbd66dfa70ce4d7fc9dcad6e1ae45fe4)
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_t *i40e = arg;
43 	struct i40e_aqc_remove_macvlan_element_data filt;
44 	struct i40e_hw *hw = &i40e->i40e_hw_space;
45 	int ret, i, last;
46 	i40e_uaddr_t *iua;
47 
48 	if (I40E_IS_MULTICAST(mac_addr))
49 		return (EINVAL);
50 
51 	mutex_enter(&i40e->i40e_general_lock);
52 
53 	if (i40e->i40e_state & I40E_SUSPENDED) {
54 		ret = ECANCELED;
55 		goto done;
56 	}
57 
58 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
59 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
60 		    ETHERADDRL) == 0)
61 			break;
62 	}
63 
64 	if (i == i40e->i40e_resources.ifr_nmacfilt_used) {
65 		ret = ENOENT;
66 		goto done;
67 	}
68 
69 	iua = &i40e->i40e_uaddrs[i];
70 	ASSERT(i40e->i40e_resources.ifr_nmacfilt_used > 0);
71 
72 	bzero(&filt, sizeof (filt));
73 	bcopy(mac_addr, filt.mac_addr, ETHERADDRL);
74 	filt.flags = I40E_AQC_MACVLAN_DEL_PERFECT_MATCH |
75 	    I40E_AQC_MACVLAN_DEL_IGNORE_VLAN;
76 
77 	if (i40e_aq_remove_macvlan(hw, iua->iua_vsi, &filt, 1, NULL) !=
78 	    I40E_SUCCESS) {
79 		i40e_error(i40e, "failed to remove mac address "
80 		    "%2x:%2x:%2x:%2x:%2x:%2x from unicast filter: %d",
81 		    mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3],
82 		    mac_addr[4], mac_addr[5], filt.error_code);
83 		ret = EIO;
84 		goto done;
85 	}
86 
87 	last = i40e->i40e_resources.ifr_nmacfilt_used - 1;
88 	if (i != last) {
89 		i40e_uaddr_t *src = &i40e->i40e_uaddrs[last];
90 		bcopy(src, iua, sizeof (i40e_uaddr_t));
91 	}
92 
93 	/*
94 	 * Set the multicast bit in the last one to indicate to ourselves that
95 	 * it's invalid.
96 	 */
97 	bzero(&i40e->i40e_uaddrs[last], sizeof (i40e_uaddr_t));
98 	i40e->i40e_uaddrs[last].iua_mac[0] = 0x01;
99 	i40e->i40e_resources.ifr_nmacfilt_used--;
100 	ret = 0;
101 done:
102 	mutex_exit(&i40e->i40e_general_lock);
103 
104 	return (ret);
105 }
106 
107 static int
108 i40e_group_add_mac(void *arg, const uint8_t *mac_addr)
109 {
110 	i40e_t *i40e = arg;
111 	struct i40e_hw *hw = &i40e->i40e_hw_space;
112 	int i, ret;
113 	i40e_uaddr_t *iua;
114 	struct i40e_aqc_add_macvlan_element_data filt;
115 
116 	if (I40E_IS_MULTICAST(mac_addr))
117 		return (EINVAL);
118 
119 	mutex_enter(&i40e->i40e_general_lock);
120 	if (i40e->i40e_state & I40E_SUSPENDED) {
121 		ret = ECANCELED;
122 		goto done;
123 	}
124 
125 	if (i40e->i40e_resources.ifr_nmacfilt ==
126 	    i40e->i40e_resources.ifr_nmacfilt_used) {
127 		ret = ENOSPC;
128 		goto done;
129 	}
130 
131 	for (i = 0; i < i40e->i40e_resources.ifr_nmacfilt_used; i++) {
132 		if (bcmp(mac_addr, i40e->i40e_uaddrs[i].iua_mac,
133 		    ETHERADDRL) == 0) {
134 			ret = EEXIST;
135 			goto done;
136 		}
137 	}
138 
139 	/*
140 	 * Note, the general use of the i40e_vsi_id will have to be refactored
141 	 * when we have proper group support.
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, i40e->i40e_vsi_id, &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 = i40e->i40e_vsi_id;
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, B_TRUE)) {
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, B_TRUE);
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->i40e_vsi_id,
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->i40e_vsi_id,
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, i40e->i40e_vsi_id,
261 		    !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->i40e_vsi_id, 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->i40e_vsi_id, 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->i40e_vsi_id, &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->i40e_vsi_id,
357 		    &filt, 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->i40e_vsi_id, 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->i40e_vsi_id, 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 
440 	/*
441 	 * GLDv3 requires we keep track of a generation number, as it uses
442 	 * that number to keep track of whether or not a ring is active.
443 	 */
444 	mutex_enter(&itrq->itrq_rx_lock);
445 	itrq->itrq_rxgen = gen_num;
446 	mutex_exit(&itrq->itrq_rx_lock);
447 	return (0);
448 }
449 
450 /* ARGSUSED */
451 static int
452 i40e_rx_ring_intr_enable(mac_intr_handle_t intrh)
453 {
454 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
455 
456 	mutex_enter(&itrq->itrq_rx_lock);
457 	ASSERT(itrq->itrq_intr_poll == B_TRUE);
458 	i40e_intr_rx_queue_enable(itrq);
459 	itrq->itrq_intr_poll = B_FALSE;
460 	mutex_exit(&itrq->itrq_rx_lock);
461 
462 	return (0);
463 }
464 
465 /* ARGSUSED */
466 static int
467 i40e_rx_ring_intr_disable(mac_intr_handle_t intrh)
468 {
469 	i40e_trqpair_t *itrq = (i40e_trqpair_t *)intrh;
470 
471 	mutex_enter(&itrq->itrq_rx_lock);
472 	i40e_intr_rx_queue_disable(itrq);
473 	itrq->itrq_intr_poll = B_TRUE;
474 	mutex_exit(&itrq->itrq_rx_lock);
475 
476 	return (0);
477 }
478 
479 /* ARGSUSED */
480 static void
481 i40e_fill_tx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
482     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
483 {
484 	i40e_t *i40e = arg;
485 	mac_intr_t *mintr = &infop->mri_intr;
486 	i40e_trqpair_t *itrq = &(i40e->i40e_trqpairs[ring_index]);
487 
488 	/*
489 	 * Note the group index here is expected to be -1 due to the fact that
490 	 * we're not actually grouping things tx-wise at this time.
491 	 */
492 	ASSERT(group_index == -1);
493 	ASSERT(ring_index < i40e->i40e_num_trqpairs);
494 
495 	itrq->itrq_mactxring = rh;
496 	infop->mri_driver = (mac_ring_driver_t)itrq;
497 	infop->mri_start = NULL;
498 	infop->mri_stop = NULL;
499 	infop->mri_tx = i40e_ring_tx;
500 	infop->mri_stat = i40e_tx_ring_stat;
501 
502 	/*
503 	 * We only provide the handle in cases where we have MSI-X interrupts,
504 	 * to indicate that we'd actually support retargetting.
505 	 */
506 	if (i40e->i40e_intr_type & DDI_INTR_TYPE_MSIX) {
507 		mintr->mi_ddi_handle =
508 		    i40e->i40e_intr_handles[itrq->itrq_tx_intrvec];
509 	}
510 }
511 
512 /* ARGSUSED */
513 static void
514 i40e_fill_rx_ring(void *arg, mac_ring_type_t rtype, const int group_index,
515     const int ring_index, mac_ring_info_t *infop, mac_ring_handle_t rh)
516 {
517 	i40e_t *i40e = arg;
518 	mac_intr_t *mintr = &infop->mri_intr;
519 	i40e_trqpair_t *itrq = &i40e->i40e_trqpairs[ring_index];
520 
521 	/*
522 	 * We assert the group number and ring index to help sanity check
523 	 * ourselves and mark that we'll need to rework this when we have
524 	 * multiple groups.
525 	 */
526 	ASSERT3S(group_index, ==, 0);
527 	ASSERT3S(ring_index, <, i40e->i40e_num_trqpairs);
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 
556 	if (rtype != MAC_RING_TYPE_RX)
557 		return;
558 
559 	/*
560 	 * Note, this is a simplified view of a group, given that we only have a
561 	 * single group and a single ring at the moment. We'll want to expand
562 	 * upon this as we leverage more hardware functionality.
563 	 */
564 	i40e->i40e_rx_group_handle = gh;
565 	infop->mgi_driver = (mac_group_driver_t)i40e;
566 	infop->mgi_start = NULL;
567 	infop->mgi_stop = NULL;
568 	infop->mgi_addmac = i40e_group_add_mac;
569 	infop->mgi_remmac = i40e_group_remove_mac;
570 
571 	ASSERT(i40e->i40e_num_rx_groups == I40E_GROUP_MAX);
572 	infop->mgi_count = i40e->i40e_num_trqpairs;
573 }
574 
575 static int
576 i40e_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
577 {
578 	boolean_t present, usable;
579 	i40e_t *i40e = arg;
580 
581 	if (id != 0 || infop == NULL)
582 		return (EINVAL);
583 
584 	mutex_enter(&i40e->i40e_general_lock);
585 	switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
586 	case I40E_MODULE_TYPE_SFP:
587 	case I40E_MODULE_TYPE_QSFP:
588 		break;
589 	default:
590 		mutex_exit(&i40e->i40e_general_lock);
591 		return (ENOTSUP);
592 	}
593 
594 	present = !!(i40e->i40e_hw_space.phy.link_info.link_info &
595 	    I40E_AQ_MEDIA_AVAILABLE);
596 	if (present) {
597 		usable = !!(i40e->i40e_hw_space.phy.link_info.an_info &
598 		    I40E_AQ_QUALIFIED_MODULE);
599 	} else {
600 		usable = B_FALSE;
601 	}
602 	mutex_exit(&i40e->i40e_general_lock);
603 
604 	mac_transceiver_info_set_usable(infop, usable);
605 	mac_transceiver_info_set_present(infop, present);
606 
607 	return (0);
608 }
609 
610 static int
611 i40e_transceiver_read(void *arg, uint_t id, uint_t page, void *buf,
612     size_t nbytes, off_t offset, size_t *nread)
613 {
614 	i40e_t *i40e = arg;
615 	struct i40e_hw *hw = &i40e->i40e_hw_space;
616 	uint8_t *buf8 = buf;
617 	size_t i;
618 
619 	if (id != 0 || buf == NULL || nbytes == 0 || nread == NULL ||
620 	    (page != 0xa0 && page != 0xa2) || offset < 0)
621 		return (EINVAL);
622 
623 	/*
624 	 * Both supported pages have a length of 256 bytes, ensure nothing asks
625 	 * us to go beyond that.
626 	 */
627 	if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
628 		return (EINVAL);
629 	}
630 
631 	mutex_enter(&i40e->i40e_general_lock);
632 	switch (i40e->i40e_hw_space.phy.link_info.module_type[0]) {
633 	case I40E_MODULE_TYPE_SFP:
634 	case I40E_MODULE_TYPE_QSFP:
635 		break;
636 	default:
637 		mutex_exit(&i40e->i40e_general_lock);
638 		return (ENOTSUP);
639 	}
640 
641 	/*
642 	 * Make sure we have a sufficiently new firmware version to run this
643 	 * command. This was introduced in firmware API 1.7. This is apparently
644 	 * only supported on the XL710 MAC, not the XL722.
645 	 */
646 	if (hw->mac.type != I40E_MAC_XL710 || hw->aq.api_maj_ver != 1 ||
647 	    hw->aq.api_min_ver < 7) {
648 		mutex_exit(&i40e->i40e_general_lock);
649 		return (ENOTSUP);
650 	}
651 
652 	for (i = 0; i < nbytes; i++, offset++) {
653 		enum i40e_status_code status;
654 		uint32_t val;
655 
656 		status = i40e_aq_get_phy_register(hw,
657 		    I40E_AQ_PHY_REG_ACCESS_EXTERNAL_MODULE, page, offset,
658 		    &val, NULL);
659 		if (status != I40E_SUCCESS) {
660 			mutex_exit(&i40e->i40e_general_lock);
661 			return (EIO);
662 		}
663 
664 		buf8[i] = (uint8_t)val;
665 	}
666 
667 	mutex_exit(&i40e->i40e_general_lock);
668 	*nread = nbytes;
669 
670 	return (0);
671 }
672 
673 static int
674 i40e_gld_led_set(void *arg, mac_led_mode_t mode, uint_t flags)
675 {
676 	i40e_t *i40e = arg;
677 	struct i40e_hw *hw = &i40e->i40e_hw_space;
678 
679 	if (flags != 0)
680 		return (EINVAL);
681 
682 	if (mode != MAC_LED_DEFAULT &&
683 	    mode != MAC_LED_IDENT &&
684 	    mode != MAC_LED_OFF &&
685 	    mode != MAC_LED_ON)
686 		return (ENOTSUP);
687 
688 	if (mode != MAC_LED_DEFAULT && !i40e->i40e_led_saved) {
689 		i40e->i40e_led_status = i40e_led_get(hw);
690 		i40e->i40e_led_saved = B_TRUE;
691 	}
692 
693 	switch (mode) {
694 	case MAC_LED_DEFAULT:
695 		if (i40e->i40e_led_saved) {
696 			i40e_led_set(hw, i40e->i40e_led_status, B_FALSE);
697 			i40e->i40e_led_status = 0;
698 			i40e->i40e_led_saved = B_FALSE;
699 		}
700 		break;
701 	case MAC_LED_IDENT:
702 		i40e_led_set(hw, 0xf, B_TRUE);
703 		break;
704 	case MAC_LED_OFF:
705 		i40e_led_set(hw, 0x0, B_FALSE);
706 		break;
707 	case MAC_LED_ON:
708 		i40e_led_set(hw, 0xf, B_FALSE);
709 		break;
710 	default:
711 		return (ENOTSUP);
712 	}
713 
714 	return (0);
715 }
716 
717 static boolean_t
718 i40e_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
719 {
720 	i40e_t *i40e = arg;
721 	mac_capab_rings_t *cap_rings;
722 	mac_capab_transceiver_t *mct;
723 	mac_capab_led_t *mcl;
724 
725 	switch (cap) {
726 	case MAC_CAPAB_HCKSUM: {
727 		uint32_t *txflags = cap_data;
728 
729 		*txflags = 0;
730 		if (i40e->i40e_tx_hcksum_enable == B_TRUE)
731 			*txflags = HCKSUM_INET_PARTIAL | HCKSUM_IPHDRCKSUM;
732 		break;
733 	}
734 
735 	case MAC_CAPAB_RINGS:
736 		cap_rings = cap_data;
737 		cap_rings->mr_group_type = MAC_GROUP_TYPE_STATIC;
738 		switch (cap_rings->mr_type) {
739 		case MAC_RING_TYPE_TX:
740 			/*
741 			 * Note, saying we have no rings, but some number of
742 			 * groups indicates to MAC that it should create
743 			 * psuedo-groups with one for each TX ring. This may not
744 			 * be the long term behavior we want, but it'll work for
745 			 * now.
746 			 */
747 			cap_rings->mr_gnum = 0;
748 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
749 			cap_rings->mr_rget = i40e_fill_tx_ring;
750 			cap_rings->mr_gget = NULL;
751 			cap_rings->mr_gaddring = NULL;
752 			cap_rings->mr_gremring = NULL;
753 			break;
754 		case MAC_RING_TYPE_RX:
755 			cap_rings->mr_rnum = i40e->i40e_num_trqpairs;
756 			cap_rings->mr_rget = i40e_fill_rx_ring;
757 			cap_rings->mr_gnum = I40E_GROUP_MAX;
758 			cap_rings->mr_gget = i40e_fill_rx_group;
759 			cap_rings->mr_gaddring = NULL;
760 			cap_rings->mr_gremring = NULL;
761 			break;
762 		default:
763 			return (B_FALSE);
764 		}
765 		break;
766 	case MAC_CAPAB_TRANSCEIVER:
767 		mct = cap_data;
768 
769 		/*
770 		 * Firmware doesn't have a great way of telling us in advance
771 		 * whether we'd expect a SFF transceiver. As such, we always
772 		 * advertise the support for this capability.
773 		 */
774 		mct->mct_flags = 0;
775 		mct->mct_ntransceivers = 1;
776 		mct->mct_info = i40e_transceiver_info;
777 		mct->mct_read = i40e_transceiver_read;
778 
779 		return (B_TRUE);
780 	case MAC_CAPAB_LED:
781 		mcl = cap_data;
782 
783 		mcl->mcl_flags = 0;
784 		mcl->mcl_modes = MAC_LED_DEFAULT | MAC_LED_IDENT | MAC_LED_OFF |
785 		    MAC_LED_ON;
786 		mcl->mcl_set = i40e_gld_led_set;
787 		break;
788 
789 	default:
790 		return (B_FALSE);
791 	}
792 
793 	return (B_TRUE);
794 }
795 
796 /* ARGSUSED */
797 static int
798 i40e_m_setprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
799     const void *pr_val)
800 {
801 	int ret;
802 	long val;
803 	char *eptr;
804 
805 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
806 
807 	if ((ret = ddi_strtol(pr_val, &eptr, 10, &val)) != 0 ||
808 	    *eptr != '\0') {
809 		return (ret);
810 	}
811 
812 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
813 		if (val < I40E_MIN_RX_DMA_THRESH ||
814 		    val > I40E_MAX_RX_DMA_THRESH) {
815 			return (EINVAL);
816 		}
817 		i40e->i40e_rx_dma_min = (uint32_t)val;
818 		return (0);
819 	}
820 
821 	if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
822 		if (val < I40E_MIN_TX_DMA_THRESH ||
823 		    val > I40E_MAX_TX_DMA_THRESH) {
824 			return (EINVAL);
825 		}
826 		i40e->i40e_tx_dma_min = (uint32_t)val;
827 		return (0);
828 	}
829 
830 	if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
831 		if (val < I40E_MIN_ITR ||
832 		    val > I40E_MAX_ITR) {
833 			return (EINVAL);
834 		}
835 		i40e->i40e_rx_itr = (uint32_t)val;
836 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_RX, i40e->i40e_rx_itr);
837 		return (0);
838 	}
839 
840 	if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
841 		if (val < I40E_MIN_ITR ||
842 		    val > I40E_MAX_ITR) {
843 			return (EINVAL);
844 		}
845 		i40e->i40e_tx_itr = (uint32_t)val;
846 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_TX, i40e->i40e_tx_itr);
847 		return (0);
848 	}
849 
850 	if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
851 		if (val < I40E_MIN_ITR ||
852 		    val > I40E_MAX_ITR) {
853 			return (EINVAL);
854 		}
855 		i40e->i40e_tx_itr = (uint32_t)val;
856 		i40e_intr_set_itr(i40e, I40E_ITR_INDEX_OTHER,
857 		    i40e->i40e_other_itr);
858 		return (0);
859 	}
860 
861 	return (ENOTSUP);
862 }
863 
864 static int
865 i40e_m_getprop_private(i40e_t *i40e, const char *pr_name, uint_t pr_valsize,
866     void *pr_val)
867 {
868 	uint32_t val;
869 
870 	ASSERT(MUTEX_HELD(&i40e->i40e_general_lock));
871 
872 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
873 		val = i40e->i40e_rx_dma_min;
874 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
875 		val = i40e->i40e_tx_dma_min;
876 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
877 		val = i40e->i40e_rx_itr;
878 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
879 		val = i40e->i40e_tx_itr;
880 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
881 		val = i40e->i40e_other_itr;
882 	} else {
883 		return (ENOTSUP);
884 	}
885 
886 	if (snprintf(pr_val, pr_valsize, "%d", val) >= pr_valsize)
887 		return (ERANGE);
888 	return (0);
889 }
890 
891 /*
892  * Annoyingly for private properties MAC seems to ignore default values that
893  * aren't strings. That means that we have to translate all of these into
894  * uint32_t's and instead we size the buffer to be large enough to hold a
895  * uint32_t.
896  */
897 /* ARGSUSED */
898 static void
899 i40e_m_propinfo_private(i40e_t *i40e, const char *pr_name,
900     mac_prop_info_handle_t prh)
901 {
902 	char buf[64];
903 	uint32_t def;
904 
905 	if (strcmp(pr_name, I40E_PROP_RX_DMA_THRESH) == 0) {
906 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
907 		def = I40E_DEF_RX_DMA_THRESH;
908 		mac_prop_info_set_range_uint32(prh,
909 		    I40E_MIN_RX_DMA_THRESH,
910 		    I40E_MAX_RX_DMA_THRESH);
911 	} else if (strcmp(pr_name, I40E_PROP_TX_DMA_THRESH) == 0) {
912 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
913 		def = I40E_DEF_TX_DMA_THRESH;
914 		mac_prop_info_set_range_uint32(prh,
915 		    I40E_MIN_TX_DMA_THRESH,
916 		    I40E_MAX_TX_DMA_THRESH);
917 	} else if (strcmp(pr_name, I40E_PROP_RX_ITR) == 0) {
918 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
919 		def = I40E_DEF_RX_ITR;
920 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
921 	} else if (strcmp(pr_name, I40E_PROP_TX_ITR) == 0) {
922 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
923 		def = I40E_DEF_TX_ITR;
924 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
925 	} else if (strcmp(pr_name, I40E_PROP_OTHER_ITR) == 0) {
926 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_RW);
927 		def = I40E_DEF_OTHER_ITR;
928 		mac_prop_info_set_range_uint32(prh, I40E_MIN_ITR, I40E_MAX_ITR);
929 	} else {
930 		return;
931 	}
932 
933 	(void) snprintf(buf, sizeof (buf), "%d", def);
934 	mac_prop_info_set_default_str(prh, buf);
935 }
936 
937 static int
938 i40e_m_setprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
939     uint_t pr_valsize, const void *pr_val)
940 {
941 	uint32_t new_mtu;
942 	i40e_t *i40e = arg;
943 	int ret = 0;
944 
945 	mutex_enter(&i40e->i40e_general_lock);
946 	if (i40e->i40e_state & I40E_SUSPENDED) {
947 		mutex_exit(&i40e->i40e_general_lock);
948 		return (ECANCELED);
949 	}
950 
951 	switch (pr_num) {
952 	/*
953 	 * These properties are always read-only across every device.
954 	 */
955 	case MAC_PROP_DUPLEX:
956 	case MAC_PROP_SPEED:
957 	case MAC_PROP_STATUS:
958 	case MAC_PROP_ADV_100FDX_CAP:
959 	case MAC_PROP_ADV_1000FDX_CAP:
960 	case MAC_PROP_ADV_10GFDX_CAP:
961 	case MAC_PROP_ADV_25GFDX_CAP:
962 	case MAC_PROP_ADV_40GFDX_CAP:
963 		ret = ENOTSUP;
964 		break;
965 	/*
966 	 * These are read-only at this time as we don't support configuring
967 	 * auto-negotiation. See the theory statement in i40e_main.c.
968 	 */
969 	case MAC_PROP_EN_100FDX_CAP:
970 	case MAC_PROP_EN_1000FDX_CAP:
971 	case MAC_PROP_EN_10GFDX_CAP:
972 	case MAC_PROP_EN_25GFDX_CAP:
973 	case MAC_PROP_EN_40GFDX_CAP:
974 	case MAC_PROP_AUTONEG:
975 	case MAC_PROP_FLOWCTRL:
976 		ret = ENOTSUP;
977 		break;
978 
979 	case MAC_PROP_MTU:
980 		bcopy(pr_val, &new_mtu, sizeof (new_mtu));
981 		if (new_mtu == i40e->i40e_sdu)
982 			break;
983 
984 		if (new_mtu < I40E_MIN_MTU ||
985 		    new_mtu > I40E_MAX_MTU) {
986 			ret = EINVAL;
987 			break;
988 		}
989 
990 		if (i40e->i40e_state & I40E_STARTED) {
991 			ret = EBUSY;
992 			break;
993 		}
994 
995 		ret = mac_maxsdu_update(i40e->i40e_mac_hdl, new_mtu);
996 		if (ret == 0) {
997 			i40e->i40e_sdu = new_mtu;
998 			i40e_update_mtu(i40e);
999 		}
1000 		break;
1001 
1002 	case MAC_PROP_PRIVATE:
1003 		ret = i40e_m_setprop_private(i40e, pr_name, pr_valsize, pr_val);
1004 		break;
1005 	default:
1006 		ret = ENOTSUP;
1007 		break;
1008 	}
1009 
1010 	mutex_exit(&i40e->i40e_general_lock);
1011 	return (ret);
1012 }
1013 
1014 static int
1015 i40e_m_getprop(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1016     uint_t pr_valsize, void *pr_val)
1017 {
1018 	i40e_t *i40e = arg;
1019 	uint64_t speed;
1020 	int ret = 0;
1021 	uint8_t *u8;
1022 	link_flowctrl_t fctl;
1023 
1024 	mutex_enter(&i40e->i40e_general_lock);
1025 
1026 	switch (pr_num) {
1027 	case MAC_PROP_DUPLEX:
1028 		if (pr_valsize < sizeof (link_duplex_t)) {
1029 			ret = EOVERFLOW;
1030 			break;
1031 		}
1032 		bcopy(&i40e->i40e_link_duplex, pr_val, sizeof (link_duplex_t));
1033 		break;
1034 	case MAC_PROP_SPEED:
1035 		if (pr_valsize < sizeof (uint64_t)) {
1036 			ret = EOVERFLOW;
1037 			break;
1038 		}
1039 		speed = i40e->i40e_link_speed * 1000000ULL;
1040 		bcopy(&speed, pr_val, sizeof (speed));
1041 		break;
1042 	case MAC_PROP_STATUS:
1043 		if (pr_valsize < sizeof (link_state_t)) {
1044 			ret = EOVERFLOW;
1045 			break;
1046 		}
1047 		bcopy(&i40e->i40e_link_state, pr_val, sizeof (link_state_t));
1048 		break;
1049 	case MAC_PROP_AUTONEG:
1050 		if (pr_valsize < sizeof (uint8_t)) {
1051 			ret = EOVERFLOW;
1052 			break;
1053 		}
1054 		u8 = pr_val;
1055 		*u8 = 1;
1056 		break;
1057 	case MAC_PROP_FLOWCTRL:
1058 		/*
1059 		 * Because we don't currently support hardware flow control, we
1060 		 * just hardcode this to be none.
1061 		 */
1062 		if (pr_valsize < sizeof (link_flowctrl_t)) {
1063 			ret = EOVERFLOW;
1064 			break;
1065 		}
1066 		fctl = LINK_FLOWCTRL_NONE;
1067 		bcopy(&fctl, pr_val, sizeof (link_flowctrl_t));
1068 		break;
1069 	case MAC_PROP_MTU:
1070 		if (pr_valsize < sizeof (uint32_t)) {
1071 			ret = EOVERFLOW;
1072 			break;
1073 		}
1074 		bcopy(&i40e->i40e_sdu, pr_val, sizeof (uint32_t));
1075 		break;
1076 
1077 	/*
1078 	 * Because we don't let users control the speeds we may auto-negotiate
1079 	 * to, the values of the ADV_ and EN_ will always be the same.
1080 	 */
1081 	case MAC_PROP_ADV_100FDX_CAP:
1082 	case MAC_PROP_EN_100FDX_CAP:
1083 		if (pr_valsize < sizeof (uint8_t)) {
1084 			ret = EOVERFLOW;
1085 			break;
1086 		}
1087 		u8 = pr_val;
1088 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0;
1089 		break;
1090 	case MAC_PROP_ADV_1000FDX_CAP:
1091 	case MAC_PROP_EN_1000FDX_CAP:
1092 		if (pr_valsize < sizeof (uint8_t)) {
1093 			ret = EOVERFLOW;
1094 			break;
1095 		}
1096 		u8 = pr_val;
1097 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0;
1098 		break;
1099 	case MAC_PROP_ADV_10GFDX_CAP:
1100 	case MAC_PROP_EN_10GFDX_CAP:
1101 		if (pr_valsize < sizeof (uint8_t)) {
1102 			ret = EOVERFLOW;
1103 			break;
1104 		}
1105 		u8 = pr_val;
1106 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0;
1107 		break;
1108 	case MAC_PROP_ADV_25GFDX_CAP:
1109 	case MAC_PROP_EN_25GFDX_CAP:
1110 		if (pr_valsize < sizeof (uint8_t)) {
1111 			ret = EOVERFLOW;
1112 			break;
1113 		}
1114 		u8 = pr_val;
1115 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_25GB) != 0;
1116 		break;
1117 	case MAC_PROP_ADV_40GFDX_CAP:
1118 	case MAC_PROP_EN_40GFDX_CAP:
1119 		if (pr_valsize < sizeof (uint8_t)) {
1120 			ret = EOVERFLOW;
1121 			break;
1122 		}
1123 		u8 = pr_val;
1124 		*u8 = (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_40GB) != 0;
1125 		break;
1126 	case MAC_PROP_PRIVATE:
1127 		ret = i40e_m_getprop_private(i40e, pr_name, pr_valsize, pr_val);
1128 		break;
1129 	default:
1130 		ret = ENOTSUP;
1131 		break;
1132 	}
1133 
1134 	mutex_exit(&i40e->i40e_general_lock);
1135 
1136 	return (ret);
1137 }
1138 
1139 static void
1140 i40e_m_propinfo(void *arg, const char *pr_name, mac_prop_id_t pr_num,
1141     mac_prop_info_handle_t prh)
1142 {
1143 	i40e_t *i40e = arg;
1144 
1145 	mutex_enter(&i40e->i40e_general_lock);
1146 
1147 	switch (pr_num) {
1148 	case MAC_PROP_DUPLEX:
1149 	case MAC_PROP_SPEED:
1150 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1151 		break;
1152 	case MAC_PROP_FLOWCTRL:
1153 		/*
1154 		 * At the moment, the driver doesn't support flow control, hence
1155 		 * why this is set to read-only and none.
1156 		 */
1157 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1158 		mac_prop_info_set_default_link_flowctrl(prh,
1159 		    LINK_FLOWCTRL_NONE);
1160 		break;
1161 	case MAC_PROP_MTU:
1162 		mac_prop_info_set_range_uint32(prh, I40E_MIN_MTU, I40E_MAX_MTU);
1163 		break;
1164 
1165 	/*
1166 	 * We set the defaults for these based upon the phy's ability to
1167 	 * support the speeds. Note, auto-negotiation is required for fiber,
1168 	 * hence it is read-only and always enabled. When we have access to
1169 	 * copper phys we can revisit this.
1170 	 */
1171 	case MAC_PROP_AUTONEG:
1172 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1173 		mac_prop_info_set_default_uint8(prh, 1);
1174 		break;
1175 	case MAC_PROP_ADV_100FDX_CAP:
1176 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1177 		mac_prop_info_set_default_uint8(prh,
1178 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1179 		break;
1180 	case MAC_PROP_EN_100FDX_CAP:
1181 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1182 		mac_prop_info_set_default_uint8(prh,
1183 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_100MB) != 0);
1184 		break;
1185 	case MAC_PROP_ADV_1000FDX_CAP:
1186 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1187 		mac_prop_info_set_default_uint8(prh,
1188 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1189 		break;
1190 	case MAC_PROP_EN_1000FDX_CAP:
1191 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1192 		mac_prop_info_set_default_uint8(prh,
1193 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_1GB) != 0);
1194 		break;
1195 	case MAC_PROP_ADV_10GFDX_CAP:
1196 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1197 		mac_prop_info_set_default_uint8(prh,
1198 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1199 		break;
1200 	case MAC_PROP_EN_10GFDX_CAP:
1201 		mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ);
1202 		mac_prop_info_set_default_uint8(prh,
1203 		    (i40e->i40e_phy.link_speed & I40E_LINK_SPEED_10GB) != 0);
1204 		break;
1205 	case MAC_PROP_ADV_25GFDX_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_25GB) != 0);
1209 		break;
1210 	case MAC_PROP_EN_25GFDX_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_25GB) != 0);
1214 		break;
1215 	case MAC_PROP_ADV_40GFDX_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_40GB) != 0);
1219 		break;
1220 	case MAC_PROP_EN_40GFDX_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_40GB) != 0);
1224 		break;
1225 	case MAC_PROP_PRIVATE:
1226 		i40e_m_propinfo_private(i40e, pr_name, prh);
1227 		break;
1228 	default:
1229 		break;
1230 	}
1231 
1232 	mutex_exit(&i40e->i40e_general_lock);
1233 }
1234 
1235 #define	I40E_M_CALLBACK_FLAGS \
1236 	(MC_IOCTL | MC_GETCAPAB | MC_SETPROP | MC_GETPROP | MC_PROPINFO)
1237 
1238 static mac_callbacks_t i40e_m_callbacks = {
1239 	I40E_M_CALLBACK_FLAGS,
1240 	i40e_m_stat,
1241 	i40e_m_start,
1242 	i40e_m_stop,
1243 	i40e_m_promisc,
1244 	i40e_m_multicast,
1245 	NULL,
1246 	NULL,
1247 	NULL,
1248 	i40e_m_ioctl,
1249 	i40e_m_getcapab,
1250 	NULL,
1251 	NULL,
1252 	i40e_m_setprop,
1253 	i40e_m_getprop,
1254 	i40e_m_propinfo
1255 };
1256 
1257 boolean_t
1258 i40e_register_mac(i40e_t *i40e)
1259 {
1260 	struct i40e_hw *hw = &i40e->i40e_hw_space;
1261 	int status;
1262 	mac_register_t *mac = mac_alloc(MAC_VERSION);
1263 
1264 	if (mac == NULL)
1265 		return (B_FALSE);
1266 
1267 	mac->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
1268 	mac->m_driver = i40e;
1269 	mac->m_dip = i40e->i40e_dip;
1270 	mac->m_src_addr = hw->mac.addr;
1271 	mac->m_callbacks = &i40e_m_callbacks;
1272 	mac->m_min_sdu = 0;
1273 	mac->m_max_sdu = i40e->i40e_sdu;
1274 	mac->m_margin = VLAN_TAGSZ;
1275 	mac->m_priv_props = i40e_priv_props;
1276 	mac->m_v12n = MAC_VIRT_LEVEL1;
1277 
1278 	status = mac_register(mac, &i40e->i40e_mac_hdl);
1279 	if (status != 0)
1280 		i40e_error(i40e, "mac_register() returned %d", status);
1281 	mac_free(mac);
1282 
1283 	return (status == 0);
1284 }
1285