125428Speter/* $NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $ */ 250477Speter/* $FreeBSD: stable/11/sys/net/if_media.c 331643 2018-03-27 18:52:27Z dim $ */ 325428Speter 4139823Simp/*- 525428Speter * Copyright (c) 1997 625428Speter * Jonathan Stone and Jason R. Thorpe. All rights reserved. 725428Speter * 825428Speter * This software is derived from information provided by Matt Thomas. 925428Speter * 1025428Speter * Redistribution and use in source and binary forms, with or without 1125428Speter * modification, are permitted provided that the following conditions 1225428Speter * are met: 1325428Speter * 1. Redistributions of source code must retain the above copyright 1425428Speter * notice, this list of conditions and the following disclaimer. 1525428Speter * 2. Redistributions in binary form must reproduce the above copyright 1625428Speter * notice, this list of conditions and the following disclaimer in the 1725428Speter * documentation and/or other materials provided with the distribution. 1825428Speter * 3. All advertising materials mentioning features or use of this software 1925428Speter * must display the following acknowledgement: 2025428Speter * This product includes software developed by Jonathan Stone 2125428Speter * and Jason R. Thorpe for the NetBSD Project. 2225428Speter * 4. The names of the authors may not be used to endorse or promote products 2325428Speter * derived from this software without specific prior written permission. 2425428Speter * 2525428Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 2625428Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2725428Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2825428Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2925428Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 3025428Speter * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 3125428Speter * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 3225428Speter * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 3325428Speter * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3425428Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3525428Speter * SUCH DAMAGE. 3625428Speter */ 3725428Speter 3825428Speter/* 3925428Speter * BSD/OS-compatible network interface media selection. 4025428Speter * 4125428Speter * Where it is safe to do so, this code strays slightly from the BSD/OS 4225428Speter * design. Software which uses the API (device drivers, basically) 4325428Speter * shouldn't notice any difference. 4425428Speter * 4525428Speter * Many thanks to Matt Thomas for providing the information necessary 4625428Speter * to implement this interface. 4725428Speter */ 4825428Speter 49281824Sglebius#include "opt_ifmedia.h" 50281824Sglebius 5125428Speter#include <sys/param.h> 5225428Speter#include <sys/systm.h> 5325428Speter#include <sys/socket.h> 5425431Speter#include <sys/sockio.h> 5525428Speter#include <sys/malloc.h> 56153723Ssam#include <sys/module.h> 57153723Ssam#include <sys/sysctl.h> 5825428Speter 5925428Speter#include <net/if.h> 6025428Speter#include <net/if_media.h> 6125428Speter 6225428Speter/* 6325428Speter * Compile-time options: 6425428Speter * IFMEDIA_DEBUG: 6525428Speter * turn on implementation-level debug printfs. 6625428Speter * Useful for debugging newly-ported drivers. 6725428Speter */ 6825428Speter 6992725Salfredstatic struct ifmedia_entry *ifmedia_match(struct ifmedia *ifm, 7092725Salfred int flags, int mask); 7125428Speter 7225428Speter#ifdef IFMEDIA_DEBUG 73281236Serj#include <net/if_var.h> 7425428Speterint ifmedia_debug = 0; 75153723SsamSYSCTL_INT(_debug, OID_AUTO, ifmedia, CTLFLAG_RW, &ifmedia_debug, 76153723Ssam 0, "if_media debugging msgs"); 7792725Salfredstatic void ifmedia_printword(int); 7825428Speter#endif 7925428Speter 8025428Speter/* 8125428Speter * Initialize if_media struct for a specific interface instance. 8225428Speter */ 8325428Spetervoid 8425428Speterifmedia_init(ifm, dontcare_mask, change_callback, status_callback) 8525428Speter struct ifmedia *ifm; 8625428Speter int dontcare_mask; 8725428Speter ifm_change_cb_t change_callback; 8825428Speter ifm_stat_cb_t status_callback; 8925428Speter{ 9025428Speter 9125428Speter LIST_INIT(&ifm->ifm_list); 9225428Speter ifm->ifm_cur = NULL; 9325428Speter ifm->ifm_media = 0; 9425428Speter ifm->ifm_mask = dontcare_mask; /* IF don't-care bits */ 9525428Speter ifm->ifm_change = change_callback; 9625428Speter ifm->ifm_status = status_callback; 9725428Speter} 9825428Speter 9945720Spetervoid 10045720Speterifmedia_removeall(ifm) 10145720Speter struct ifmedia *ifm; 10245720Speter{ 10345720Speter struct ifmedia_entry *entry; 10445720Speter 10545720Speter for (entry = LIST_FIRST(&ifm->ifm_list); entry; 10645720Speter entry = LIST_FIRST(&ifm->ifm_list)) { 10745720Speter LIST_REMOVE(entry, ifm_list); 10845720Speter free(entry, M_IFADDR); 10945720Speter } 110313388Srstone ifm->ifm_cur = NULL; 11145720Speter} 11245720Speter 11325428Speter/* 11425428Speter * Add a media configuration to the list of supported media 11525428Speter * for a specific interface instance. 11625428Speter */ 11725428Spetervoid 11825428Speterifmedia_add(ifm, mword, data, aux) 11925428Speter struct ifmedia *ifm; 12025428Speter int mword; 12125428Speter int data; 12225428Speter void *aux; 12325428Speter{ 124331643Sdim struct ifmedia_entry *entry; 12525428Speter 12625428Speter#ifdef IFMEDIA_DEBUG 12725428Speter if (ifmedia_debug) { 12825428Speter if (ifm == NULL) { 12925428Speter printf("ifmedia_add: null ifm\n"); 13025428Speter return; 13125428Speter } 13225428Speter printf("Adding entry for "); 13325428Speter ifmedia_printword(mword); 13425428Speter } 13525428Speter#endif 13625428Speter 13725428Speter entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT); 13825428Speter if (entry == NULL) 13925428Speter panic("ifmedia_add: can't malloc entry"); 14025428Speter 14125428Speter entry->ifm_media = mword; 14225428Speter entry->ifm_data = data; 14325428Speter entry->ifm_aux = aux; 14425428Speter 14525428Speter LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list); 14625428Speter} 14725428Speter 14825428Speter/* 14925428Speter * Add an array of media configurations to the list of 15025428Speter * supported media for a specific interface instance. 15125428Speter */ 15225428Spetervoid 15325428Speterifmedia_list_add(ifm, lp, count) 15425428Speter struct ifmedia *ifm; 15525428Speter struct ifmedia_entry *lp; 15625428Speter int count; 15725428Speter{ 15825428Speter int i; 15925428Speter 16025428Speter for (i = 0; i < count; i++) 16125428Speter ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data, 16225428Speter lp[i].ifm_aux); 16325428Speter} 16425428Speter 16525428Speter/* 16625428Speter * Set the default active media. 16725428Speter * 16825428Speter * Called by device-specific code which is assumed to have already 16925428Speter * selected the default media in hardware. We do _not_ call the 17025428Speter * media-change callback. 17125428Speter */ 17225428Spetervoid 17325428Speterifmedia_set(ifm, target) 17425428Speter struct ifmedia *ifm; 17525428Speter int target; 17625428Speter 17725428Speter{ 17825428Speter struct ifmedia_entry *match; 17925428Speter 18025428Speter match = ifmedia_match(ifm, target, ifm->ifm_mask); 18125428Speter 18225428Speter if (match == NULL) { 18325428Speter printf("ifmedia_set: no match for 0x%x/0x%x\n", 18425428Speter target, ~ifm->ifm_mask); 18525428Speter panic("ifmedia_set"); 18625428Speter } 18725428Speter ifm->ifm_cur = match; 18825428Speter 18925428Speter#ifdef IFMEDIA_DEBUG 19025428Speter if (ifmedia_debug) { 19125428Speter printf("ifmedia_set: target "); 19225428Speter ifmedia_printword(target); 19325428Speter printf("ifmedia_set: setting to "); 19425428Speter ifmedia_printword(ifm->ifm_cur->ifm_media); 19525428Speter } 19625428Speter#endif 19725428Speter} 19825428Speter 19925428Speter/* 200281236Serj * Given a media word, return one suitable for an application 201281236Serj * using the original encoding. 202281236Serj */ 203281236Serjstatic int 204281236Serjcompat_media(int media) 205281236Serj{ 206281236Serj 207281236Serj if (IFM_TYPE(media) == IFM_ETHER && IFM_SUBTYPE(media) > IFM_OTHER) { 208281236Serj media &= ~(IFM_ETH_XTYPE|IFM_TMASK); 209281236Serj media |= IFM_OTHER; 210281236Serj } 211281236Serj return (media); 212281236Serj} 213281236Serj 214281236Serj/* 21525428Speter * Device-independent media ioctl support function. 21625428Speter */ 21725428Speterint 21825428Speterifmedia_ioctl(ifp, ifr, ifm, cmd) 21925428Speter struct ifnet *ifp; 22025428Speter struct ifreq *ifr; 22125428Speter struct ifmedia *ifm; 22225428Speter u_long cmd; 22325428Speter{ 22425428Speter struct ifmedia_entry *match; 22525428Speter struct ifmediareq *ifmr = (struct ifmediareq *) ifr; 226279592Sglebius int error = 0; 22725428Speter 22825428Speter if (ifp == NULL || ifr == NULL || ifm == NULL) 22925428Speter return(EINVAL); 23025428Speter 23125428Speter switch (cmd) { 23225428Speter 23325428Speter /* 23425428Speter * Set the current media. 23525428Speter */ 23625428Speter case SIOCSIFMEDIA: 23725428Speter { 23825428Speter struct ifmedia_entry *oldentry; 23925428Speter int oldmedia; 24025428Speter int newmedia = ifr->ifr_media; 24125428Speter 24225428Speter match = ifmedia_match(ifm, newmedia, ifm->ifm_mask); 24325428Speter if (match == NULL) { 24425428Speter#ifdef IFMEDIA_DEBUG 24525428Speter if (ifmedia_debug) { 24625428Speter printf( 24725428Speter "ifmedia_ioctl: no media found for 0x%x\n", 24825428Speter newmedia); 24925428Speter } 25025428Speter#endif 25125428Speter return (ENXIO); 25225428Speter } 25325428Speter 25425428Speter /* 25525428Speter * If no change, we're done. 25625428Speter * XXX Automedia may invole software intervention. 257218909Sbrucec * Keep going in case the connected media changed. 25825428Speter * Similarly, if best match changed (kernel debugger?). 25925428Speter */ 26025428Speter if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) && 26125428Speter (newmedia == ifm->ifm_media) && 26225428Speter (match == ifm->ifm_cur)) 26325428Speter return 0; 26425428Speter 26525428Speter /* 26625428Speter * We found a match, now make the driver switch to it. 26725428Speter * Make sure to preserve our old media type in case the 26825428Speter * driver can't switch. 26925428Speter */ 27025428Speter#ifdef IFMEDIA_DEBUG 27125428Speter if (ifmedia_debug) { 272121816Sbrooks printf("ifmedia_ioctl: switching %s to ", 273121816Sbrooks ifp->if_xname); 27425428Speter ifmedia_printword(match->ifm_media); 27525428Speter } 27625428Speter#endif 27725428Speter oldentry = ifm->ifm_cur; 27825428Speter oldmedia = ifm->ifm_media; 27925428Speter ifm->ifm_cur = match; 28025428Speter ifm->ifm_media = newmedia; 28125428Speter error = (*ifm->ifm_change)(ifp); 28225428Speter if (error) { 28325428Speter ifm->ifm_cur = oldentry; 28425428Speter ifm->ifm_media = oldmedia; 28525428Speter } 28625428Speter break; 28725428Speter } 28825428Speter 28925428Speter /* 29025428Speter * Get list of available media and current media on interface. 29125428Speter */ 29225428Speter case SIOCGIFMEDIA: 293281236Serj case SIOCGIFXMEDIA: 29425428Speter { 29525428Speter struct ifmedia_entry *ep; 296279592Sglebius int i; 29725428Speter 298279592Sglebius if (ifmr->ifm_count < 0) 299279592Sglebius return (EINVAL); 30025428Speter 301281236Serj if (cmd == SIOCGIFMEDIA) { 302281236Serj ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 303281236Serj compat_media(ifm->ifm_cur->ifm_media) : IFM_NONE; 304281236Serj } else { 305281236Serj ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ? 306281236Serj ifm->ifm_cur->ifm_media : IFM_NONE; 307281236Serj } 30825428Speter ifmr->ifm_mask = ifm->ifm_mask; 30925428Speter ifmr->ifm_status = 0; 31025428Speter (*ifm->ifm_status)(ifp, ifmr); 31125428Speter 31273078Salfred /* 31373078Salfred * If there are more interfaces on the list, count 31473078Salfred * them. This allows the caller to set ifmr->ifm_count 31573078Salfred * to 0 on the first call to know how much space to 31673079Salfred * allocate. 31773078Salfred */ 318279592Sglebius i = 0; 31973078Salfred LIST_FOREACH(ep, &ifm->ifm_list, ifm_list) 320279592Sglebius if (i++ < ifmr->ifm_count) { 321279592Sglebius error = copyout(&ep->ifm_media, 322279592Sglebius ifmr->ifm_ulist + i - 1, sizeof(int)); 323279592Sglebius if (error) 324279592Sglebius break; 325279592Sglebius } 326279592Sglebius if (error == 0 && i > ifmr->ifm_count) 327279592Sglebius error = ifmr->ifm_count ? E2BIG : 0; 328279592Sglebius ifmr->ifm_count = i; 32925428Speter break; 33025428Speter } 33125428Speter 33225428Speter default: 33325428Speter return (EINVAL); 33425428Speter } 33525428Speter 33625428Speter return (error); 33725428Speter} 33825428Speter 33925428Speter/* 34025428Speter * Find media entry matching a given ifm word. 34125428Speter * 34225428Speter */ 34333181Seivindstatic struct ifmedia_entry * 34425428Speterifmedia_match(ifm, target, mask) 34525428Speter struct ifmedia *ifm; 34625428Speter int target; 34725428Speter int mask; 34825428Speter{ 34925428Speter struct ifmedia_entry *match, *next; 35025428Speter 35125428Speter match = NULL; 35225428Speter mask = ~mask; 35325428Speter 35472012Sphk LIST_FOREACH(next, &ifm->ifm_list, ifm_list) { 35525428Speter if ((next->ifm_media & mask) == (target & mask)) { 35625428Speter#if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC) 35725428Speter if (match) { 35825428Speter printf("ifmedia_match: multiple match for " 35925428Speter "0x%x/0x%x\n", target, mask); 36025428Speter } 36125428Speter#endif 36225428Speter match = next; 36325428Speter } 36425428Speter } 36525428Speter 36625428Speter return match; 36725428Speter} 36825428Speter 369155669Sglebius/* 370155669Sglebius * Compute the interface `baudrate' from the media, for the interface 371155669Sglebius * metrics (used by routing daemons). 372155669Sglebius */ 373155669Sglebiusstatic const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] = 374155669Sglebius IFM_BAUDRATE_DESCRIPTIONS; 375155669Sglebius 376155669Sglebiusuint64_t 377155669Sglebiusifmedia_baudrate(int mword) 378155669Sglebius{ 379155669Sglebius int i; 380155669Sglebius 381155669Sglebius for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) { 382281236Serj if (IFM_TYPE_MATCH(mword, ifmedia_baudrate_descriptions[i].ifmb_word)) 383155669Sglebius return (ifmedia_baudrate_descriptions[i].ifmb_baudrate); 384155669Sglebius } 385155669Sglebius 386155669Sglebius /* Not known. */ 387155669Sglebius return (0); 388155669Sglebius} 389155669Sglebius 39025428Speter#ifdef IFMEDIA_DEBUG 39125428Speterstruct ifmedia_description ifm_type_descriptions[] = 39225428Speter IFM_TYPE_DESCRIPTIONS; 39325428Speter 39425428Speterstruct ifmedia_description ifm_subtype_ethernet_descriptions[] = 39525428Speter IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; 39625428Speter 39725428Speterstruct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = 39825428Speter IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; 39925428Speter 40025428Speterstruct ifmedia_description ifm_subtype_tokenring_descriptions[] = 40125428Speter IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; 40225428Speter 40325428Speterstruct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = 40425428Speter IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; 40525428Speter 40625428Speterstruct ifmedia_description ifm_subtype_fddi_descriptions[] = 40725428Speter IFM_SUBTYPE_FDDI_DESCRIPTIONS; 40825428Speter 40925428Speterstruct ifmedia_description ifm_subtype_fddi_option_descriptions[] = 41025428Speter IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; 41125428Speter 41277217Sphkstruct ifmedia_description ifm_subtype_ieee80211_descriptions[] = 41377217Sphk IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; 41477217Sphk 41577217Sphkstruct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = 41677217Sphk IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; 41777217Sphk 418114163Ssamstruct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = 419114163Ssam IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; 420114163Ssam 421114232Shartistruct ifmedia_description ifm_subtype_atm_descriptions[] = 422114232Sharti IFM_SUBTYPE_ATM_DESCRIPTIONS; 423114232Sharti 424114232Shartistruct ifmedia_description ifm_subtype_atm_option_descriptions[] = 425114232Sharti IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; 426114232Sharti 42725428Speterstruct ifmedia_description ifm_subtype_shared_descriptions[] = 42825428Speter IFM_SUBTYPE_SHARED_DESCRIPTIONS; 42925428Speter 43025428Speterstruct ifmedia_description ifm_shared_option_descriptions[] = 43125428Speter IFM_SHARED_OPTION_DESCRIPTIONS; 43225428Speter 43325428Speterstruct ifmedia_type_to_subtype { 43425428Speter struct ifmedia_description *subtypes; 43525428Speter struct ifmedia_description *options; 436114163Ssam struct ifmedia_description *modes; 43725428Speter}; 43825428Speter 43925428Speter/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ 44025428Speterstruct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { 44125428Speter { 44225428Speter &ifm_subtype_ethernet_descriptions[0], 443114163Ssam &ifm_subtype_ethernet_option_descriptions[0], 444114163Ssam NULL, 44525428Speter }, 44625428Speter { 44725428Speter &ifm_subtype_tokenring_descriptions[0], 448114163Ssam &ifm_subtype_tokenring_option_descriptions[0], 449114163Ssam NULL, 45025428Speter }, 45125428Speter { 45225428Speter &ifm_subtype_fddi_descriptions[0], 453114163Ssam &ifm_subtype_fddi_option_descriptions[0], 454114163Ssam NULL, 45525428Speter }, 45677217Sphk { 45777217Sphk &ifm_subtype_ieee80211_descriptions[0], 458114163Ssam &ifm_subtype_ieee80211_option_descriptions[0], 459114163Ssam &ifm_subtype_ieee80211_mode_descriptions[0] 46077217Sphk }, 461114232Sharti { 462114232Sharti &ifm_subtype_atm_descriptions[0], 463114232Sharti &ifm_subtype_atm_option_descriptions[0], 464114232Sharti NULL, 465114232Sharti }, 46625428Speter}; 46725428Speter 46825428Speter/* 46925428Speter * print a media word. 47025428Speter */ 47125428Speterstatic void 47225428Speterifmedia_printword(ifmw) 47325428Speter int ifmw; 47425428Speter{ 47525428Speter struct ifmedia_description *desc; 47625428Speter struct ifmedia_type_to_subtype *ttos; 47725428Speter int seen_option = 0; 47825428Speter 47925428Speter /* Find the top-level interface type. */ 48025428Speter for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; 48125428Speter desc->ifmt_string != NULL; desc++, ttos++) 48225428Speter if (IFM_TYPE(ifmw) == desc->ifmt_word) 48325428Speter break; 48425428Speter if (desc->ifmt_string == NULL) { 48525428Speter printf("<unknown type>\n"); 48625428Speter return; 48725428Speter } 488281236Serj printf("%s", desc->ifmt_string); 48925428Speter 490114163Ssam /* Any mode. */ 491114163Ssam for (desc = ttos->modes; desc && desc->ifmt_string != NULL; desc++) 492114163Ssam if (IFM_MODE(ifmw) == desc->ifmt_word) { 493114163Ssam if (desc->ifmt_string != NULL) 494114163Ssam printf(" mode %s", desc->ifmt_string); 495114163Ssam break; 496114163Ssam } 497114163Ssam 49825428Speter /* 49925428Speter * Check for the shared subtype descriptions first, then the 50025428Speter * type-specific ones. 50125428Speter */ 50225428Speter for (desc = ifm_subtype_shared_descriptions; 50325428Speter desc->ifmt_string != NULL; desc++) 50425428Speter if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) 50525428Speter goto got_subtype; 50625428Speter 50725428Speter for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++) 50825428Speter if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) 50925428Speter break; 51025428Speter if (desc->ifmt_string == NULL) { 51125428Speter printf(" <unknown subtype>\n"); 51225428Speter return; 51325428Speter } 51425428Speter 51525428Speter got_subtype: 51625428Speter printf(" %s", desc->ifmt_string); 51725428Speter 51825428Speter /* 51925428Speter * Look for shared options. 52025428Speter */ 52125428Speter for (desc = ifm_shared_option_descriptions; 52225428Speter desc->ifmt_string != NULL; desc++) { 52325428Speter if (ifmw & desc->ifmt_word) { 52425428Speter if (seen_option == 0) 52525428Speter printf(" <"); 52625428Speter printf("%s%s", seen_option++ ? "," : "", 52725428Speter desc->ifmt_string); 52825428Speter } 52925428Speter } 53025428Speter 53125428Speter /* 53225428Speter * Look for subtype-specific options. 53325428Speter */ 53425428Speter for (desc = ttos->options; desc->ifmt_string != NULL; desc++) { 53525428Speter if (ifmw & desc->ifmt_word) { 53625428Speter if (seen_option == 0) 53725428Speter printf(" <"); 53825428Speter printf("%s%s", seen_option++ ? "," : "", 53925428Speter desc->ifmt_string); 54025428Speter } 54125428Speter } 54225428Speter printf("%s\n", seen_option ? ">" : ""); 54325428Speter} 54425428Speter#endif /* IFMEDIA_DEBUG */ 545