xref: /netbsd-src/sys/net/if_media.c (revision 6ffff4f242a1d82be7e156c3b9b52202b2135ad8)
1*6ffff4f2Sthorpej /*	$NetBSD: if_media.c,v 1.54 2022/09/03 02:47:59 thorpej Exp $	*/
2b04ded72Sthorpej 
3b04ded72Sthorpej /*-
47a9a30c5Sthorpej  * Copyright (c) 1998, 2020 The NetBSD Foundation, Inc.
5b04ded72Sthorpej  * All rights reserved.
6b04ded72Sthorpej  *
7b04ded72Sthorpej  * This code is derived from software contributed to The NetBSD Foundation
8b04ded72Sthorpej  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9b04ded72Sthorpej  * NASA Ames Research Center.
10b04ded72Sthorpej  *
11b04ded72Sthorpej  * Redistribution and use in source and binary forms, with or without
12b04ded72Sthorpej  * modification, are permitted provided that the following conditions
13b04ded72Sthorpej  * are met:
14b04ded72Sthorpej  * 1. Redistributions of source code must retain the above copyright
15b04ded72Sthorpej  *    notice, this list of conditions and the following disclaimer.
16b04ded72Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
17b04ded72Sthorpej  *    notice, this list of conditions and the following disclaimer in the
18b04ded72Sthorpej  *    documentation and/or other materials provided with the distribution.
19b04ded72Sthorpej  *
20b04ded72Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21b04ded72Sthorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22b04ded72Sthorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23b04ded72Sthorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24b04ded72Sthorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25b04ded72Sthorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26b04ded72Sthorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27b04ded72Sthorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28b04ded72Sthorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29b04ded72Sthorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30b04ded72Sthorpej  * POSSIBILITY OF SUCH DAMAGE.
31b04ded72Sthorpej  */
321b1c7ef8Sthorpej 
331b1c7ef8Sthorpej /*
341b1c7ef8Sthorpej  * Copyright (c) 1997
351b1c7ef8Sthorpej  *	Jonathan Stone and Jason R. Thorpe.  All rights reserved.
361b1c7ef8Sthorpej  *
371b1c7ef8Sthorpej  * This software is derived from information provided by Matt Thomas.
381b1c7ef8Sthorpej  *
391b1c7ef8Sthorpej  * Redistribution and use in source and binary forms, with or without
401b1c7ef8Sthorpej  * modification, are permitted provided that the following conditions
411b1c7ef8Sthorpej  * are met:
421b1c7ef8Sthorpej  * 1. Redistributions of source code must retain the above copyright
431b1c7ef8Sthorpej  *    notice, this list of conditions and the following disclaimer.
441b1c7ef8Sthorpej  * 2. Redistributions in binary form must reproduce the above copyright
451b1c7ef8Sthorpej  *    notice, this list of conditions and the following disclaimer in the
461b1c7ef8Sthorpej  *    documentation and/or other materials provided with the distribution.
471b1c7ef8Sthorpej  * 3. All advertising materials mentioning features or use of this software
481b1c7ef8Sthorpej  *    must display the following acknowledgement:
491b1c7ef8Sthorpej  *      This product includes software developed by Jonathan Stone
501b1c7ef8Sthorpej  *	and Jason R. Thorpe for the NetBSD Project.
511b1c7ef8Sthorpej  * 4. The names of the authors may not be used to endorse or promote products
521b1c7ef8Sthorpej  *    derived from this software without specific prior written permission.
531b1c7ef8Sthorpej  *
541b1c7ef8Sthorpej  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
551b1c7ef8Sthorpej  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
561b1c7ef8Sthorpej  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
571b1c7ef8Sthorpej  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
581b1c7ef8Sthorpej  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
591b1c7ef8Sthorpej  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
601b1c7ef8Sthorpej  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
611b1c7ef8Sthorpej  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
621b1c7ef8Sthorpej  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
631b1c7ef8Sthorpej  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
641b1c7ef8Sthorpej  * SUCH DAMAGE.
651b1c7ef8Sthorpej  */
661b1c7ef8Sthorpej 
671b1c7ef8Sthorpej /*
681b1c7ef8Sthorpej  * BSD/OS-compatible network interface media selection.
691b1c7ef8Sthorpej  *
701b1c7ef8Sthorpej  * Where it is safe to do so, this code strays slightly from the BSD/OS
711b1c7ef8Sthorpej  * design.  Software which uses the API (device drivers, basically)
721b1c7ef8Sthorpej  * shouldn't notice any difference.
731b1c7ef8Sthorpej  *
741b1c7ef8Sthorpej  * Many thanks to Matt Thomas for providing the information necessary
751b1c7ef8Sthorpej  * to implement this interface.
761b1c7ef8Sthorpej  */
771b1c7ef8Sthorpej 
7834d65a34Slukem #include <sys/cdefs.h>
79*6ffff4f2Sthorpej __KERNEL_RCSID(0, "$NetBSD: if_media.c,v 1.54 2022/09/03 02:47:59 thorpej Exp $");
807a9a30c5Sthorpej 
817a9a30c5Sthorpej #define	__IFMEDIA_PRIVATE
8234d65a34Slukem 
831b1c7ef8Sthorpej #include <sys/param.h>
841b1c7ef8Sthorpej #include <sys/systm.h>
851b1c7ef8Sthorpej #include <sys/errno.h>
861b1c7ef8Sthorpej #include <sys/ioctl.h>
871b1c7ef8Sthorpej #include <sys/socket.h>
884385a5d1Sthorpej #include <sys/kmem.h>
891b1c7ef8Sthorpej 
901b1c7ef8Sthorpej #include <net/if.h>
911b1c7ef8Sthorpej #include <net/if_media.h>
921b1c7ef8Sthorpej 
9353cc1211Smsaitoh static void	ifmedia_status(struct ifmedia *, struct ifnet *,
9453cc1211Smsaitoh 		    struct ifmediareq *);
957a9a30c5Sthorpej static struct ifmedia_entry *
967a9a30c5Sthorpej 		ifmedia_match_locked(struct ifmedia *, u_int, u_int);
97ae07208dSmlelstv 
981b1c7ef8Sthorpej /*
991b1c7ef8Sthorpej  * Compile-time options:
1001b1c7ef8Sthorpej  * IFMEDIA_DEBUG:
1011b9aeaf0Smsaitoh  *	Turn on implementation-level debug printfs.
1021b1c7ef8Sthorpej  * 	Useful for debugging newly-ported drivers.
1031b1c7ef8Sthorpej  */
1041b1c7ef8Sthorpej 
1051b1c7ef8Sthorpej #ifdef IFMEDIA_DEBUG
1061b1c7ef8Sthorpej int	ifmedia_debug = 0;
10715921b5fSthorpej static	void ifmedia_printword(int);
1081b1c7ef8Sthorpej #endif
1091b1c7ef8Sthorpej 
1101b1c7ef8Sthorpej /*
1117a9a30c5Sthorpej  * We need to implement a recursive mutex to handle the un-converted
1127a9a30c5Sthorpej  * driver case.  For a fully MP-safe driver, the media lock will be
1137a9a30c5Sthorpej  * held before calling any of the entry points that require it.  However,
1147a9a30c5Sthorpej  * this is not necessarily the case for a driver that hasn't yet been
1157a9a30c5Sthorpej  * converted, and the entry point calls may be nested (for example
1167a9a30c5Sthorpej  * mii_ifmedia_change -> ether_mediachange -> mii_mediachg).  Luckily,
1177a9a30c5Sthorpej  * the nesting won't be very deep, and 4 nested holds should be plenty.
1187a9a30c5Sthorpej  */
1197a9a30c5Sthorpej #define	IFM_L_OWNLOCK		0x01
1207a9a30c5Sthorpej #define	IFM_L_COUNT_MASK	0x3UL
1217a9a30c5Sthorpej #define	IFM_L_CPU_MASK		~(IFM_L_COUNT_MASK)
1227a9a30c5Sthorpej 
1237a9a30c5Sthorpej void
ifmedia_lock_for_legacy(struct ifmedia * ifm)1247a9a30c5Sthorpej ifmedia_lock_for_legacy(struct ifmedia *ifm)
1257a9a30c5Sthorpej {
1267a9a30c5Sthorpej 	uintptr_t cnt = IFM_L_OWNLOCK;
1277a9a30c5Sthorpej 	uintptr_t ci;
1287a9a30c5Sthorpej 
1297a9a30c5Sthorpej 	if (mutex_tryenter(ifm->ifm_lock)) {
1307a9a30c5Sthorpej 		goto gotit;
1317a9a30c5Sthorpej 	}
1327a9a30c5Sthorpej 
1337a9a30c5Sthorpej 	kpreempt_disable();
1347a9a30c5Sthorpej 	ci = (uintptr_t)curcpu();
1357a9a30c5Sthorpej 	if ((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci) {
1367a9a30c5Sthorpej 		cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK;
1377a9a30c5Sthorpej 		KASSERT(cnt < IFM_L_COUNT_MASK);
1387a9a30c5Sthorpej 		cnt++;
1397a9a30c5Sthorpej 		kpreempt_enable();
1407a9a30c5Sthorpej 		goto gotit;
1417a9a30c5Sthorpej 	}
1427a9a30c5Sthorpej 	kpreempt_enable();
1437a9a30c5Sthorpej 
1447a9a30c5Sthorpej 	mutex_enter(ifm->ifm_lock);
1457a9a30c5Sthorpej  gotit:
1467a9a30c5Sthorpej 	KASSERT(kpreempt_disabled());
1477a9a30c5Sthorpej 	ci = (uintptr_t)curcpu();
1487a9a30c5Sthorpej 	KASSERT((ci & IFM_L_CPU_MASK) == ci);
1497a9a30c5Sthorpej 	ifm->ifm_legacy = ci | cnt;
1507a9a30c5Sthorpej }
1517a9a30c5Sthorpej 
1527a9a30c5Sthorpej void
ifmedia_unlock_for_legacy(struct ifmedia * ifm)1537a9a30c5Sthorpej ifmedia_unlock_for_legacy(struct ifmedia *ifm)
1547a9a30c5Sthorpej {
1557a9a30c5Sthorpej 	uintptr_t cnt;
1567a9a30c5Sthorpej 	uintptr_t ci = (uintptr_t)curcpu();
1577a9a30c5Sthorpej 
1587a9a30c5Sthorpej 	KASSERT(kpreempt_disabled());
1597a9a30c5Sthorpej 	KASSERT((ifm->ifm_legacy & IFM_L_CPU_MASK) == ci);
1607a9a30c5Sthorpej 	cnt = ifm->ifm_legacy & IFM_L_COUNT_MASK;
1617a9a30c5Sthorpej 	KASSERT(cnt != 0);
1627a9a30c5Sthorpej 	if (cnt == IFM_L_OWNLOCK) {
1637a9a30c5Sthorpej 		ifm->ifm_legacy = IFM_L_OWNLOCK;
1647a9a30c5Sthorpej 		mutex_exit(ifm->ifm_lock);
1657a9a30c5Sthorpej 		return;
1667a9a30c5Sthorpej 	}
1677a9a30c5Sthorpej 	cnt--;
1687a9a30c5Sthorpej 	ifm->ifm_legacy = ci | cnt;
1697a9a30c5Sthorpej }
1707a9a30c5Sthorpej 
1717a9a30c5Sthorpej /*
1721b1c7ef8Sthorpej  * Initialize if_media struct for a specific interface instance.
1731b1c7ef8Sthorpej  */
1741b1c7ef8Sthorpej void
ifmedia_init_with_lock(struct ifmedia * ifm,int dontcare_mask,ifm_change_cb_t change_callback,ifm_stat_cb_t status_callback,kmutex_t * lock)1757a9a30c5Sthorpej ifmedia_init_with_lock(struct ifmedia *ifm, int dontcare_mask,
1767a9a30c5Sthorpej     ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback,
1777a9a30c5Sthorpej     kmutex_t *lock)
1781b1c7ef8Sthorpej {
1791b1c7ef8Sthorpej 
1807a9a30c5Sthorpej 	/*
1817a9a30c5Sthorpej 	 * XXX Would really like to assert:
1827a9a30c5Sthorpej 	 *
1837a9a30c5Sthorpej 	 *	!if_is_mpsafe(ifp) || ((if_is_mpsafe(ifp) && lock != NULL)
1847a9a30c5Sthorpej 	 *
185e6cce717Sandvar 	 * ...but we don't have access to the ifnet here.
1867a9a30c5Sthorpej 	 */
1877a9a30c5Sthorpej 
188e19c356eSthorpej 	TAILQ_INIT(&ifm->ifm_list);
1891b1c7ef8Sthorpej 	ifm->ifm_cur = NULL;
190072c3839Smsaitoh 	ifm->ifm_media = IFM_NONE;
1911b1c7ef8Sthorpej 	ifm->ifm_mask = dontcare_mask;		/* IF don't-care bits */
1921b1c7ef8Sthorpej 	ifm->ifm_change = change_callback;
1931b1c7ef8Sthorpej 	ifm->ifm_status = status_callback;
1947a9a30c5Sthorpej 	ifm->ifm_legacy = 0;
1957a9a30c5Sthorpej 
1967a9a30c5Sthorpej 	if (lock == NULL) {
1977a9a30c5Sthorpej 		/*
1987a9a30c5Sthorpej 		 * This is to support drivers that are not yet MP-safe
1997a9a30c5Sthorpej 		 * with regard to the ifmedia layer.  In these cases,
2007a9a30c5Sthorpej 		 * we supply the lock and we ensure it's taken upon entry
2017a9a30c5Sthorpej 		 * to various routines that expect it to be held.  When
2027a9a30c5Sthorpej 		 * we do this, we expect that the driver is in general a
2037a9a30c5Sthorpej 		 * non-MP-safe driver and has already gone to splnet().
2047a9a30c5Sthorpej 		 */
2057a9a30c5Sthorpej 		lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
2067a9a30c5Sthorpej 		ifm->ifm_legacy = IFM_L_OWNLOCK;
2077a9a30c5Sthorpej 	}
2087a9a30c5Sthorpej 	ifm->ifm_lock = lock;
2097a9a30c5Sthorpej }
2107a9a30c5Sthorpej 
2117a9a30c5Sthorpej void
ifmedia_init(struct ifmedia * ifm,int dontcare_mask,ifm_change_cb_t change_callback,ifm_stat_cb_t status_callback)2127a9a30c5Sthorpej ifmedia_init(struct ifmedia *ifm, int dontcare_mask,
2137a9a30c5Sthorpej     ifm_change_cb_t change_callback, ifm_stat_cb_t status_callback)
2147a9a30c5Sthorpej {
2157a9a30c5Sthorpej 	ifmedia_init_with_lock(ifm, dontcare_mask, change_callback,
2167a9a30c5Sthorpej 	    status_callback, NULL);
2171b1c7ef8Sthorpej }
2181b1c7ef8Sthorpej 
219a2249e70Sthorpej /*
220a2249e70Sthorpej  * Free resources associated with an ifmedia.
221a2249e70Sthorpej  */
222a2249e70Sthorpej void
ifmedia_fini(struct ifmedia * ifm)223a2249e70Sthorpej ifmedia_fini(struct ifmedia *ifm)
224a2249e70Sthorpej {
225a2249e70Sthorpej 
226a2249e70Sthorpej 	ifmedia_removeall(ifm);
2277a9a30c5Sthorpej 
2287a9a30c5Sthorpej 	if (ifm->ifm_legacy) {
2297a9a30c5Sthorpej 		KASSERT(ifm->ifm_legacy == IFM_L_OWNLOCK);
2307a9a30c5Sthorpej 		mutex_obj_free(ifm->ifm_lock);
2317a9a30c5Sthorpej 	}
2327a9a30c5Sthorpej 	ifm->ifm_legacy = 0;
2337a9a30c5Sthorpej 	ifm->ifm_lock = NULL;
234a2249e70Sthorpej }
235a2249e70Sthorpej 
23672fea72bSdyoung int
ifmedia_change(struct ifmedia * ifm,struct ifnet * ifp)23772fea72bSdyoung ifmedia_change(struct ifmedia *ifm, struct ifnet *ifp)
23872fea72bSdyoung {
2397a9a30c5Sthorpej 	int rv;
240ae07208dSmlelstv 
2417a9a30c5Sthorpej 	IFMEDIA_LOCK_FOR_LEGACY(ifm);
2427a9a30c5Sthorpej 	KASSERT(ifmedia_locked(ifm));
2437a9a30c5Sthorpej 	if (ifm->ifm_change)
2447a9a30c5Sthorpej 		rv = (*ifm->ifm_change)(ifp);
2457a9a30c5Sthorpej 	else
2467a9a30c5Sthorpej 		rv = -1;
2477a9a30c5Sthorpej 	IFMEDIA_UNLOCK_FOR_LEGACY(ifm);
2487a9a30c5Sthorpej 
2497a9a30c5Sthorpej 	return rv;
25072fea72bSdyoung }
25172fea72bSdyoung 
252ae07208dSmlelstv static void
ifmedia_status(struct ifmedia * ifm,struct ifnet * ifp,struct ifmediareq * ifmr)25322d7e3fcSmsaitoh ifmedia_status(struct ifmedia *ifm, struct ifnet *ifp, struct ifmediareq *ifmr)
254ae07208dSmlelstv {
255ae07208dSmlelstv 
2567a9a30c5Sthorpej 	KASSERT(ifmedia_locked(ifm));
257ae07208dSmlelstv 	if (ifm->ifm_status == NULL)
258ae07208dSmlelstv 		return;
259ae07208dSmlelstv 	(*ifm->ifm_status)(ifp, ifmr);
260ae07208dSmlelstv }
261ae07208dSmlelstv 
2621b1c7ef8Sthorpej /*
2631b1c7ef8Sthorpej  * Add a media configuration to the list of supported media
2641b1c7ef8Sthorpej  * for a specific interface instance.
2651b1c7ef8Sthorpej  */
2667a9a30c5Sthorpej static void
ifmedia_add_entry(struct ifmedia * ifm,int mword,int data,void * aux,struct ifmedia_entry * entry)2677a9a30c5Sthorpej ifmedia_add_entry(struct ifmedia *ifm, int mword, int data, void *aux,
2687a9a30c5Sthorpej     struct ifmedia_entry *entry)
2691b1c7ef8Sthorpej {
2701b1c7ef8Sthorpej 
2711b1c7ef8Sthorpej #ifdef IFMEDIA_DEBUG
2721b1c7ef8Sthorpej 	if (ifmedia_debug) {
2731b1c7ef8Sthorpej 		if (ifm == NULL) {
2741b1c7ef8Sthorpej 			printf("ifmedia_add: null ifm\n");
2751b1c7ef8Sthorpej 			return;
2761b1c7ef8Sthorpej 		}
2771b1c7ef8Sthorpej 		printf("Adding entry for ");
2781b1c7ef8Sthorpej 		ifmedia_printword(mword);
2791b1c7ef8Sthorpej 	}
2801b1c7ef8Sthorpej #endif
2811b1c7ef8Sthorpej 
2821b1c7ef8Sthorpej 	entry->ifm_media = mword;
2831b1c7ef8Sthorpej 	entry->ifm_data = data;
2841b1c7ef8Sthorpej 	entry->ifm_aux = aux;
285e19c356eSthorpej 	TAILQ_INSERT_TAIL(&ifm->ifm_list, entry, ifm_list);
2861b1c7ef8Sthorpej }
2871b1c7ef8Sthorpej 
2887a9a30c5Sthorpej void
ifmedia_add(struct ifmedia * ifm,int mword,int data,void * aux)2897a9a30c5Sthorpej ifmedia_add(struct ifmedia *ifm, int mword, int data, void *aux)
2907a9a30c5Sthorpej {
2917a9a30c5Sthorpej 	struct ifmedia_entry *entry;
2927a9a30c5Sthorpej 
2937a9a30c5Sthorpej 	entry = kmem_zalloc(sizeof(*entry), KM_SLEEP);
2947a9a30c5Sthorpej 	ifmedia_lock(ifm);
2957a9a30c5Sthorpej 	ifmedia_add_entry(ifm, mword, data, aux, entry);
2967a9a30c5Sthorpej 	ifmedia_unlock(ifm);
2977a9a30c5Sthorpej }
2987a9a30c5Sthorpej 
2991b1c7ef8Sthorpej /*
3001b1c7ef8Sthorpej  * Add an array of media configurations to the list of
3011b1c7ef8Sthorpej  * supported media for a specific interface instance.
3021b1c7ef8Sthorpej  */
3031b1c7ef8Sthorpej void
ifmedia_list_add(struct ifmedia * ifm,struct ifmedia_entry * lp,int count)30415921b5fSthorpej ifmedia_list_add(struct ifmedia *ifm, struct ifmedia_entry *lp, int count)
3051b1c7ef8Sthorpej {
3061b1c7ef8Sthorpej 	int i;
3071b1c7ef8Sthorpej 
3081b1c7ef8Sthorpej 	for (i = 0; i < count; i++)
3091b1c7ef8Sthorpej 		ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data,
3101b1c7ef8Sthorpej 		    lp[i].ifm_aux);
3111b1c7ef8Sthorpej }
3121b1c7ef8Sthorpej 
3131b1c7ef8Sthorpej /*
3141b1c7ef8Sthorpej  * Set the default active media.
3151b1c7ef8Sthorpej  *
3161b1c7ef8Sthorpej  * Called by device-specific code which is assumed to have already
3171b1c7ef8Sthorpej  * selected the default media in hardware.  We do _not_ call the
3181b1c7ef8Sthorpej  * media-change callback.
3191b1c7ef8Sthorpej  */
3201b1c7ef8Sthorpej void
ifmedia_set(struct ifmedia * ifm,int target)32115921b5fSthorpej ifmedia_set(struct ifmedia *ifm, int target)
3221b1c7ef8Sthorpej {
3237a9a30c5Sthorpej 	struct ifmedia_entry *match, *entry = NULL;
3241b1c7ef8Sthorpej 
3257a9a30c5Sthorpej 	ifmedia_lock(ifm);
3267a9a30c5Sthorpej 	match = ifmedia_match_locked(ifm, target, ifm->ifm_mask);
3271b1c7ef8Sthorpej 
328953bcec0Sbriggs 	/*
329953bcec0Sbriggs 	 * If we didn't find the requested media, then we try to fall
330953bcec0Sbriggs 	 * back to target-type (IFM_ETHER, e.g.) | IFM_NONE.  If that's
331953bcec0Sbriggs 	 * not on the list, then we add it and set the media to it.
332953bcec0Sbriggs 	 *
333953bcec0Sbriggs 	 * Since ifmedia_set is almost always called with IFM_AUTO or
334953bcec0Sbriggs 	 * with a known-good media, this really should only occur if we:
335953bcec0Sbriggs 	 *
336953bcec0Sbriggs 	 * a) didn't find any PHYs, or
337953bcec0Sbriggs 	 * b) didn't find an autoselect option on the PHY when the
338953bcec0Sbriggs 	 *    parent ethernet driver expected to.
339953bcec0Sbriggs 	 *
340953bcec0Sbriggs 	 * In either case, it makes sense to select no media.
341953bcec0Sbriggs 	 */
3421b1c7ef8Sthorpej 	if (match == NULL) {
3431b1c7ef8Sthorpej 		printf("ifmedia_set: no match for 0x%x/0x%x\n",
3441b1c7ef8Sthorpej 		    target, ~ifm->ifm_mask);
345953bcec0Sbriggs 		target = (target & IFM_NMASK) | IFM_NONE;
3467a9a30c5Sthorpej 		match = ifmedia_match_locked(ifm, target, ifm->ifm_mask);
347953bcec0Sbriggs 		if (match == NULL) {
3487a9a30c5Sthorpej 			ifmedia_unlock(ifm);
3497a9a30c5Sthorpej 			entry = kmem_zalloc(sizeof(*entry), KM_SLEEP);
3507a9a30c5Sthorpej 			ifmedia_lock(ifm);
3517a9a30c5Sthorpej 			match = ifmedia_match_locked(ifm, target,
3527a9a30c5Sthorpej 			    ifm->ifm_mask);
3537a9a30c5Sthorpej 			if (match == NULL) {
3547a9a30c5Sthorpej 				ifmedia_add_entry(ifm, target, 0, NULL, entry);
3557a9a30c5Sthorpej 				entry = NULL;
3567a9a30c5Sthorpej 			}
3577a9a30c5Sthorpej 			match = ifmedia_match_locked(ifm, target,
3587a9a30c5Sthorpej 			    ifm->ifm_mask);
359428e2171Smsaitoh 			if (match == NULL)
360953bcec0Sbriggs 				panic("ifmedia_set failed");
361953bcec0Sbriggs 		}
362953bcec0Sbriggs 	}
3631b1c7ef8Sthorpej 	ifm->ifm_cur = match;
3647a9a30c5Sthorpej 	ifmedia_unlock(ifm);
3657a9a30c5Sthorpej 
3667a9a30c5Sthorpej 	if (entry)
3677a9a30c5Sthorpej 		kmem_free(entry, sizeof(*entry));
3681b1c7ef8Sthorpej 
3691b1c7ef8Sthorpej #ifdef IFMEDIA_DEBUG
3701b1c7ef8Sthorpej 	if (ifmedia_debug) {
3711b1c7ef8Sthorpej 		printf("ifmedia_set: target ");
3721b1c7ef8Sthorpej 		ifmedia_printword(target);
3731b1c7ef8Sthorpej 		printf("ifmedia_set: setting to ");
3741b1c7ef8Sthorpej 		ifmedia_printword(ifm->ifm_cur->ifm_media);
3751b1c7ef8Sthorpej 	}
3761b1c7ef8Sthorpej #endif
3771b1c7ef8Sthorpej }
3781b1c7ef8Sthorpej 
3794385a5d1Sthorpej static int
ifmedia_getwords(struct ifmedia * const ifm,int * words,int maxwords)3804385a5d1Sthorpej ifmedia_getwords(struct ifmedia * const ifm, int *words, int maxwords)
3814385a5d1Sthorpej {
3824385a5d1Sthorpej 	struct ifmedia_entry *ep;
3834385a5d1Sthorpej 	int nwords = 0;
3844385a5d1Sthorpej 
3857a9a30c5Sthorpej 	KASSERT(ifmedia_locked(ifm));
3867a9a30c5Sthorpej 
3874385a5d1Sthorpej 	TAILQ_FOREACH(ep, &ifm->ifm_list, ifm_list) {
3884385a5d1Sthorpej 		if (words != NULL && nwords < maxwords) {
3894385a5d1Sthorpej 			words[nwords] = ep->ifm_media;
3904385a5d1Sthorpej 		}
3914385a5d1Sthorpej 		nwords++;
3924385a5d1Sthorpej 	}
3934385a5d1Sthorpej 
3944385a5d1Sthorpej 	return nwords;
3954385a5d1Sthorpej }
3964385a5d1Sthorpej 
3977a9a30c5Sthorpej #define	IFMEDIA_IOCTL_LOCK(ifm)						\
3987a9a30c5Sthorpej do {									\
3997a9a30c5Sthorpej 	if (ifmedia_islegacy(ifm))					\
4007a9a30c5Sthorpej 		ifmedia_lock_for_legacy(ifm);				\
4017a9a30c5Sthorpej 	else								\
4027a9a30c5Sthorpej 		ifmedia_lock(ifm);					\
4037a9a30c5Sthorpej } while (/*CONSTCOND*/0)
4047a9a30c5Sthorpej 
4057a9a30c5Sthorpej #define	IFMEDIA_IOCTL_UNLOCK(ifm)					\
4067a9a30c5Sthorpej do {									\
4077a9a30c5Sthorpej 	if (ifmedia_islegacy(ifm))					\
4087a9a30c5Sthorpej 		ifmedia_unlock_for_legacy(ifm);				\
4097a9a30c5Sthorpej 	else								\
4107a9a30c5Sthorpej 		ifmedia_unlock(ifm);					\
4117a9a30c5Sthorpej } while (/*CONSTCOND*/0)
4127a9a30c5Sthorpej 
4131b1c7ef8Sthorpej /*
4141b1c7ef8Sthorpej  * Device-independent media ioctl support function.
4151b1c7ef8Sthorpej  */
4167a9a30c5Sthorpej int
ifmedia_ioctl(struct ifnet * ifp,struct ifreq * ifr,struct ifmedia * ifm,u_long cmd)4177a9a30c5Sthorpej ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, struct ifmedia *ifm,
41815921b5fSthorpej     u_long cmd)
4191b1c7ef8Sthorpej {
4201b1c7ef8Sthorpej 	struct ifmedia_entry *match;
4211b1c7ef8Sthorpej 	struct ifmediareq *ifmr = (struct ifmediareq *)ifr;
4229a1168a5Schristos 	int error = 0;
4231b1c7ef8Sthorpej 
4241b1c7ef8Sthorpej 	if (ifp == NULL || ifr == NULL || ifm == NULL)
4251b9aeaf0Smsaitoh 		return EINVAL;
4261b1c7ef8Sthorpej 
4277a9a30c5Sthorpej 	KERNEL_LOCK_UNLESS_IFP_MPSAFE(ifp);
4287a9a30c5Sthorpej 
4291b1c7ef8Sthorpej 	switch (cmd) {
4301b9aeaf0Smsaitoh 	case SIOCSIFMEDIA:	/* Set the current media. */
4311b1c7ef8Sthorpej 	{
4321b1c7ef8Sthorpej 		struct ifmedia_entry *oldentry;
433f5c6a07cSthorpej 		u_int oldmedia;
434f5c6a07cSthorpej 		u_int newmedia = ifr->ifr_media;
4351b1c7ef8Sthorpej 
4367a9a30c5Sthorpej 		IFMEDIA_IOCTL_LOCK(ifm);
4377a9a30c5Sthorpej 
4387a9a30c5Sthorpej 		match = ifmedia_match_locked(ifm, newmedia, ifm->ifm_mask);
4391b1c7ef8Sthorpej 		if (match == NULL) {
4401b1c7ef8Sthorpej #ifdef IFMEDIA_DEBUG
4411b1c7ef8Sthorpej 			if (ifmedia_debug) {
442007b45a4Smsaitoh 				printf("ifmedia_ioctl: no media found for "
443007b45a4Smsaitoh 				    "0x%08x\n", newmedia);
4441b1c7ef8Sthorpej 			}
4451b1c7ef8Sthorpej #endif
4467a9a30c5Sthorpej 			IFMEDIA_IOCTL_UNLOCK(ifm);
4477a9a30c5Sthorpej 			error = EINVAL;
4487a9a30c5Sthorpej 			break;
4491b1c7ef8Sthorpej 		}
4501b1c7ef8Sthorpej 
4511b1c7ef8Sthorpej 		/*
4521b1c7ef8Sthorpej 		 * If no change, we're done.
45332f3bee5Sdrochner 		 * XXX Automedia may involve software intervention.
45495054da1Ssoren 		 *     Keep going in case the connected media changed.
4551b1c7ef8Sthorpej 		 *     Similarly, if best match changed (kernel debugger?).
4561b1c7ef8Sthorpej 		 */
4571b1c7ef8Sthorpej 		if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) &&
4587a9a30c5Sthorpej 		    (newmedia == ifm->ifm_media) && (match == ifm->ifm_cur)) {
4597a9a30c5Sthorpej 			IFMEDIA_IOCTL_UNLOCK(ifm);
4607a9a30c5Sthorpej 			break;
4617a9a30c5Sthorpej 		}
4621b1c7ef8Sthorpej 
4631b1c7ef8Sthorpej 		/*
4641b1c7ef8Sthorpej 		 * We found a match, now make the driver switch to it.
4651b1c7ef8Sthorpej 		 * Make sure to preserve our old media type in case the
4661b1c7ef8Sthorpej 		 * driver can't switch.
4671b1c7ef8Sthorpej 		 */
4681b1c7ef8Sthorpej #ifdef IFMEDIA_DEBUG
4691b1c7ef8Sthorpej 		if (ifmedia_debug) {
4701b1c7ef8Sthorpej 			printf("ifmedia_ioctl: switching %s to ",
4711b1c7ef8Sthorpej 			    ifp->if_xname);
4721b1c7ef8Sthorpej 			ifmedia_printword(match->ifm_media);
4731b1c7ef8Sthorpej 		}
4741b1c7ef8Sthorpej #endif
4751b1c7ef8Sthorpej 		oldentry = ifm->ifm_cur;
4761b1c7ef8Sthorpej 		oldmedia = ifm->ifm_media;
4771b1c7ef8Sthorpej 		ifm->ifm_cur = match;
4781b1c7ef8Sthorpej 		ifm->ifm_media = newmedia;
47972fea72bSdyoung 		error = ifmedia_change(ifm, ifp);
4801b1c7ef8Sthorpej 		if (error) {
4811b1c7ef8Sthorpej 			ifm->ifm_cur = oldentry;
4821b1c7ef8Sthorpej 			ifm->ifm_media = oldmedia;
4831b1c7ef8Sthorpej 		}
4847a9a30c5Sthorpej 		IFMEDIA_IOCTL_UNLOCK(ifm);
4851b1c7ef8Sthorpej 		break;
4861b1c7ef8Sthorpej 	}
4871b1c7ef8Sthorpej 
4881b9aeaf0Smsaitoh 	/* Get list of available media and current media on interface. */
4891b1c7ef8Sthorpej 	case SIOCGIFMEDIA:
4901b1c7ef8Sthorpej 	{
4914385a5d1Sthorpej 		int nwords1, nwords2;
4921b1c7ef8Sthorpej 
4937a9a30c5Sthorpej 		if (ifmr->ifm_count < 0) {
4947a9a30c5Sthorpej 			error = EINVAL;
4957a9a30c5Sthorpej 			break;
4967a9a30c5Sthorpej 		}
4971b1c7ef8Sthorpej 
4987a9a30c5Sthorpej 		IFMEDIA_IOCTL_LOCK(ifm);
4991b1c7ef8Sthorpej 		ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
5001b1c7ef8Sthorpej 		    ifm->ifm_cur->ifm_media : IFM_NONE;
5011b1c7ef8Sthorpej 		ifmr->ifm_mask = ifm->ifm_mask;
5021b1c7ef8Sthorpej 		ifmr->ifm_status = 0;
503ae07208dSmlelstv 		ifmedia_status(ifm, ifp, ifmr);
5041b1c7ef8Sthorpej 
5059a1168a5Schristos 		/*
5067a9a30c5Sthorpej 		 * Count them so we know how much is the max we'll
5079a1168a5Schristos 		 * need.
5089a1168a5Schristos 		 */
5094385a5d1Sthorpej 		nwords1 = nwords2 = ifmedia_getwords(ifm, NULL, 0);
5107a9a30c5Sthorpej 		IFMEDIA_IOCTL_UNLOCK(ifm);
5111b1c7ef8Sthorpej 
5121b1c7ef8Sthorpej 		if (ifmr->ifm_count != 0) {
5134385a5d1Sthorpej 			int maxwords = MIN(nwords1, ifmr->ifm_count);
5144385a5d1Sthorpej 			int *kptr = kmem_zalloc(maxwords * sizeof(int),
5154385a5d1Sthorpej 			    KM_SLEEP);
51653cc1211Smsaitoh 
5177a9a30c5Sthorpej 			ifmedia_lock(ifm);
5184385a5d1Sthorpej 			nwords2 = ifmedia_getwords(ifm, kptr, maxwords);
5197a9a30c5Sthorpej 			ifmedia_unlock(ifm);
5209a1168a5Schristos 			error = copyout(kptr, ifmr->ifm_ulist,
5214385a5d1Sthorpej 			    maxwords * sizeof(int));
5224385a5d1Sthorpej 			if (error == 0 && nwords2 > nwords1)
5231b1c7ef8Sthorpej 				error = E2BIG;	/* oops! */
5244385a5d1Sthorpej 			kmem_free(kptr, maxwords * sizeof(int));
5259a1168a5Schristos 		}
526bf354a07Smsaitoh 		/* Update with the real number */
5274385a5d1Sthorpej 		ifmr->ifm_count = nwords2;
5281b1c7ef8Sthorpej 		break;
5291b1c7ef8Sthorpej 	}
5301b1c7ef8Sthorpej 
5311b1c7ef8Sthorpej 	default:
5327a9a30c5Sthorpej 		error = EINVAL;
5337a9a30c5Sthorpej 		break;
5341b1c7ef8Sthorpej 	}
5351b1c7ef8Sthorpej 
5367a9a30c5Sthorpej 	KERNEL_UNLOCK_UNLESS_IFP_MPSAFE(ifp);
5377a9a30c5Sthorpej 
538428e2171Smsaitoh 	return error;
5391b1c7ef8Sthorpej }
5401b1c7ef8Sthorpej 
5411b1c7ef8Sthorpej /*
5421b1c7ef8Sthorpej  * Find media entry matching a given ifm word.
5431b1c7ef8Sthorpej  */
5447a9a30c5Sthorpej static struct ifmedia_entry *
ifmedia_match_locked(struct ifmedia * ifm,u_int target,u_int mask)5457a9a30c5Sthorpej ifmedia_match_locked(struct ifmedia *ifm, u_int target, u_int mask)
5461b1c7ef8Sthorpej {
5471b1c7ef8Sthorpej 	struct ifmedia_entry *match, *next;
5481b1c7ef8Sthorpej 
5491b1c7ef8Sthorpej 	match = NULL;
5501b1c7ef8Sthorpej 	mask = ~mask;
5511b1c7ef8Sthorpej 
552ecdec069Smsaitoh 	TAILQ_FOREACH(next, &ifm->ifm_list, ifm_list) {
5531b1c7ef8Sthorpej 		if ((next->ifm_media & mask) == (target & mask)) {
5541b1c7ef8Sthorpej 			if (match) {
5557d862276Schs #if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC)
5561b1c7ef8Sthorpej 				printf("ifmedia_match: multiple match for "
5577d862276Schs 				    "0x%x/0x%x, selected instance %d\n",
5587d862276Schs 				    target, mask, IFM_INST(match->ifm_media));
5591b1c7ef8Sthorpej #endif
5607d862276Schs 				break;
5617d862276Schs 			}
5621b1c7ef8Sthorpej 			match = next;
5631b1c7ef8Sthorpej 		}
5641b1c7ef8Sthorpej 	}
5651b1c7ef8Sthorpej 
5661b1c7ef8Sthorpej 	return match;
5671b1c7ef8Sthorpej }
5681b1c7ef8Sthorpej 
5697a9a30c5Sthorpej struct ifmedia_entry *
ifmedia_match(struct ifmedia * ifm,u_int target,u_int mask)5707a9a30c5Sthorpej ifmedia_match(struct ifmedia *ifm, u_int target, u_int mask)
5717a9a30c5Sthorpej {
5727a9a30c5Sthorpej 	struct ifmedia_entry *match;
5737a9a30c5Sthorpej 
5747a9a30c5Sthorpej 	/*
5757a9a30c5Sthorpej 	 * N.B. We expect the caller is responsible fot the lifecycle
5767a9a30c5Sthorpej 	 * of the media entries.  Use with extreme caution.
5777a9a30c5Sthorpej 	 */
5787a9a30c5Sthorpej 
5797a9a30c5Sthorpej 	ifmedia_lock(ifm);
5807a9a30c5Sthorpej 	match = ifmedia_match_locked(ifm, target, mask);
5817a9a30c5Sthorpej 	ifmedia_unlock(ifm);
5827a9a30c5Sthorpej 	return match;
5837a9a30c5Sthorpej }
5847a9a30c5Sthorpej 
5853302e4f5Sthorpej /*
5863302e4f5Sthorpej  * Delete all media for a given instance.
5873302e4f5Sthorpej  */
5883302e4f5Sthorpej void
ifmedia_delete_instance(struct ifmedia * ifm,u_int inst)58915921b5fSthorpej ifmedia_delete_instance(struct ifmedia *ifm, u_int inst)
5903302e4f5Sthorpej {
5913302e4f5Sthorpej 	struct ifmedia_entry *ife, *nife;
5927a9a30c5Sthorpej 	TAILQ_HEAD(, ifmedia_entry) dead_entries;
5933302e4f5Sthorpej 
5947a9a30c5Sthorpej 	TAILQ_INIT(&dead_entries);
5957a9a30c5Sthorpej 
5967a9a30c5Sthorpej 	ifmedia_lock(ifm);
597ecdec069Smsaitoh 	TAILQ_FOREACH_SAFE(ife, &ifm->ifm_list, ifm_list, nife) {
5983302e4f5Sthorpej 		if (inst == IFM_INST_ANY ||
5993302e4f5Sthorpej 		    inst == IFM_INST(ife->ifm_media)) {
600a2249e70Sthorpej 			if (ifm->ifm_cur == ife) {
601a2249e70Sthorpej 				ifm->ifm_cur = NULL;
602a2249e70Sthorpej 				ifm->ifm_media = IFM_NONE;
603a2249e70Sthorpej 			}
6043302e4f5Sthorpej 			TAILQ_REMOVE(&ifm->ifm_list, ife, ifm_list);
6057a9a30c5Sthorpej 			TAILQ_INSERT_TAIL(&dead_entries, ife, ifm_list);
6063302e4f5Sthorpej 		}
6073302e4f5Sthorpej 	}
6087a9a30c5Sthorpej 	ifmedia_unlock(ifm);
6097a9a30c5Sthorpej 
6107a9a30c5Sthorpej 	TAILQ_FOREACH_SAFE(ife, &dead_entries, ifm_list, nife) {
6117a9a30c5Sthorpej 		TAILQ_REMOVE(&dead_entries, ife, ifm_list);
6127a9a30c5Sthorpej 		kmem_free(ife, sizeof(*ife));
6137a9a30c5Sthorpej 	}
6143302e4f5Sthorpej }
6153302e4f5Sthorpej 
616ab4f03ffSchristos void
ifmedia_removeall(struct ifmedia * ifm)617ab4f03ffSchristos ifmedia_removeall(struct ifmedia *ifm)
618ab4f03ffSchristos {
619ab4f03ffSchristos 
620428e2171Smsaitoh 	ifmedia_delete_instance(ifm, IFM_INST_ANY);
621ab4f03ffSchristos }
622ab4f03ffSchristos 
623877704d3Sthorpej /*
624877704d3Sthorpej  * Compute the interface `baudrate' from the media, for the interface
625877704d3Sthorpej  * metrics (used by routing daemons).
626877704d3Sthorpej  */
627fd0f6888Sjdolecek static const struct ifmedia_baudrate ifmedia_baudrate_descriptions[] =
628877704d3Sthorpej     IFM_BAUDRATE_DESCRIPTIONS;
629877704d3Sthorpej 
6307b7a5800Sdyoung uint64_t
ifmedia_baudrate(int mword)6319a3ec25fSragge ifmedia_baudrate(int mword)
632877704d3Sthorpej {
633877704d3Sthorpej 	int i;
634877704d3Sthorpej 
635877704d3Sthorpej 	for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) {
636bf354a07Smsaitoh 		if (IFM_TYPE_SUBTYPE_MATCH(mword,
637bf354a07Smsaitoh 		    ifmedia_baudrate_descriptions[i].ifmb_word))
6381b9aeaf0Smsaitoh 			return ifmedia_baudrate_descriptions[i].ifmb_baudrate;
639877704d3Sthorpej 	}
640877704d3Sthorpej 
641877704d3Sthorpej 	/* Not known. */
642428e2171Smsaitoh 	return 0;
643877704d3Sthorpej }
644877704d3Sthorpej 
6451b1c7ef8Sthorpej #ifdef IFMEDIA_DEBUG
646b04ded72Sthorpej 
647fd0f6888Sjdolecek static const struct ifmedia_description ifm_type_descriptions[] =
6481b1c7ef8Sthorpej     IFM_TYPE_DESCRIPTIONS;
6491b1c7ef8Sthorpej 
650fd0f6888Sjdolecek static const struct ifmedia_description ifm_subtype_descriptions[] =
651b04ded72Sthorpej     IFM_SUBTYPE_DESCRIPTIONS;
6521b1c7ef8Sthorpej 
653fd0f6888Sjdolecek static const struct ifmedia_description ifm_option_descriptions[] =
654b04ded72Sthorpej     IFM_OPTION_DESCRIPTIONS;
6551b1c7ef8Sthorpej 
6561b1c7ef8Sthorpej /*
6571b1c7ef8Sthorpej  * print a media word.
6581b1c7ef8Sthorpej  */
6591b1c7ef8Sthorpej static void
ifmedia_printword(int ifmw)66015921b5fSthorpej ifmedia_printword(int ifmw)
6611b1c7ef8Sthorpej {
662fd0f6888Sjdolecek 	const struct ifmedia_description *desc;
6631b1c7ef8Sthorpej 	int seen_option = 0;
6641b1c7ef8Sthorpej 
665b04ded72Sthorpej 	/* Print the top-level interface type. */
666b04ded72Sthorpej 	for (desc = ifm_type_descriptions; desc->ifmt_string != NULL;
667b04ded72Sthorpej 	     desc++) {
6681b1c7ef8Sthorpej 		if (IFM_TYPE(ifmw) == desc->ifmt_word)
6691b1c7ef8Sthorpej 			break;
6701b1c7ef8Sthorpej 	}
671b04ded72Sthorpej 	if (desc->ifmt_string == NULL)
672b04ded72Sthorpej 		printf("<unknown type> ");
673b04ded72Sthorpej 	else
6741b1c7ef8Sthorpej 		printf("%s ", desc->ifmt_string);
6751b1c7ef8Sthorpej 
676b04ded72Sthorpej 	/* Print the subtype. */
677b04ded72Sthorpej 	for (desc = ifm_subtype_descriptions; desc->ifmt_string != NULL;
678b04ded72Sthorpej 	     desc++) {
679b04ded72Sthorpej 		if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) &&
680b04ded72Sthorpej 		    IFM_SUBTYPE(desc->ifmt_word) == IFM_SUBTYPE(ifmw))
681b04ded72Sthorpej 			break;
6821b1c7ef8Sthorpej 	}
683b04ded72Sthorpej 	if (desc->ifmt_string == NULL)
684b04ded72Sthorpej 		printf("<unknown subtype>");
685b04ded72Sthorpej 	else
686b04ded72Sthorpej 		printf("%s", desc->ifmt_string);
6871b1c7ef8Sthorpej 
688b04ded72Sthorpej 	/* Print any options. */
689b04ded72Sthorpej 	for (desc = ifm_option_descriptions; desc->ifmt_string != NULL;
690b04ded72Sthorpej 	     desc++) {
691b04ded72Sthorpej 		if (IFM_TYPE_MATCH(desc->ifmt_word, ifmw) &&
692b04ded72Sthorpej 		    (ifmw & desc->ifmt_word) != 0 &&
693b04ded72Sthorpej 		    (seen_option & IFM_OPTIONS(desc->ifmt_word)) == 0) {
6941b1c7ef8Sthorpej 			if (seen_option == 0)
6951b1c7ef8Sthorpej 				printf(" <");
69627d86392Senami 			printf("%s%s", seen_option ? "," : "",
6971b1c7ef8Sthorpej 			    desc->ifmt_string);
698b04ded72Sthorpej 			seen_option |= IFM_OPTIONS(desc->ifmt_word);
6991b1c7ef8Sthorpej 		}
7001b1c7ef8Sthorpej 	}
7011b1c7ef8Sthorpej 	printf("%s\n", seen_option ? ">" : "");
7021b1c7ef8Sthorpej }
703b04ded72Sthorpej 
7041b1c7ef8Sthorpej #endif /* IFMEDIA_DEBUG */
705