if_media.c revision 25428
125428Speter/*	$NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $	*/
225428Speter
325428Speter/*
425428Speter * Copyright (c) 1997
525428Speter *	Jonathan Stone and Jason R. Thorpe.  All rights reserved.
625428Speter *
725428Speter * This software is derived from information provided by Matt Thomas.
825428Speter *
925428Speter * Redistribution and use in source and binary forms, with or without
1025428Speter * modification, are permitted provided that the following conditions
1125428Speter * are met:
1225428Speter * 1. Redistributions of source code must retain the above copyright
1325428Speter *    notice, this list of conditions and the following disclaimer.
1425428Speter * 2. Redistributions in binary form must reproduce the above copyright
1525428Speter *    notice, this list of conditions and the following disclaimer in the
1625428Speter *    documentation and/or other materials provided with the distribution.
1725428Speter * 3. All advertising materials mentioning features or use of this software
1825428Speter *    must display the following acknowledgement:
1925428Speter *      This product includes software developed by Jonathan Stone
2025428Speter *	and Jason R. Thorpe for the NetBSD Project.
2125428Speter * 4. The names of the authors may not be used to endorse or promote products
2225428Speter *    derived from this software without specific prior written permission.
2325428Speter *
2425428Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
2525428Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2625428Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2725428Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2825428Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2925428Speter * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
3025428Speter * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
3125428Speter * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3225428Speter * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3325428Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3425428Speter * SUCH DAMAGE.
3525428Speter */
3625428Speter
3725428Speter/*
3825428Speter * BSD/OS-compatible network interface media selection.
3925428Speter *
4025428Speter * Where it is safe to do so, this code strays slightly from the BSD/OS
4125428Speter * design.  Software which uses the API (device drivers, basically)
4225428Speter * shouldn't notice any difference.
4325428Speter *
4425428Speter * Many thanks to Matt Thomas for providing the information necessary
4525428Speter * to implement this interface.
4625428Speter */
4725428Speter
4825428Speter#include <sys/param.h>
4925428Speter#include <sys/systm.h>
5025428Speter#include <sys/errno.h>
5125428Speter#include <sys/ioctl.h>
5225428Speter#include <sys/socket.h>
5325428Speter#include <sys/malloc.h>
5425428Speter
5525428Speter#include <net/if.h>
5625428Speter#include <net/if_media.h>
5725428Speter#include <net/netisr.h>
5825428Speter
5925428Speter/*
6025428Speter * Compile-time options:
6125428Speter * IFMEDIA_DEBUG:
6225428Speter *	turn on implementation-level debug printfs.
6325428Speter * 	Useful for debugging newly-ported  drivers.
6425428Speter */
6525428Speter
6625428Speterstruct ifmedia_entry *ifmedia_match __P((struct ifmedia *ifm,
6725428Speter    int flags, int mask));
6825428Speter
6925428Speter#ifdef IFMEDIA_DEBUG
7025428Speterint	ifmedia_debug = 0;
7125428Speterstatic	void ifmedia_printword __P((int));
7225428Speter#endif
7325428Speter
7425428Speter/*
7525428Speter * Initialize if_media struct for a specific interface instance.
7625428Speter */
7725428Spetervoid
7825428Speterifmedia_init(ifm, dontcare_mask, change_callback, status_callback)
7925428Speter	struct ifmedia *ifm;
8025428Speter	int dontcare_mask;
8125428Speter	ifm_change_cb_t change_callback;
8225428Speter	ifm_stat_cb_t status_callback;
8325428Speter{
8425428Speter
8525428Speter	LIST_INIT(&ifm->ifm_list);
8625428Speter	ifm->ifm_cur = NULL;
8725428Speter	ifm->ifm_media = 0;
8825428Speter	ifm->ifm_mask = dontcare_mask;		/* IF don't-care bits */
8925428Speter	ifm->ifm_change = change_callback;
9025428Speter	ifm->ifm_status = status_callback;
9125428Speter}
9225428Speter
9325428Speter/*
9425428Speter * Add a media configuration to the list of supported media
9525428Speter * for a specific interface instance.
9625428Speter */
9725428Spetervoid
9825428Speterifmedia_add(ifm, mword, data, aux)
9925428Speter	struct ifmedia *ifm;
10025428Speter	int mword;
10125428Speter	int data;
10225428Speter	void *aux;
10325428Speter{
10425428Speter	register struct ifmedia_entry *entry;
10525428Speter
10625428Speter#ifdef IFMEDIA_DEBUG
10725428Speter	if (ifmedia_debug) {
10825428Speter		if (ifm == NULL) {
10925428Speter			printf("ifmedia_add: null ifm\n");
11025428Speter			return;
11125428Speter		}
11225428Speter		printf("Adding entry for ");
11325428Speter		ifmedia_printword(mword);
11425428Speter	}
11525428Speter#endif
11625428Speter
11725428Speter	entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT);
11825428Speter	if (entry == NULL)
11925428Speter		panic("ifmedia_add: can't malloc entry");
12025428Speter
12125428Speter	entry->ifm_media = mword;
12225428Speter	entry->ifm_data = data;
12325428Speter	entry->ifm_aux = aux;
12425428Speter
12525428Speter	LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list);
12625428Speter}
12725428Speter
12825428Speter/*
12925428Speter * Add an array of media configurations to the list of
13025428Speter * supported media for a specific interface instance.
13125428Speter */
13225428Spetervoid
13325428Speterifmedia_list_add(ifm, lp, count)
13425428Speter	struct ifmedia *ifm;
13525428Speter	struct ifmedia_entry *lp;
13625428Speter	int count;
13725428Speter{
13825428Speter	int i;
13925428Speter
14025428Speter	for (i = 0; i < count; i++)
14125428Speter		ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data,
14225428Speter		    lp[i].ifm_aux);
14325428Speter}
14425428Speter
14525428Speter/*
14625428Speter * Set the default active media.
14725428Speter *
14825428Speter * Called by device-specific code which is assumed to have already
14925428Speter * selected the default media in hardware.  We do _not_ call the
15025428Speter * media-change callback.
15125428Speter */
15225428Spetervoid
15325428Speterifmedia_set(ifm, target)
15425428Speter	struct ifmedia *ifm;
15525428Speter	int target;
15625428Speter
15725428Speter{
15825428Speter	struct ifmedia_entry *match;
15925428Speter
16025428Speter	match = ifmedia_match(ifm, target, ifm->ifm_mask);
16125428Speter
16225428Speter	if (match == NULL) {
16325428Speter		printf("ifmedia_set: no match for 0x%x/0x%x\n",
16425428Speter		    target, ~ifm->ifm_mask);
16525428Speter		panic("ifmedia_set");
16625428Speter	}
16725428Speter	ifm->ifm_cur = match;
16825428Speter
16925428Speter#ifdef IFMEDIA_DEBUG
17025428Speter	if (ifmedia_debug) {
17125428Speter		printf("ifmedia_set: target ");
17225428Speter		ifmedia_printword(target);
17325428Speter		printf("ifmedia_set: setting to ");
17425428Speter		ifmedia_printword(ifm->ifm_cur->ifm_media);
17525428Speter	}
17625428Speter#endif
17725428Speter}
17825428Speter
17925428Speter/*
18025428Speter * Device-independent media ioctl support function.
18125428Speter */
18225428Speterint
18325428Speterifmedia_ioctl(ifp, ifr, ifm, cmd)
18425428Speter	struct ifnet *ifp;
18525428Speter	struct ifreq *ifr;
18625428Speter	struct ifmedia *ifm;
18725428Speter	u_long cmd;
18825428Speter{
18925428Speter	struct ifmedia_entry *match;
19025428Speter	struct ifmediareq *ifmr = (struct ifmediareq *) ifr;
19125428Speter	int error = 0, sticky;
19225428Speter
19325428Speter	if (ifp == NULL || ifr == NULL || ifm == NULL)
19425428Speter		return(EINVAL);
19525428Speter
19625428Speter	switch (cmd) {
19725428Speter
19825428Speter	/*
19925428Speter	 * Set the current media.
20025428Speter	 */
20125428Speter	case  SIOCSIFMEDIA:
20225428Speter	{
20325428Speter		struct ifmedia_entry *oldentry;
20425428Speter		int oldmedia;
20525428Speter		int newmedia = ifr->ifr_media;
20625428Speter
20725428Speter		match = ifmedia_match(ifm, newmedia, ifm->ifm_mask);
20825428Speter		if (match == NULL) {
20925428Speter#ifdef IFMEDIA_DEBUG
21025428Speter			if (ifmedia_debug) {
21125428Speter				printf(
21225428Speter				    "ifmedia_ioctl: no media found for 0x%x\n",
21325428Speter				    newmedia);
21425428Speter			}
21525428Speter#endif
21625428Speter			return (ENXIO);
21725428Speter		}
21825428Speter
21925428Speter		/*
22025428Speter		 * If no change, we're done.
22125428Speter		 * XXX Automedia may invole software intervention.
22225428Speter		 *     Keep going in case the the connected media changed.
22325428Speter		 *     Similarly, if best match changed (kernel debugger?).
22425428Speter		 */
22525428Speter		if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) &&
22625428Speter		    (newmedia == ifm->ifm_media) &&
22725428Speter		    (match == ifm->ifm_cur))
22825428Speter			return 0;
22925428Speter
23025428Speter		/*
23125428Speter		 * We found a match, now make the driver switch to it.
23225428Speter		 * Make sure to preserve our old media type in case the
23325428Speter		 * driver can't switch.
23425428Speter		 */
23525428Speter#ifdef IFMEDIA_DEBUG
23625428Speter		if (ifmedia_debug) {
23725428Speter			printf("ifmedia_ioctl: switching %s to ",
23825428Speter			    ifp->if_xname);
23925428Speter			ifmedia_printword(match->ifm_media);
24025428Speter		}
24125428Speter#endif
24225428Speter		oldentry = ifm->ifm_cur;
24325428Speter		oldmedia = ifm->ifm_media;
24425428Speter		ifm->ifm_cur = match;
24525428Speter		ifm->ifm_media = newmedia;
24625428Speter		error = (*ifm->ifm_change)(ifp);
24725428Speter		if (error) {
24825428Speter			ifm->ifm_cur = oldentry;
24925428Speter			ifm->ifm_media = oldmedia;
25025428Speter		}
25125428Speter		break;
25225428Speter	}
25325428Speter
25425428Speter	/*
25525428Speter	 * Get list of available media and current media on interface.
25625428Speter	 */
25725428Speter	case  SIOCGIFMEDIA:
25825428Speter	{
25925428Speter		struct ifmedia_entry *ep;
26025428Speter		int *kptr, count;
26125428Speter
26225428Speter		kptr = NULL;		/* XXX gcc */
26325428Speter
26425428Speter		ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
26525428Speter		    ifm->ifm_cur->ifm_media : IFM_NONE;
26625428Speter		ifmr->ifm_mask = ifm->ifm_mask;
26725428Speter		ifmr->ifm_status = 0;
26825428Speter		(*ifm->ifm_status)(ifp, ifmr);
26925428Speter
27025428Speter		count = 0;
27125428Speter		ep = ifm->ifm_list.lh_first;
27225428Speter
27325428Speter		if (ifmr->ifm_count != 0) {
27425428Speter			kptr = (int *)malloc(ifmr->ifm_count * sizeof(int),
27525428Speter			    M_TEMP, M_WAITOK);
27625428Speter
27725428Speter			/*
27825428Speter			 * Get the media words from the interface's list.
27925428Speter			 */
28025428Speter			for (; ep != NULL && count < ifmr->ifm_count;
28125428Speter			    ep = ep->ifm_list.le_next, count++)
28225428Speter				kptr[count] = ep->ifm_media;
28325428Speter
28425428Speter			if (ep != NULL)
28525428Speter				error = E2BIG;	/* oops! */
28625428Speter		}
28725428Speter
28825428Speter		/*
28925428Speter		 * If there are more interfaces on the list, count
29025428Speter		 * them.  This allows the caller to set ifmr->ifm_count
29125428Speter		 * to 0 on the first call to know how much space to
29225428Speter		 * callocate.
29325428Speter		 */
29425428Speter		for (; ep != NULL; ep = ep->ifm_list.le_next)
29525428Speter			count++;
29625428Speter
29725428Speter		/*
29825428Speter		 * We do the copyout on E2BIG, because that's
29925428Speter		 * just our way of telling userland that there
30025428Speter		 * are more.  This is the behavior I've observed
30125428Speter		 * under BSD/OS 3.0
30225428Speter		 */
30325428Speter		sticky = error;
30425428Speter		if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) {
30525428Speter			error = copyout((caddr_t)kptr,
30625428Speter			    (caddr_t)ifmr->ifm_ulist,
30725428Speter			    ifmr->ifm_count * sizeof(int));
30825428Speter		}
30925428Speter
31025428Speter		if (error == 0)
31125428Speter			error = sticky;
31225428Speter
31325428Speter		if (ifmr->ifm_count != 0)
31425428Speter			free(kptr, M_TEMP);
31525428Speter
31625428Speter		ifmr->ifm_count = count;
31725428Speter		break;
31825428Speter	}
31925428Speter
32025428Speter	default:
32125428Speter		return (EINVAL);
32225428Speter	}
32325428Speter
32425428Speter	return (error);
32525428Speter}
32625428Speter
32725428Speter/*
32825428Speter * Find media entry matching a given ifm word.
32925428Speter *
33025428Speter */
33125428Speterstruct ifmedia_entry *
33225428Speterifmedia_match(ifm, target, mask)
33325428Speter	struct ifmedia *ifm;
33425428Speter	int target;
33525428Speter	int mask;
33625428Speter{
33725428Speter	struct ifmedia_entry *match, *next;
33825428Speter
33925428Speter	match = NULL;
34025428Speter	mask = ~mask;
34125428Speter
34225428Speter	for (next = ifm->ifm_list.lh_first; next != NULL;
34325428Speter	    next = next->ifm_list.le_next) {
34425428Speter		if ((next->ifm_media & mask) == (target & mask)) {
34525428Speter#if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC)
34625428Speter			if (match) {
34725428Speter				printf("ifmedia_match: multiple match for "
34825428Speter				    "0x%x/0x%x\n", target, mask);
34925428Speter			}
35025428Speter#endif
35125428Speter			match = next;
35225428Speter		}
35325428Speter	}
35425428Speter
35525428Speter	return match;
35625428Speter}
35725428Speter
35825428Speter#ifdef IFMEDIA_DEBUG
35925428Speterstruct ifmedia_description ifm_type_descriptions[] =
36025428Speter    IFM_TYPE_DESCRIPTIONS;
36125428Speter
36225428Speterstruct ifmedia_description ifm_subtype_ethernet_descriptions[] =
36325428Speter    IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
36425428Speter
36525428Speterstruct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
36625428Speter    IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
36725428Speter
36825428Speterstruct ifmedia_description ifm_subtype_tokenring_descriptions[] =
36925428Speter    IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
37025428Speter
37125428Speterstruct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
37225428Speter    IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
37325428Speter
37425428Speterstruct ifmedia_description ifm_subtype_fddi_descriptions[] =
37525428Speter    IFM_SUBTYPE_FDDI_DESCRIPTIONS;
37625428Speter
37725428Speterstruct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
37825428Speter    IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
37925428Speter
38025428Speterstruct ifmedia_description ifm_subtype_shared_descriptions[] =
38125428Speter    IFM_SUBTYPE_SHARED_DESCRIPTIONS;
38225428Speter
38325428Speterstruct ifmedia_description ifm_shared_option_descriptions[] =
38425428Speter    IFM_SHARED_OPTION_DESCRIPTIONS;
38525428Speter
38625428Speterstruct ifmedia_type_to_subtype {
38725428Speter	struct ifmedia_description *subtypes;
38825428Speter	struct ifmedia_description *options;
38925428Speter};
39025428Speter
39125428Speter/* must be in the same order as IFM_TYPE_DESCRIPTIONS */
39225428Speterstruct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
39325428Speter	{
39425428Speter	  &ifm_subtype_ethernet_descriptions[0],
39525428Speter	  &ifm_subtype_ethernet_option_descriptions[0]
39625428Speter	},
39725428Speter	{
39825428Speter	  &ifm_subtype_tokenring_descriptions[0],
39925428Speter	  &ifm_subtype_tokenring_option_descriptions[0]
40025428Speter	},
40125428Speter	{
40225428Speter	  &ifm_subtype_fddi_descriptions[0],
40325428Speter	  &ifm_subtype_fddi_option_descriptions[0]
40425428Speter	},
40525428Speter};
40625428Speter
40725428Speter/*
40825428Speter * print a media word.
40925428Speter */
41025428Speterstatic void
41125428Speterifmedia_printword(ifmw)
41225428Speter	int ifmw;
41325428Speter{
41425428Speter	struct ifmedia_description *desc;
41525428Speter	struct ifmedia_type_to_subtype *ttos;
41625428Speter	int seen_option = 0;
41725428Speter
41825428Speter	/* Find the top-level interface type. */
41925428Speter	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
42025428Speter	    desc->ifmt_string != NULL; desc++, ttos++)
42125428Speter		if (IFM_TYPE(ifmw) == desc->ifmt_word)
42225428Speter			break;
42325428Speter	if (desc->ifmt_string == NULL) {
42425428Speter		printf("<unknown type>\n");
42525428Speter		return;
42625428Speter	}
42725428Speter	printf(desc->ifmt_string);
42825428Speter
42925428Speter	/*
43025428Speter	 * Check for the shared subtype descriptions first, then the
43125428Speter	 * type-specific ones.
43225428Speter	 */
43325428Speter	for (desc = ifm_subtype_shared_descriptions;
43425428Speter	    desc->ifmt_string != NULL; desc++)
43525428Speter		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
43625428Speter			goto got_subtype;
43725428Speter
43825428Speter	for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++)
43925428Speter		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
44025428Speter			break;
44125428Speter	if (desc->ifmt_string == NULL) {
44225428Speter		printf(" <unknown subtype>\n");
44325428Speter		return;
44425428Speter	}
44525428Speter
44625428Speter got_subtype:
44725428Speter	printf(" %s", desc->ifmt_string);
44825428Speter
44925428Speter	/*
45025428Speter	 * Look for shared options.
45125428Speter	 */
45225428Speter	for (desc = ifm_shared_option_descriptions;
45325428Speter	    desc->ifmt_string != NULL; desc++) {
45425428Speter		if (ifmw & desc->ifmt_word) {
45525428Speter			if (seen_option == 0)
45625428Speter				printf(" <");
45725428Speter			printf("%s%s", seen_option++ ? "," : "",
45825428Speter			    desc->ifmt_string);
45925428Speter		}
46025428Speter	}
46125428Speter
46225428Speter	/*
46325428Speter	 * Look for subtype-specific options.
46425428Speter	 */
46525428Speter	for (desc = ttos->options; desc->ifmt_string != NULL; desc++) {
46625428Speter		if (ifmw & desc->ifmt_word) {
46725428Speter			if (seen_option == 0)
46825428Speter				printf(" <");
46925428Speter			printf("%s%s", seen_option++ ? "," : "",
47025428Speter			    desc->ifmt_string);
47125428Speter		}
47225428Speter	}
47325428Speter	printf("%s\n", seen_option ? ">" : "");
47425428Speter}
47525428Speter#endif /* IFMEDIA_DEBUG */
476