xref: /freebsd/sbin/ifconfig/ifmedia.c (revision 8fa113e5fc65fe6abc757f0089f477a87ee4d185)
1 /*	$NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $	*/
2 /* $FreeBSD$ */
3 
4 /*
5  * Copyright (c) 1997 Jason R. Thorpe.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed for the NetBSD Project
19  *	by Jason R. Thorpe.
20  * 4. The name of the author may not be used to endorse or promote products
21  *    derived from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 /*
37  * Copyright (c) 1983, 1993
38  *	The Regents of the University of California.  All rights reserved.
39  *
40  * Redistribution and use in source and binary forms, with or without
41  * modification, are permitted provided that the following conditions
42  * are met:
43  * 1. Redistributions of source code must retain the above copyright
44  *    notice, this list of conditions and the following disclaimer.
45  * 2. Redistributions in binary form must reproduce the above copyright
46  *    notice, this list of conditions and the following disclaimer in the
47  *    documentation and/or other materials provided with the distribution.
48  * 3. All advertising materials mentioning features or use of this software
49  *    must display the following acknowledgement:
50  *	This product includes software developed by the University of
51  *	California, Berkeley and its contributors.
52  * 4. Neither the name of the University nor the names of its contributors
53  *    may be used to endorse or promote products derived from this software
54  *    without specific prior written permission.
55  *
56  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
57  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
58  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
59  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
60  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
61  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
62  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
63  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
64  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
65  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
66  * SUCH DAMAGE.
67  */
68 
69 #include <sys/param.h>
70 #include <sys/ioctl.h>
71 #include <sys/socket.h>
72 #include <sys/sysctl.h>
73 #include <sys/time.h>
74 
75 #include <net/if.h>
76 #include <net/if_dl.h>
77 #include <net/if_types.h>
78 #include <net/if_media.h>
79 #include <net/route.h>
80 
81 #include <ctype.h>
82 #include <err.h>
83 #include <errno.h>
84 #include <fcntl.h>
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88 #include <unistd.h>
89 
90 #include "ifconfig.h"
91 
92 static void	domediaopt __P((const char *, int, int));
93 static int	get_media_subtype __P((int, const char *));
94 static int	get_media_options __P((int, const char *));
95 static int	lookup_media_word __P((struct ifmedia_description *, const char *));
96 static void	print_media_word __P((int, int));
97 static void	print_media_word_ifconfig __P((int));
98 
99 static struct ifmedia_description *get_toptype_desc __P((int));
100 static struct ifmedia_type_to_subtype *get_toptype_ttos __P((int));
101 static struct ifmedia_description *get_subtype_desc __P((int,
102     struct ifmedia_type_to_subtype *ttos));
103 
104 void
105 media_status(s, info)
106 	int s;
107 	struct rt_addrinfo *info __unused;
108 {
109 	struct ifmediareq ifmr;
110 	int *media_list, i;
111 
112 	(void) memset(&ifmr, 0, sizeof(ifmr));
113 	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
114 
115 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
116 		/*
117 		 * Interface doesn't support SIOC{G,S}IFMEDIA.
118 		 */
119 		return;
120 	}
121 
122 	if (ifmr.ifm_count == 0) {
123 		warnx("%s: no media types?", name);
124 		return;
125 	}
126 
127 	media_list = (int *)malloc(ifmr.ifm_count * sizeof(int));
128 	if (media_list == NULL)
129 		err(1, "malloc");
130 	ifmr.ifm_ulist = media_list;
131 
132 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
133 		err(1, "SIOCGIFMEDIA");
134 
135 	printf("\tmedia: ");
136 	print_media_word(ifmr.ifm_current, 1);
137 	if (ifmr.ifm_active != ifmr.ifm_current) {
138 		putchar(' ');
139 		putchar('(');
140 		print_media_word(ifmr.ifm_active, 0);
141 		putchar(')');
142 	}
143 
144 	putchar('\n');
145 
146 	if (ifmr.ifm_status & IFM_AVALID) {
147 		printf("\tstatus: ");
148 		switch (IFM_TYPE(ifmr.ifm_active)) {
149 		case IFM_ETHER:
150 			if (ifmr.ifm_status & IFM_ACTIVE)
151 				printf("active");
152 			else
153 				printf("no carrier");
154 			break;
155 
156 		case IFM_FDDI:
157 		case IFM_TOKEN:
158 			if (ifmr.ifm_status & IFM_ACTIVE)
159 				printf("inserted");
160 			else
161 				printf("no ring");
162 			break;
163 		case IFM_IEEE80211:
164 			/* XXX: Different value for adhoc? */
165 			if (ifmr.ifm_status & IFM_ACTIVE)
166 				printf("associated");
167 			else
168 				printf("no carrier");
169 			break;
170 		}
171 		putchar('\n');
172 	}
173 
174 	if (ifmr.ifm_count > 0 && supmedia) {
175 		printf("\tsupported media:\n");
176 		for (i = 0; i < ifmr.ifm_count; i++) {
177 			printf("\t\t");
178 			print_media_word_ifconfig(media_list[i]);
179 			putchar('\n');
180 		}
181 	}
182 
183 	free(media_list);
184 }
185 
186 void
187 setmedia(val, d, s, afp)
188 	const char *val;
189 	int d;
190 	int s;
191 	const struct afswtch *afp;
192 {
193 	struct ifmediareq ifmr;
194 	int first_type, subtype;
195 
196 	(void) memset(&ifmr, 0, sizeof(ifmr));
197 	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
198 
199 	ifmr.ifm_count = 1;
200 	ifmr.ifm_ulist = &first_type;
201 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
202 		/*
203 		 * If we get E2BIG, the kernel is telling us
204 		 * that there are more, so we can ignore it.
205 		 */
206 		if (errno != E2BIG)
207 			err(1, "SIOCGIFMEDIA");
208 	}
209 
210 	if (ifmr.ifm_count == 0)
211 		errx(1, "%s: no media types?", name);
212 
213 	/*
214 	 * We are primarily concerned with the top-level type.
215 	 * However, "current" may be only IFM_NONE, so we just look
216 	 * for the top-level type in the first "supported type"
217 	 * entry.
218 	 *
219 	 * (I'm assuming that all supported media types for a given
220 	 * interface will be the same top-level type..)
221 	 */
222 	subtype = get_media_subtype(IFM_TYPE(first_type), val);
223 
224 	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
225 	ifr.ifr_media = (ifmr.ifm_current & ~(IFM_NMASK|IFM_TMASK)) |
226 	    IFM_TYPE(first_type) | subtype;
227 
228 	if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
229 		err(1, "SIOCSIFMEDIA");
230 }
231 
232 void
233 setmediaopt(val, d, s, afp)
234 	const char *val;
235 	int d;
236 	int s;
237 	const struct afswtch *afp;
238 {
239 
240 	domediaopt(val, 0, s);
241 }
242 
243 void
244 unsetmediaopt(val, d, s, afp)
245 	const char *val;
246 	int d;
247 	int s;
248 	const struct afswtch *afp;
249 {
250 
251 	domediaopt(val, 1, s);
252 }
253 
254 static void
255 domediaopt(val, clear, s)
256 	const char *val;
257 	int clear;
258 	int s;
259 {
260 	struct ifmediareq ifmr;
261 	int *mwords, options;
262 
263 	(void) memset(&ifmr, 0, sizeof(ifmr));
264 	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
265 
266 	/*
267 	 * We must go through the motions of reading all
268 	 * supported media because we need to know both
269 	 * the current media type and the top-level type.
270 	 */
271 
272 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
273 		err(1, "SIOCGIFMEDIA");
274 
275 	if (ifmr.ifm_count == 0)
276 		errx(1, "%s: no media types?", name);
277 
278 	mwords = (int *)malloc(ifmr.ifm_count * sizeof(int));
279 	if (mwords == NULL)
280 		err(1, "malloc");
281 
282 	ifmr.ifm_ulist = mwords;
283 	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
284 		err(1, "SIOCGIFMEDIA");
285 
286 	options = get_media_options(IFM_TYPE(mwords[0]), val);
287 
288 	free(mwords);
289 
290 	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
291 	ifr.ifr_media = ifmr.ifm_current;
292 	if (clear)
293 		ifr.ifr_media &= ~options;
294 	else
295 		ifr.ifr_media |= options;
296 
297 	if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
298 		err(1, "SIOCSIFMEDIA");
299 }
300 
301 /**********************************************************************
302  * A good chunk of this is duplicated from sys/net/ifmedia.c
303  **********************************************************************/
304 
305 static struct ifmedia_description ifm_type_descriptions[] =
306     IFM_TYPE_DESCRIPTIONS;
307 
308 static struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
309     IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
310 
311 static struct ifmedia_description ifm_subtype_ethernet_aliases[] =
312     IFM_SUBTYPE_ETHERNET_ALIASES;
313 
314 static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
315     IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
316 
317 static struct ifmedia_description ifm_subtype_tokenring_descriptions[] =
318     IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
319 
320 static struct ifmedia_description ifm_subtype_tokenring_aliases[] =
321     IFM_SUBTYPE_TOKENRING_ALIASES;
322 
323 static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
324     IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
325 
326 static struct ifmedia_description ifm_subtype_fddi_descriptions[] =
327     IFM_SUBTYPE_FDDI_DESCRIPTIONS;
328 
329 static struct ifmedia_description ifm_subtype_fddi_aliases[] =
330     IFM_SUBTYPE_FDDI_ALIASES;
331 
332 static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
333     IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
334 
335 static struct ifmedia_description ifm_subtype_ieee80211_descriptions[] =
336     IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
337 
338 static struct ifmedia_description ifm_subtype_ieee80211_aliases[] =
339     IFM_SUBTYPE_IEEE80211_ALIASES;
340 
341 static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] =
342     IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
343 
344 static struct ifmedia_description ifm_subtype_shared_descriptions[] =
345     IFM_SUBTYPE_SHARED_DESCRIPTIONS;
346 
347 static struct ifmedia_description ifm_subtype_shared_aliases[] =
348     IFM_SUBTYPE_SHARED_ALIASES;
349 
350 static struct ifmedia_description ifm_shared_option_descriptions[] =
351     IFM_SHARED_OPTION_DESCRIPTIONS;
352 
353 struct ifmedia_type_to_subtype {
354 	struct {
355 		struct ifmedia_description *desc;
356 		int alias;
357 	} subtypes[5];
358 	struct {
359 		struct ifmedia_description *desc;
360 		int alias;
361 	} options[3];
362 };
363 
364 /* must be in the same order as IFM_TYPE_DESCRIPTIONS */
365 static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
366 	{
367 		{
368 			{ &ifm_subtype_shared_descriptions[0], 0 },
369 			{ &ifm_subtype_shared_aliases[0], 1 },
370 			{ &ifm_subtype_ethernet_descriptions[0], 0 },
371 			{ &ifm_subtype_ethernet_aliases[0], 1 },
372 			{ NULL, 0 },
373 		},
374 		{
375 			{ &ifm_shared_option_descriptions[0], 0 },
376 			{ &ifm_subtype_ethernet_option_descriptions[0], 0 },
377 			{ NULL, 0 },
378 		},
379 	},
380 	{
381 		{
382 			{ &ifm_subtype_shared_descriptions[0], 0 },
383 			{ &ifm_subtype_shared_aliases[0], 1 },
384 			{ &ifm_subtype_tokenring_descriptions[0], 0 },
385 			{ &ifm_subtype_tokenring_aliases[0], 1 },
386 			{ NULL, 0 },
387 		},
388 		{
389 			{ &ifm_shared_option_descriptions[0], 0 },
390 			{ &ifm_subtype_tokenring_option_descriptions[0], 0 },
391 			{ NULL, 0 },
392 		},
393 	},
394 	{
395 		{
396 			{ &ifm_subtype_shared_descriptions[0], 0 },
397 			{ &ifm_subtype_shared_aliases[0], 1 },
398 			{ &ifm_subtype_fddi_descriptions[0], 0 },
399 			{ &ifm_subtype_fddi_aliases[0], 1 },
400 			{ NULL, 0 },
401 		},
402 		{
403 			{ &ifm_shared_option_descriptions[0], 0 },
404 			{ &ifm_subtype_fddi_option_descriptions[0], 0 },
405 			{ NULL, 0 },
406 		},
407 	},
408 	{
409 		{
410 			{ &ifm_subtype_shared_descriptions[0], 0 },
411 			{ &ifm_subtype_shared_aliases[0], 1 },
412 			{ &ifm_subtype_ieee80211_descriptions[0], 0 },
413 			{ &ifm_subtype_ieee80211_aliases[0], 1 },
414 			{ NULL, 0 },
415 		},
416 		{
417 			{ &ifm_shared_option_descriptions[0], 0 },
418 			{ &ifm_subtype_ieee80211_option_descriptions[0], 0 },
419 			{ NULL, 0 },
420 		},
421 	},
422 };
423 
424 static int
425 get_media_subtype(type, val)
426 	int type;
427 	const char *val;
428 {
429 	struct ifmedia_description *desc;
430 	struct ifmedia_type_to_subtype *ttos;
431 	int rval, i;
432 
433 	/* Find the top-level interface type. */
434 	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
435 	    desc->ifmt_string != NULL; desc++, ttos++)
436 		if (type == desc->ifmt_word)
437 			break;
438 	if (desc->ifmt_string == NULL)
439 		errx(1, "unknown media type 0x%x", type);
440 
441 	for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
442 		rval = lookup_media_word(ttos->subtypes[i].desc, val);
443 		if (rval != -1)
444 			return (rval);
445 	}
446 	errx(1, "unknown media subtype: %s", val);
447 	/* NOTREACHED */
448 }
449 
450 static int
451 get_media_options(type, val)
452 	int type;
453 	const char *val;
454 {
455 	struct ifmedia_description *desc;
456 	struct ifmedia_type_to_subtype *ttos;
457 	char *optlist, *optptr;
458 	int option = 0, i, rval = 0;
459 
460 	/* We muck with the string, so copy it. */
461 	optlist = strdup(val);
462 	if (optlist == NULL)
463 		err(1, "strdup");
464 
465 	/* Find the top-level interface type. */
466 	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
467 	    desc->ifmt_string != NULL; desc++, ttos++)
468 		if (type == desc->ifmt_word)
469 			break;
470 	if (desc->ifmt_string == NULL)
471 		errx(1, "unknown media type 0x%x", type);
472 
473 	/*
474 	 * Look up the options in the user-provided comma-separated
475 	 * list.
476 	 */
477 	optptr = optlist;
478 	for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) {
479 		for (i = 0; ttos->options[i].desc != NULL; i++) {
480 			option = lookup_media_word(ttos->options[i].desc, optptr);
481 			if (option != -1)
482 				break;
483 		}
484 		if (option == 0)
485 			errx(1, "unknown option: %s", optptr);
486 		rval |= option;
487 	}
488 
489 	free(optlist);
490 	return (rval);
491 }
492 
493 static int
494 lookup_media_word(desc, val)
495 	struct ifmedia_description *desc;
496 	const char *val;
497 {
498 
499 	for (; desc->ifmt_string != NULL; desc++)
500 		if (strcasecmp(desc->ifmt_string, val) == 0)
501 			return (desc->ifmt_word);
502 
503 	return (-1);
504 }
505 
506 static struct ifmedia_description *get_toptype_desc(ifmw)
507 	int ifmw;
508 {
509 	struct ifmedia_description *desc;
510 
511 	for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++)
512 		if (IFM_TYPE(ifmw) == desc->ifmt_word)
513 			break;
514 
515 	return desc;
516 }
517 
518 static struct ifmedia_type_to_subtype *get_toptype_ttos(ifmw)
519 	int ifmw;
520 {
521 	struct ifmedia_description *desc;
522 	struct ifmedia_type_to_subtype *ttos;
523 
524 	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
525 	    desc->ifmt_string != NULL; desc++, ttos++)
526 		if (IFM_TYPE(ifmw) == desc->ifmt_word)
527 			break;
528 
529 	return ttos;
530 }
531 
532 static struct ifmedia_description *get_subtype_desc(ifmw, ttos)
533 	int ifmw;
534 	struct ifmedia_type_to_subtype *ttos;
535 {
536 	int i;
537 	struct ifmedia_description *desc;
538 
539 	for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
540 		if (ttos->subtypes[i].alias)
541 			continue;
542 		for (desc = ttos->subtypes[i].desc;
543 		    desc->ifmt_string != NULL; desc++) {
544 			if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
545 				return desc;
546 		}
547 	}
548 
549 	return NULL;
550 }
551 
552 static void
553 print_media_word(ifmw, print_toptype)
554 	int ifmw;
555 	int print_toptype;
556 {
557 	struct ifmedia_description *desc;
558 	struct ifmedia_type_to_subtype *ttos;
559 	int seen_option = 0, i;
560 
561 	/* Find the top-level interface type. */
562 	desc = get_toptype_desc(ifmw);
563 	ttos = get_toptype_ttos(ifmw);
564 	if (desc->ifmt_string == NULL) {
565 		printf("<unknown type>");
566 		return;
567 	} else if (print_toptype) {
568 		printf("%s", desc->ifmt_string);
569 	}
570 
571 	/*
572 	 * Don't print the top-level type; it's not like we can
573 	 * change it, or anything.
574 	 */
575 
576 	/* Find subtype. */
577 	desc = get_subtype_desc(ifmw, ttos);
578 	if (desc != NULL)
579 		goto got_subtype;
580 
581 	/* Falling to here means unknown subtype. */
582 	printf("<unknown subtype>");
583 	return;
584 
585  got_subtype:
586 	if (print_toptype)
587 		putchar(' ');
588 
589 	printf("%s", desc->ifmt_string);
590 
591 	/* Find options. */
592 	for (i = 0; ttos->options[i].desc != NULL; i++) {
593 		if (ttos->options[i].alias)
594 			continue;
595 		for (desc = ttos->options[i].desc;
596 		    desc->ifmt_string != NULL; desc++) {
597 			if (ifmw & desc->ifmt_word) {
598 				if (seen_option == 0)
599 					printf(" <");
600 				printf("%s%s", seen_option++ ? "," : "",
601 				    desc->ifmt_string);
602 			}
603 		}
604 	}
605 	printf("%s", seen_option ? ">" : "");
606 }
607 
608 static void
609 print_media_word_ifconfig(ifmw)
610 	int ifmw;
611 {
612 	struct ifmedia_description *desc;
613 	struct ifmedia_type_to_subtype *ttos;
614 	int i;
615 
616 	/* Find the top-level interface type. */
617 	desc = get_toptype_desc(ifmw);
618 	ttos = get_toptype_ttos(ifmw);
619 	if (desc->ifmt_string == NULL) {
620 		printf("<unknown type>");
621 		return;
622 	}
623 
624 	/*
625 	 * Don't print the top-level type; it's not like we can
626 	 * change it, or anything.
627 	 */
628 
629 	/* Find subtype. */
630 	desc = get_subtype_desc(ifmw, ttos);
631 	if (desc != NULL)
632 		goto got_subtype;
633 
634 	/* Falling to here means unknown subtype. */
635 	printf("<unknown subtype>");
636 	return;
637 
638  got_subtype:
639 	printf("media %s", desc->ifmt_string);
640 
641 	/* Find options. */
642 	for (i = 0; ttos->options[i].desc != NULL; i++) {
643 		if (ttos->options[i].alias)
644 			continue;
645 		for (desc = ttos->options[i].desc;
646 		    desc->ifmt_string != NULL; desc++) {
647 			if (ifmw & desc->ifmt_word) {
648 				printf(" mediaopt %s", desc->ifmt_string);
649 			}
650 		}
651 	}
652 }
653 
654 /**********************************************************************
655  * ...until here.
656  **********************************************************************/
657