xref: /illumos-gate/usr/src/uts/common/io/sundlpi.c (revision 69b1fd3f24d0ee2e682883606201c61f52085805)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  *  Common Sun DLPI routines.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/sysmacros.h>
32 #include <sys/byteorder.h>
33 #include <sys/stream.h>
34 #include <sys/strsun.h>
35 #include <sys/dlpi.h>
36 #include <sys/ddi.h>
37 #include <sys/sunddi.h>
38 #include <sys/sunldi.h>
39 #include <sys/cmn_err.h>
40 
41 void
42 dlbindack(
43 	queue_t		*wq,
44 	mblk_t		*mp,
45 	t_scalar_t	sap,
46 	const void	*addrp,
47 	t_uscalar_t	addrlen,
48 	t_uscalar_t	maxconind,
49 	t_uscalar_t	xidtest)
50 {
51 	union DL_primitives	*dlp;
52 	size_t			size;
53 
54 	size = sizeof (dl_bind_ack_t) + addrlen;
55 	if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_BIND_ACK)) == NULL)
56 		return;
57 
58 	dlp = (union DL_primitives *)mp->b_rptr;
59 	dlp->bind_ack.dl_sap = sap;
60 	dlp->bind_ack.dl_addr_length = addrlen;
61 	dlp->bind_ack.dl_addr_offset = sizeof (dl_bind_ack_t);
62 	dlp->bind_ack.dl_max_conind = maxconind;
63 	dlp->bind_ack.dl_xidtest_flg = xidtest;
64 	if (addrlen != 0)
65 		bcopy(addrp, mp->b_rptr + sizeof (dl_bind_ack_t), addrlen);
66 	qreply(wq, mp);
67 }
68 
69 void
70 dlokack(
71 	queue_t		*wq,
72 	mblk_t		*mp,
73 	t_uscalar_t	correct_primitive)
74 {
75 	union DL_primitives	*dlp;
76 
77 	if ((mp = mexchange(wq, mp, sizeof (dl_ok_ack_t), M_PCPROTO,
78 	    DL_OK_ACK)) == NULL)
79 		return;
80 	dlp = (union DL_primitives *)mp->b_rptr;
81 	dlp->ok_ack.dl_correct_primitive = correct_primitive;
82 	qreply(wq, mp);
83 }
84 
85 void
86 dlerrorack(
87 	queue_t		*wq,
88 	mblk_t		*mp,
89 	t_uscalar_t	error_primitive,
90 	t_uscalar_t	error,
91 	t_uscalar_t	unix_errno)
92 {
93 	union DL_primitives	*dlp;
94 
95 	if ((mp = mexchange(wq, mp, sizeof (dl_error_ack_t), M_PCPROTO,
96 	    DL_ERROR_ACK)) == NULL)
97 		return;
98 	dlp = (union DL_primitives *)mp->b_rptr;
99 	dlp->error_ack.dl_error_primitive = error_primitive;
100 	dlp->error_ack.dl_errno = error;
101 	dlp->error_ack.dl_unix_errno = unix_errno;
102 	qreply(wq, mp);
103 }
104 
105 void
106 dluderrorind(
107 	queue_t		*wq,
108 	mblk_t		*mp,
109 	const void	*addrp,
110 	t_uscalar_t	addrlen,
111 	t_uscalar_t	error,
112 	t_uscalar_t	unix_errno)
113 {
114 	union DL_primitives	*dlp;
115 	size_t			size;
116 
117 	size = sizeof (dl_uderror_ind_t) + addrlen;
118 	if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_UDERROR_IND)) == NULL)
119 		return;
120 
121 	dlp = (union DL_primitives *)mp->b_rptr;
122 	dlp->uderror_ind.dl_dest_addr_length = addrlen;
123 	dlp->uderror_ind.dl_dest_addr_offset = sizeof (dl_uderror_ind_t);
124 	dlp->uderror_ind.dl_unix_errno = unix_errno;
125 	dlp->uderror_ind.dl_errno = error;
126 	if (addrlen != 0)
127 		bcopy(addrp, mp->b_rptr + sizeof (dl_uderror_ind_t), addrlen);
128 	qreply(wq, mp);
129 }
130 
131 void
132 dlphysaddrack(
133 	queue_t		*wq,
134 	mblk_t		*mp,
135 	const void	*addrp,
136 	t_uscalar_t	len)
137 {
138 	union DL_primitives	*dlp;
139 	size_t			size;
140 
141 	size = sizeof (dl_phys_addr_ack_t) + len;
142 	if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_PHYS_ADDR_ACK)) == NULL)
143 		return;
144 	dlp = (union DL_primitives *)mp->b_rptr;
145 	dlp->physaddr_ack.dl_addr_length = len;
146 	dlp->physaddr_ack.dl_addr_offset = sizeof (dl_phys_addr_ack_t);
147 	if (len != 0)
148 		bcopy(addrp, mp->b_rptr + sizeof (dl_phys_addr_ack_t), len);
149 	qreply(wq, mp);
150 }
151 
152 void
153 dlcapabsetqid(dl_mid_t *idp, const queue_t *q)
154 {
155 #ifndef _LP64
156 	idp->mid[0] = (t_uscalar_t)q;
157 #else
158 	idp->mid[0] = (t_uscalar_t)BMASK_32((uint64_t)q);
159 	idp->mid[1] = (t_uscalar_t)BMASK_32(((uint64_t)q) >> 32);
160 #endif
161 }
162 
163 boolean_t
164 dlcapabcheckqid(const dl_mid_t *idp, const queue_t *q)
165 {
166 #ifndef _LP64
167 	return ((queue_t *)(idp->mid[0]) == q);
168 #else
169 	return ((queue_t *)
170 	    ((uint64_t)idp->mid[0] | ((uint64_t)idp->mid[1] << 32)) == q);
171 #endif
172 }
173 
174 void
175 dlnotifyack(
176 	queue_t		*wq,
177 	mblk_t		*mp,
178 	uint32_t	notifications)
179 {
180 	union DL_primitives	*dlp;
181 
182 	if ((mp = mexchange(wq, mp, sizeof (dl_notify_ack_t), M_PROTO,
183 	    DL_NOTIFY_ACK)) == NULL)
184 		return;
185 	dlp = (union DL_primitives *)mp->b_rptr;
186 	dlp->notify_ack.dl_notifications = notifications;
187 	qreply(wq, mp);
188 }
189 
190 static int
191 dl_op(ldi_handle_t lh, mblk_t **mpp, t_uscalar_t expprim, size_t minlen,
192     dl_error_ack_t *dleap, timestruc_t *tvp)
193 {
194 	int		err;
195 	size_t		len;
196 	mblk_t		*mp = *mpp;
197 	t_uscalar_t	reqprim, ackprim, ackreqprim;
198 	union DL_primitives *dlp;
199 
200 	reqprim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
201 
202 	(void) ldi_putmsg(lh, mp);
203 
204 	switch (err = ldi_getmsg(lh, &mp, tvp)) {
205 	case 0:
206 		break;
207 	case ETIME:
208 		cmn_err(CE_NOTE, "!dl_op: timed out waiting for %s to %s",
209 		    dl_primstr(reqprim), dl_primstr(expprim));
210 		return (ETIME);
211 	default:
212 		cmn_err(CE_NOTE, "!dl_op: ldi_getmsg() for %s failed: %d",
213 		    dl_primstr(expprim), err);
214 		return (err);
215 	}
216 
217 	len = MBLKL(mp);
218 	if (len < sizeof (t_uscalar_t)) {
219 		cmn_err(CE_NOTE, "!dl_op: received runt DLPI message");
220 		freemsg(mp);
221 		return (EBADMSG);
222 	}
223 
224 	dlp = (union DL_primitives *)mp->b_rptr;
225 	ackprim = dlp->dl_primitive;
226 
227 	if (ackprim == expprim) {
228 		if (len < minlen)
229 			goto runt;
230 
231 		if (ackprim == DL_OK_ACK) {
232 			if (dlp->ok_ack.dl_correct_primitive != reqprim) {
233 				ackreqprim = dlp->ok_ack.dl_correct_primitive;
234 				goto mixup;
235 			}
236 		}
237 		*mpp = mp;
238 		return (0);
239 	}
240 
241 	if (ackprim == DL_ERROR_ACK) {
242 		if (len < DL_ERROR_ACK_SIZE)
243 			goto runt;
244 
245 		if (dlp->error_ack.dl_error_primitive != reqprim) {
246 			ackreqprim = dlp->error_ack.dl_error_primitive;
247 			goto mixup;
248 		}
249 
250 		/*
251 		 * Return a special error code (ENOTSUP) indicating that the
252 		 * caller has returned DL_ERROR_ACK.  Callers that want more
253 		 * details an pass a non-NULL dleap.
254 		 */
255 		if (dleap != NULL)
256 			*dleap = dlp->error_ack;
257 
258 		freemsg(mp);
259 		return (ENOTSUP);
260 	}
261 
262 	cmn_err(CE_NOTE, "!dl_op: expected %s but received %s",
263 	    dl_primstr(expprim), dl_primstr(ackprim));
264 	freemsg(mp);
265 	return (EBADMSG);
266 runt:
267 	cmn_err(CE_NOTE, "!dl_op: received runt %s", dl_primstr(ackprim));
268 	freemsg(mp);
269 	return (EBADMSG);
270 mixup:
271 	cmn_err(CE_NOTE, "!dl_op: received %s for %s instead of %s",
272 	    dl_primstr(ackprim), dl_primstr(ackreqprim), dl_primstr(reqprim));
273 	freemsg(mp);
274 	return (EBADMSG);
275 }
276 
277 /*
278  * Send a DL_ATTACH_REQ for `ppa' over `lh' and wait for the response.
279  *
280  * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the
281  * caller can get the contents by passing a non-NULL `dleap').
282  */
283 int
284 dl_attach(ldi_handle_t lh, int ppa, dl_error_ack_t *dleap)
285 {
286 	mblk_t	*mp;
287 	int	err;
288 
289 	mp = mexchange(NULL, NULL, DL_ATTACH_REQ_SIZE, M_PROTO, DL_ATTACH_REQ);
290 	if (mp == NULL)
291 		return (ENOMEM);
292 
293 	((dl_attach_req_t *)mp->b_rptr)->dl_ppa = ppa;
294 
295 	err = dl_op(lh, &mp, DL_OK_ACK, DL_OK_ACK_SIZE, dleap, NULL);
296 	if (err == 0)
297 		freemsg(mp);
298 	return (err);
299 }
300 
301 /*
302  * Send a DL_BIND_REQ for `sap' over `lh' and wait for the response.
303  *
304  * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the
305  * caller can get the contents by passing a non-NULL `dleap').
306  */
307 int
308 dl_bind(ldi_handle_t lh, uint_t sap, dl_error_ack_t *dleap)
309 {
310 	dl_bind_req_t	*dlbrp;
311 	dl_bind_ack_t	*dlbap;
312 	mblk_t 		*mp;
313 	int		err;
314 
315 	mp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
316 	if (mp == NULL)
317 		return (ENOMEM);
318 
319 	dlbrp = (dl_bind_req_t *)mp->b_rptr;
320 	dlbrp->dl_sap = sap;
321 	dlbrp->dl_conn_mgmt = 0;
322 	dlbrp->dl_max_conind = 0;
323 	dlbrp->dl_xidtest_flg = 0;
324 	dlbrp->dl_service_mode = DL_CLDLS;
325 
326 	err = dl_op(lh, &mp, DL_BIND_ACK, DL_BIND_ACK_SIZE, dleap, NULL);
327 	if (err == 0) {
328 		dlbap = (dl_bind_ack_t *)mp->b_rptr;
329 		if (dlbap->dl_sap != sap) {
330 			cmn_err(CE_NOTE, "!dl_bind: DL_BIND_ACK: bad sap %u",
331 			    dlbap->dl_sap);
332 			err = EPROTO;
333 		}
334 		freemsg(mp);
335 	}
336 	return (err);
337 }
338 
339 /*
340  * Send a DL_PHYS_ADDR_REQ over `lh' and wait for the response.  The caller
341  * must set `*physlenp' to the size of `physaddr' (both of which must be
342  * non-NULL); upon success they will be updated to contain the actual physical
343  * address and length.
344  *
345  * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the
346  * caller can get the contents by passing a non-NULL `dleap').
347  */
348 int
349 dl_phys_addr(ldi_handle_t lh, uchar_t *physaddr, size_t *physlenp,
350     dl_error_ack_t *dleap)
351 {
352 	dl_phys_addr_ack_t *dlpap;
353 	mblk_t		*mp;
354 	int		err;
355 	t_uscalar_t	paddrlen, paddroff;
356 	timestruc_t	tv;
357 
358 	mp = mexchange(NULL, NULL, DL_PHYS_ADDR_REQ_SIZE, M_PROTO,
359 	    DL_PHYS_ADDR_REQ);
360 	if (mp == NULL)
361 		return (ENOMEM);
362 
363 	((dl_phys_addr_req_t *)mp->b_rptr)->dl_addr_type = DL_CURR_PHYS_ADDR;
364 
365 	/*
366 	 * In case some provider doesn't implement or NAK the
367 	 * request, just wait for 15 seconds.
368 	 */
369 	tv.tv_sec = 15;
370 	tv.tv_nsec = 0;
371 
372 	err = dl_op(lh, &mp, DL_PHYS_ADDR_ACK, DL_PHYS_ADDR_ACK_SIZE, dleap,
373 	    &tv);
374 	if (err == 0) {
375 		dlpap = (dl_phys_addr_ack_t *)mp->b_rptr;
376 		paddrlen = dlpap->dl_addr_length;
377 		paddroff = dlpap->dl_addr_offset;
378 		if (paddroff == 0 || paddrlen == 0 || paddrlen > *physlenp ||
379 		    !MBLKIN(mp, paddroff, paddrlen)) {
380 			cmn_err(CE_NOTE, "!dl_phys_addr: DL_PHYS_ADDR_ACK: "
381 			    "bad length/offset %d/%d", paddrlen, paddroff);
382 			err = EBADMSG;
383 		} else {
384 			bcopy(mp->b_rptr + paddroff, physaddr, paddrlen);
385 			*physlenp = paddrlen;
386 		}
387 		freemsg(mp);
388 	}
389 	return (err);
390 }
391 
392 /*
393  * Send a DL_INFO_REQ over `lh' and wait for the response.  The caller must
394  * pass a non-NULL `dliap', which upon success will contain the dl_info_ack_t
395  * from the provider.  The caller may optionally get the provider's physical
396  * address by passing a non-NULL `physaddr' and setting `*physlenp' to its
397  * size; upon success they will be updated to contain the actual physical
398  * address and its length.
399  *
400  * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the
401  * caller can get the contents by passing a non-NULL `dleap').
402  */
403 int
404 dl_info(ldi_handle_t lh, dl_info_ack_t *dliap, uchar_t *physaddr,
405     size_t *physlenp, dl_error_ack_t *dleap)
406 {
407 	mblk_t	*mp;
408 	int	err;
409 	int	addrlen, addroff;
410 
411 	mp = mexchange(NULL, NULL, DL_INFO_REQ_SIZE, M_PCPROTO, DL_INFO_REQ);
412 	if (mp == NULL)
413 		return (ENOMEM);
414 
415 	err = dl_op(lh, &mp, DL_INFO_ACK, DL_INFO_ACK_SIZE, dleap, NULL);
416 	if (err != 0)
417 		return (err);
418 
419 	*dliap = *(dl_info_ack_t *)mp->b_rptr;
420 	if (physaddr != NULL) {
421 		addrlen = dliap->dl_addr_length - ABS(dliap->dl_sap_length);
422 		addroff = dliap->dl_addr_offset;
423 		if (addroff == 0 || addrlen <= 0 || addrlen > *physlenp ||
424 		    !MBLKIN(mp, addroff, dliap->dl_addr_length)) {
425 			cmn_err(CE_NOTE, "!dl_info: DL_INFO_ACK: "
426 			    "bad length/offset %d/%d", addrlen, addroff);
427 			freemsg(mp);
428 			return (EBADMSG);
429 		}
430 
431 		if (dliap->dl_sap_length > 0)
432 			addroff += dliap->dl_sap_length;
433 		bcopy(mp->b_rptr + addroff, physaddr, addrlen);
434 		*physlenp = addrlen;
435 	}
436 	freemsg(mp);
437 	return (err);
438 }
439 
440 /*
441  * Send a DL_NOTIFY_REQ over `lh' and wait for the response.  The caller
442  * should set `notesp' to the set of notifications they wish to enable;
443  * upon success it will contain the notifications enabled by the provider.
444  *
445  * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the
446  * caller can get the contents by passing a non-NULL `dleap').
447  */
448 int
449 dl_notify(ldi_handle_t lh, uint32_t *notesp, dl_error_ack_t *dleap)
450 {
451 	mblk_t	*mp;
452 	int	err;
453 
454 	mp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ);
455 	if (mp == NULL)
456 		return (ENOMEM);
457 
458 	((dl_notify_req_t *)mp->b_rptr)->dl_notifications = *notesp;
459 
460 	err = dl_op(lh, &mp, DL_NOTIFY_ACK, DL_NOTIFY_ACK_SIZE, dleap, NULL);
461 	if (err == 0) {
462 		*notesp = ((dl_notify_ack_t *)mp->b_rptr)->dl_notifications;
463 		freemsg(mp);
464 	}
465 	return (err);
466 }
467 
468 const char *
469 dl_primstr(t_uscalar_t prim)
470 {
471 	switch (prim) {
472 	case DL_INFO_REQ:		return ("DL_INFO_REQ");
473 	case DL_INFO_ACK:		return ("DL_INFO_ACK");
474 	case DL_ATTACH_REQ:		return ("DL_ATTACH_REQ");
475 	case DL_DETACH_REQ:		return ("DL_DETACH_REQ");
476 	case DL_BIND_REQ:		return ("DL_BIND_REQ");
477 	case DL_BIND_ACK:		return ("DL_BIND_ACK");
478 	case DL_UNBIND_REQ:		return ("DL_UNBIND_REQ");
479 	case DL_OK_ACK:			return ("DL_OK_ACK");
480 	case DL_ERROR_ACK:		return ("DL_ERROR_ACK");
481 	case DL_ENABMULTI_REQ:		return ("DL_ENABMULTI_REQ");
482 	case DL_DISABMULTI_REQ:		return ("DL_DISABMULTI_REQ");
483 	case DL_PROMISCON_REQ:		return ("DL_PROMISCON_REQ");
484 	case DL_PROMISCOFF_REQ:		return ("DL_PROMISCOFF_REQ");
485 	case DL_UNITDATA_REQ:		return ("DL_UNITDATA_REQ");
486 	case DL_UNITDATA_IND:		return ("DL_UNITDATA_IND");
487 	case DL_UDERROR_IND:		return ("DL_UDERROR_IND");
488 	case DL_PHYS_ADDR_REQ:		return ("DL_PHYS_ADDR_REQ");
489 	case DL_PHYS_ADDR_ACK:		return ("DL_PHYS_ADDR_ACK");
490 	case DL_SET_PHYS_ADDR_REQ:	return ("DL_SET_PHYS_ADDR_REQ");
491 	case DL_NOTIFY_REQ:		return ("DL_NOTIFY_REQ");
492 	case DL_NOTIFY_ACK:		return ("DL_NOTIFY_ACK");
493 	case DL_NOTIFY_IND:		return ("DL_NOTIFY_IND");
494 	case DL_NOTIFY_CONF:		return ("DL_NOTIFY_CONF");
495 	case DL_CAPABILITY_REQ:		return ("DL_CAPABILITY_REQ");
496 	case DL_CAPABILITY_ACK:		return ("DL_CAPABILITY_ACK");
497 	case DL_CONTROL_REQ:		return ("DL_CONTROL_REQ");
498 	case DL_CONTROL_ACK:		return ("DL_CONTROL_ACK");
499 	case DL_PASSIVE_REQ:		return ("DL_PASSIVE_REQ");
500 	case DL_INTR_MODE_REQ:		return ("DL_INTR_MODE_REQ");
501 	case DL_UDQOS_REQ:		return ("DL_UDQOS_REQ");
502 	default:			return ("<unknown primitive>");
503 	}
504 }
505 
506 const char *
507 dl_errstr(t_uscalar_t err)
508 {
509 	switch (err) {
510 	case DL_ACCESS:			return ("DL_ACCESS");
511 	case DL_BADADDR:		return ("DL_BADADDR");
512 	case DL_BADCORR:		return ("DL_BADCORR");
513 	case DL_BADDATA:		return ("DL_BADDATA");
514 	case DL_BADPPA:			return ("DL_BADPPA");
515 	case DL_BADPRIM:		return ("DL_BADPRIM");
516 	case DL_BADQOSPARAM:		return ("DL_BADQOSPARAM");
517 	case DL_BADQOSTYPE:		return ("DL_BADQOSTYPE");
518 	case DL_BADSAP:			return ("DL_BADSAP");
519 	case DL_BADTOKEN:		return ("DL_BADTOKEN");
520 	case DL_BOUND:			return ("DL_BOUND");
521 	case DL_INITFAILED:		return ("DL_INITFAILED");
522 	case DL_NOADDR:			return ("DL_NOADDR");
523 	case DL_NOTINIT:		return ("DL_NOTINIT");
524 	case DL_OUTSTATE:		return ("DL_OUTSTATE");
525 	case DL_SYSERR:			return ("DL_SYSERR");
526 	case DL_UNSUPPORTED:		return ("DL_UNSUPPORTED");
527 	case DL_UNDELIVERABLE:		return ("DL_UNDELIVERABLE");
528 	case DL_NOTSUPPORTED:		return ("DL_NOTSUPPORTED ");
529 	case DL_TOOMANY:		return ("DL_TOOMANY");
530 	case DL_NOTENAB:		return ("DL_NOTENAB");
531 	case DL_BUSY:			return ("DL_BUSY");
532 	case DL_NOAUTO:			return ("DL_NOAUTO");
533 	case DL_NOXIDAUTO:		return ("DL_NOXIDAUTO");
534 	case DL_NOTESTAUTO:		return ("DL_NOTESTAUTO");
535 	case DL_XIDAUTO:		return ("DL_XIDAUTO");
536 	case DL_TESTAUTO:		return ("DL_TESTAUTO");
537 	case DL_PENDING:		return ("DL_PENDING");
538 	default:			return ("<unknown error>");
539 	}
540 }
541 
542 const char *
543 dl_mactypestr(t_uscalar_t mactype)
544 {
545 	switch (mactype) {
546 	case DL_CSMACD:		return ("CSMA/CD");
547 	case DL_TPB:		return ("Token Bus");
548 	case DL_TPR:		return ("Token Ring");
549 	case DL_METRO:		return ("Metro Net");
550 	case DL_ETHER:		return ("Ethernet");
551 	case DL_HDLC:		return ("HDLC");
552 	case DL_CHAR:		return ("Sync Character");
553 	case DL_CTCA:		return ("CTCA");
554 	case DL_FDDI:		return ("FDDI");
555 	case DL_FRAME:		return ("Frame Relay (LAPF)");
556 	case DL_MPFRAME:	return ("MP Frame Relay");
557 	case DL_ASYNC:		return ("Async Character");
558 	case DL_IPX25:		return ("X.25 (Classic IP)");
559 	case DL_LOOP:		return ("Software Loopback");
560 	case DL_FC:		return ("Fiber Channel");
561 	case DL_ATM:		return ("ATM");
562 	case DL_IPATM:		return ("ATM (Classic IP)");
563 	case DL_X25:		return ("X.25 (LAPB)");
564 	case DL_ISDN:		return ("ISDN");
565 	case DL_HIPPI:		return ("HIPPI");
566 	case DL_100VG:		return ("100BaseVG Ethernet");
567 	case DL_100VGTPR:	return ("100BaseVG Token Ring");
568 	case DL_ETH_CSMA:	return ("Ethernet/IEEE 802.3");
569 	case DL_100BT:		return ("100BaseT");
570 	case DL_IB:		return ("Infiniband");
571 	case DL_IPV4:		return ("IPv4 Tunnel");
572 	case DL_IPV6:		return ("IPv6 Tunnel");
573 	case DL_WIFI:		return ("IEEE 802.11");
574 	case DL_IPNET:		return ("IPNET");
575 	default:		return ("<unknown mactype>");
576 	}
577 }
578