if_media.c revision 77217
125428Speter/*	$NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $	*/
250477Speter/* $FreeBSD: head/sys/net/if_media.c 77217 2001-05-26 09:27:08Z phk $ */
325428Speter
425428Speter/*
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
4925428Speter#include <sys/param.h>
5025428Speter#include <sys/systm.h>
5125428Speter#include <sys/socket.h>
5225431Speter#include <sys/sockio.h>
5325428Speter#include <sys/malloc.h>
5425428Speter
5525428Speter#include <net/if.h>
5625428Speter#include <net/if_media.h>
5725428Speter
5825428Speter/*
5925428Speter * Compile-time options:
6025428Speter * IFMEDIA_DEBUG:
6125428Speter *	turn on implementation-level debug printfs.
6225428Speter * 	Useful for debugging newly-ported  drivers.
6325428Speter */
6425428Speter
6533181Seivindstatic struct ifmedia_entry *ifmedia_match __P((struct ifmedia *ifm,
6625428Speter    int flags, int mask));
6725428Speter
6825428Speter#ifdef IFMEDIA_DEBUG
6925428Speterint	ifmedia_debug = 0;
7025428Speterstatic	void ifmedia_printword __P((int));
7125428Speter#endif
7225428Speter
7325428Speter/*
7425428Speter * Initialize if_media struct for a specific interface instance.
7525428Speter */
7625428Spetervoid
7725428Speterifmedia_init(ifm, dontcare_mask, change_callback, status_callback)
7825428Speter	struct ifmedia *ifm;
7925428Speter	int dontcare_mask;
8025428Speter	ifm_change_cb_t change_callback;
8125428Speter	ifm_stat_cb_t status_callback;
8225428Speter{
8325428Speter
8425428Speter	LIST_INIT(&ifm->ifm_list);
8525428Speter	ifm->ifm_cur = NULL;
8625428Speter	ifm->ifm_media = 0;
8725428Speter	ifm->ifm_mask = dontcare_mask;		/* IF don't-care bits */
8825428Speter	ifm->ifm_change = change_callback;
8925428Speter	ifm->ifm_status = status_callback;
9025428Speter}
9125428Speter
9245720Spetervoid
9345720Speterifmedia_removeall(ifm)
9445720Speter	struct ifmedia *ifm;
9545720Speter{
9645720Speter	struct ifmedia_entry *entry;
9745720Speter
9845720Speter	for (entry = LIST_FIRST(&ifm->ifm_list); entry;
9945720Speter	     entry = LIST_FIRST(&ifm->ifm_list)) {
10045720Speter		LIST_REMOVE(entry, ifm_list);
10145720Speter		free(entry, M_IFADDR);
10245720Speter	}
10345720Speter}
10445720Speter
10525428Speter/*
10625428Speter * Add a media configuration to the list of supported media
10725428Speter * for a specific interface instance.
10825428Speter */
10925428Spetervoid
11025428Speterifmedia_add(ifm, mword, data, aux)
11125428Speter	struct ifmedia *ifm;
11225428Speter	int mword;
11325428Speter	int data;
11425428Speter	void *aux;
11525428Speter{
11625428Speter	register struct ifmedia_entry *entry;
11725428Speter
11825428Speter#ifdef IFMEDIA_DEBUG
11925428Speter	if (ifmedia_debug) {
12025428Speter		if (ifm == NULL) {
12125428Speter			printf("ifmedia_add: null ifm\n");
12225428Speter			return;
12325428Speter		}
12425428Speter		printf("Adding entry for ");
12525428Speter		ifmedia_printword(mword);
12625428Speter	}
12725428Speter#endif
12825428Speter
12925428Speter	entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT);
13025428Speter	if (entry == NULL)
13125428Speter		panic("ifmedia_add: can't malloc entry");
13225428Speter
13325428Speter	entry->ifm_media = mword;
13425428Speter	entry->ifm_data = data;
13525428Speter	entry->ifm_aux = aux;
13625428Speter
13725428Speter	LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list);
13825428Speter}
13925428Speter
14025428Speter/*
14125428Speter * Add an array of media configurations to the list of
14225428Speter * supported media for a specific interface instance.
14325428Speter */
14425428Spetervoid
14525428Speterifmedia_list_add(ifm, lp, count)
14625428Speter	struct ifmedia *ifm;
14725428Speter	struct ifmedia_entry *lp;
14825428Speter	int count;
14925428Speter{
15025428Speter	int i;
15125428Speter
15225428Speter	for (i = 0; i < count; i++)
15325428Speter		ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data,
15425428Speter		    lp[i].ifm_aux);
15525428Speter}
15625428Speter
15725428Speter/*
15825428Speter * Set the default active media.
15925428Speter *
16025428Speter * Called by device-specific code which is assumed to have already
16125428Speter * selected the default media in hardware.  We do _not_ call the
16225428Speter * media-change callback.
16325428Speter */
16425428Spetervoid
16525428Speterifmedia_set(ifm, target)
16625428Speter	struct ifmedia *ifm;
16725428Speter	int target;
16825428Speter
16925428Speter{
17025428Speter	struct ifmedia_entry *match;
17125428Speter
17225428Speter	match = ifmedia_match(ifm, target, ifm->ifm_mask);
17325428Speter
17425428Speter	if (match == NULL) {
17525428Speter		printf("ifmedia_set: no match for 0x%x/0x%x\n",
17625428Speter		    target, ~ifm->ifm_mask);
17725428Speter		panic("ifmedia_set");
17825428Speter	}
17925428Speter	ifm->ifm_cur = match;
18025428Speter
18125428Speter#ifdef IFMEDIA_DEBUG
18225428Speter	if (ifmedia_debug) {
18325428Speter		printf("ifmedia_set: target ");
18425428Speter		ifmedia_printword(target);
18525428Speter		printf("ifmedia_set: setting to ");
18625428Speter		ifmedia_printword(ifm->ifm_cur->ifm_media);
18725428Speter	}
18825428Speter#endif
18925428Speter}
19025428Speter
19125428Speter/*
19225428Speter * Device-independent media ioctl support function.
19325428Speter */
19425428Speterint
19525428Speterifmedia_ioctl(ifp, ifr, ifm, cmd)
19625428Speter	struct ifnet *ifp;
19725428Speter	struct ifreq *ifr;
19825428Speter	struct ifmedia *ifm;
19925428Speter	u_long cmd;
20025428Speter{
20125428Speter	struct ifmedia_entry *match;
20225428Speter	struct ifmediareq *ifmr = (struct ifmediareq *) ifr;
20325428Speter	int error = 0, sticky;
20425428Speter
20525428Speter	if (ifp == NULL || ifr == NULL || ifm == NULL)
20625428Speter		return(EINVAL);
20725428Speter
20825428Speter	switch (cmd) {
20925428Speter
21025428Speter	/*
21125428Speter	 * Set the current media.
21225428Speter	 */
21325428Speter	case  SIOCSIFMEDIA:
21425428Speter	{
21525428Speter		struct ifmedia_entry *oldentry;
21625428Speter		int oldmedia;
21725428Speter		int newmedia = ifr->ifr_media;
21825428Speter
21925428Speter		match = ifmedia_match(ifm, newmedia, ifm->ifm_mask);
22025428Speter		if (match == NULL) {
22125428Speter#ifdef IFMEDIA_DEBUG
22225428Speter			if (ifmedia_debug) {
22325428Speter				printf(
22425428Speter				    "ifmedia_ioctl: no media found for 0x%x\n",
22525428Speter				    newmedia);
22625428Speter			}
22725428Speter#endif
22825428Speter			return (ENXIO);
22925428Speter		}
23025428Speter
23125428Speter		/*
23225428Speter		 * If no change, we're done.
23325428Speter		 * XXX Automedia may invole software intervention.
23425428Speter		 *     Keep going in case the the connected media changed.
23525428Speter		 *     Similarly, if best match changed (kernel debugger?).
23625428Speter		 */
23725428Speter		if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) &&
23825428Speter		    (newmedia == ifm->ifm_media) &&
23925428Speter		    (match == ifm->ifm_cur))
24025428Speter			return 0;
24125428Speter
24225428Speter		/*
24325428Speter		 * We found a match, now make the driver switch to it.
24425428Speter		 * Make sure to preserve our old media type in case the
24525428Speter		 * driver can't switch.
24625428Speter		 */
24725428Speter#ifdef IFMEDIA_DEBUG
24825428Speter		if (ifmedia_debug) {
24950013Speter			printf("ifmedia_ioctl: switching %s%d to ",
25050013Speter			    ifp->if_name, ifp->if_unit);
25125428Speter			ifmedia_printword(match->ifm_media);
25225428Speter		}
25325428Speter#endif
25425428Speter		oldentry = ifm->ifm_cur;
25525428Speter		oldmedia = ifm->ifm_media;
25625428Speter		ifm->ifm_cur = match;
25725428Speter		ifm->ifm_media = newmedia;
25825428Speter		error = (*ifm->ifm_change)(ifp);
25925428Speter		if (error) {
26025428Speter			ifm->ifm_cur = oldentry;
26125428Speter			ifm->ifm_media = oldmedia;
26225428Speter		}
26325428Speter		break;
26425428Speter	}
26525428Speter
26625428Speter	/*
26725428Speter	 * Get list of available media and current media on interface.
26825428Speter	 */
26925428Speter	case  SIOCGIFMEDIA:
27025428Speter	{
27125428Speter		struct ifmedia_entry *ep;
27225428Speter		int *kptr, count;
27373078Salfred		int usermax;	/* user requested max */
27425428Speter
27525428Speter		kptr = NULL;		/* XXX gcc */
27625428Speter
27725428Speter		ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
27825428Speter		    ifm->ifm_cur->ifm_media : IFM_NONE;
27925428Speter		ifmr->ifm_mask = ifm->ifm_mask;
28025428Speter		ifmr->ifm_status = 0;
28125428Speter		(*ifm->ifm_status)(ifp, ifmr);
28225428Speter
28325428Speter		count = 0;
28473078Salfred		usermax = 0;
28525428Speter
28673078Salfred		/*
28773078Salfred		 * If there are more interfaces on the list, count
28873078Salfred		 * them.  This allows the caller to set ifmr->ifm_count
28973078Salfred		 * to 0 on the first call to know how much space to
29073079Salfred		 * allocate.
29173078Salfred		 */
29273078Salfred		LIST_FOREACH(ep, &ifm->ifm_list, ifm_list)
29373078Salfred			usermax++;
29473078Salfred
29573078Salfred		/*
29673078Salfred		 * Don't allow the user to ask for too many
29773085Salfred		 * or a negative number.
29873078Salfred		 */
29973078Salfred		if (ifmr->ifm_count > usermax)
30073078Salfred			ifmr->ifm_count = usermax;
30173085Salfred		else if (ifmr->ifm_count < 0)
30273085Salfred			return (EINVAL);
30373078Salfred
30425428Speter		if (ifmr->ifm_count != 0) {
30525428Speter			kptr = (int *)malloc(ifmr->ifm_count * sizeof(int),
30625428Speter			    M_TEMP, M_WAITOK);
30725428Speter
30825428Speter			/*
30925428Speter			 * Get the media words from the interface's list.
31025428Speter			 */
31173078Salfred			ep = LIST_FIRST(&ifm->ifm_list);
31225428Speter			for (; ep != NULL && count < ifmr->ifm_count;
31371959Sphk			    ep = LIST_NEXT(ep, ifm_list), count++)
31425428Speter				kptr[count] = ep->ifm_media;
31525428Speter
31625428Speter			if (ep != NULL)
31725428Speter				error = E2BIG;	/* oops! */
31873078Salfred		} else {
31973078Salfred			count = usermax;
32025428Speter		}
32125428Speter
32225428Speter		/*
32325428Speter		 * We do the copyout on E2BIG, because that's
32425428Speter		 * just our way of telling userland that there
32525428Speter		 * are more.  This is the behavior I've observed
32625428Speter		 * under BSD/OS 3.0
32725428Speter		 */
32825428Speter		sticky = error;
32925428Speter		if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) {
33025428Speter			error = copyout((caddr_t)kptr,
33125428Speter			    (caddr_t)ifmr->ifm_ulist,
33225428Speter			    ifmr->ifm_count * sizeof(int));
33325428Speter		}
33425428Speter
33525428Speter		if (error == 0)
33625428Speter			error = sticky;
33725428Speter
33825428Speter		if (ifmr->ifm_count != 0)
33925428Speter			free(kptr, M_TEMP);
34025428Speter
34125428Speter		ifmr->ifm_count = count;
34225428Speter		break;
34325428Speter	}
34425428Speter
34525428Speter	default:
34625428Speter		return (EINVAL);
34725428Speter	}
34825428Speter
34925428Speter	return (error);
35025428Speter}
35125428Speter
35225428Speter/*
35325428Speter * Find media entry matching a given ifm word.
35425428Speter *
35525428Speter */
35633181Seivindstatic struct ifmedia_entry *
35725428Speterifmedia_match(ifm, target, mask)
35825428Speter	struct ifmedia *ifm;
35925428Speter	int target;
36025428Speter	int mask;
36125428Speter{
36225428Speter	struct ifmedia_entry *match, *next;
36325428Speter
36425428Speter	match = NULL;
36525428Speter	mask = ~mask;
36625428Speter
36772012Sphk	LIST_FOREACH(next, &ifm->ifm_list, ifm_list) {
36825428Speter		if ((next->ifm_media & mask) == (target & mask)) {
36925428Speter#if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC)
37025428Speter			if (match) {
37125428Speter				printf("ifmedia_match: multiple match for "
37225428Speter				    "0x%x/0x%x\n", target, mask);
37325428Speter			}
37425428Speter#endif
37525428Speter			match = next;
37625428Speter		}
37725428Speter	}
37825428Speter
37925428Speter	return match;
38025428Speter}
38125428Speter
38225428Speter#ifdef IFMEDIA_DEBUG
38325428Speterstruct ifmedia_description ifm_type_descriptions[] =
38425428Speter    IFM_TYPE_DESCRIPTIONS;
38525428Speter
38625428Speterstruct ifmedia_description ifm_subtype_ethernet_descriptions[] =
38725428Speter    IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
38825428Speter
38925428Speterstruct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
39025428Speter    IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
39125428Speter
39225428Speterstruct ifmedia_description ifm_subtype_tokenring_descriptions[] =
39325428Speter    IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
39425428Speter
39525428Speterstruct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
39625428Speter    IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
39725428Speter
39825428Speterstruct ifmedia_description ifm_subtype_fddi_descriptions[] =
39925428Speter    IFM_SUBTYPE_FDDI_DESCRIPTIONS;
40025428Speter
40125428Speterstruct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
40225428Speter    IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
40325428Speter
40477217Sphkstruct ifmedia_description ifm_subtype_ieee80211_descriptions[] =
40577217Sphk    IFM_SUBTYPE_IEEE80211_DESCRIPTIONS;
40677217Sphk
40777217Sphkstruct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] =
40877217Sphk    IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS;
40977217Sphk
41025428Speterstruct ifmedia_description ifm_subtype_shared_descriptions[] =
41125428Speter    IFM_SUBTYPE_SHARED_DESCRIPTIONS;
41225428Speter
41325428Speterstruct ifmedia_description ifm_shared_option_descriptions[] =
41425428Speter    IFM_SHARED_OPTION_DESCRIPTIONS;
41525428Speter
41625428Speterstruct ifmedia_type_to_subtype {
41725428Speter	struct ifmedia_description *subtypes;
41825428Speter	struct ifmedia_description *options;
41925428Speter};
42025428Speter
42125428Speter/* must be in the same order as IFM_TYPE_DESCRIPTIONS */
42225428Speterstruct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
42325428Speter	{
42425428Speter	  &ifm_subtype_ethernet_descriptions[0],
42525428Speter	  &ifm_subtype_ethernet_option_descriptions[0]
42625428Speter	},
42725428Speter	{
42825428Speter	  &ifm_subtype_tokenring_descriptions[0],
42925428Speter	  &ifm_subtype_tokenring_option_descriptions[0]
43025428Speter	},
43125428Speter	{
43225428Speter	  &ifm_subtype_fddi_descriptions[0],
43325428Speter	  &ifm_subtype_fddi_option_descriptions[0]
43425428Speter	},
43577217Sphk	{
43677217Sphk	  &ifm_subtype_ieee80211_descriptions[0],
43777217Sphk	  &ifm_subtype_ieee80211_option_descriptions[0]
43877217Sphk	},
43925428Speter};
44025428Speter
44125428Speter/*
44225428Speter * print a media word.
44325428Speter */
44425428Speterstatic void
44525428Speterifmedia_printword(ifmw)
44625428Speter	int ifmw;
44725428Speter{
44825428Speter	struct ifmedia_description *desc;
44925428Speter	struct ifmedia_type_to_subtype *ttos;
45025428Speter	int seen_option = 0;
45125428Speter
45225428Speter	/* Find the top-level interface type. */
45325428Speter	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
45425428Speter	    desc->ifmt_string != NULL; desc++, ttos++)
45525428Speter		if (IFM_TYPE(ifmw) == desc->ifmt_word)
45625428Speter			break;
45725428Speter	if (desc->ifmt_string == NULL) {
45825428Speter		printf("<unknown type>\n");
45925428Speter		return;
46025428Speter	}
46125428Speter	printf(desc->ifmt_string);
46225428Speter
46325428Speter	/*
46425428Speter	 * Check for the shared subtype descriptions first, then the
46525428Speter	 * type-specific ones.
46625428Speter	 */
46725428Speter	for (desc = ifm_subtype_shared_descriptions;
46825428Speter	    desc->ifmt_string != NULL; desc++)
46925428Speter		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
47025428Speter			goto got_subtype;
47125428Speter
47225428Speter	for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++)
47325428Speter		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
47425428Speter			break;
47525428Speter	if (desc->ifmt_string == NULL) {
47625428Speter		printf(" <unknown subtype>\n");
47725428Speter		return;
47825428Speter	}
47925428Speter
48025428Speter got_subtype:
48125428Speter	printf(" %s", desc->ifmt_string);
48225428Speter
48325428Speter	/*
48425428Speter	 * Look for shared options.
48525428Speter	 */
48625428Speter	for (desc = ifm_shared_option_descriptions;
48725428Speter	    desc->ifmt_string != NULL; desc++) {
48825428Speter		if (ifmw & desc->ifmt_word) {
48925428Speter			if (seen_option == 0)
49025428Speter				printf(" <");
49125428Speter			printf("%s%s", seen_option++ ? "," : "",
49225428Speter			    desc->ifmt_string);
49325428Speter		}
49425428Speter	}
49525428Speter
49625428Speter	/*
49725428Speter	 * Look for subtype-specific options.
49825428Speter	 */
49925428Speter	for (desc = ttos->options; desc->ifmt_string != NULL; desc++) {
50025428Speter		if (ifmw & desc->ifmt_word) {
50125428Speter			if (seen_option == 0)
50225428Speter				printf(" <");
50325428Speter			printf("%s%s", seen_option++ ? "," : "",
50425428Speter			    desc->ifmt_string);
50525428Speter		}
50625428Speter	}
50725428Speter	printf("%s\n", seen_option ? ">" : "");
50825428Speter}
50925428Speter#endif /* IFMEDIA_DEBUG */
510