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