if_media.c revision 25431
125428Speter/*	$NetBSD: if_media.c,v 1.1 1997/03/17 02:55:15 thorpej Exp $	*/
225431Speter/*	$Id$ */
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/errno.h>
5225428Speter#include <sys/socket.h>
5325431Speter#include <sys/sockio.h>
5425428Speter#include <sys/malloc.h>
5525428Speter
5625428Speter#include <net/if.h>
5725428Speter#include <net/if_media.h>
5825428Speter#include <net/netisr.h>
5925428Speter
6025428Speter/*
6125428Speter * Compile-time options:
6225428Speter * IFMEDIA_DEBUG:
6325428Speter *	turn on implementation-level debug printfs.
6425428Speter * 	Useful for debugging newly-ported  drivers.
6525428Speter */
6625428Speter
6725428Speterstruct ifmedia_entry *ifmedia_match __P((struct ifmedia *ifm,
6825428Speter    int flags, int mask));
6925428Speter
7025428Speter#ifdef IFMEDIA_DEBUG
7125428Speterint	ifmedia_debug = 0;
7225428Speterstatic	void ifmedia_printword __P((int));
7325428Speter#endif
7425428Speter
7525428Speter/*
7625428Speter * Initialize if_media struct for a specific interface instance.
7725428Speter */
7825428Spetervoid
7925428Speterifmedia_init(ifm, dontcare_mask, change_callback, status_callback)
8025428Speter	struct ifmedia *ifm;
8125428Speter	int dontcare_mask;
8225428Speter	ifm_change_cb_t change_callback;
8325428Speter	ifm_stat_cb_t status_callback;
8425428Speter{
8525428Speter
8625428Speter	LIST_INIT(&ifm->ifm_list);
8725428Speter	ifm->ifm_cur = NULL;
8825428Speter	ifm->ifm_media = 0;
8925428Speter	ifm->ifm_mask = dontcare_mask;		/* IF don't-care bits */
9025428Speter	ifm->ifm_change = change_callback;
9125428Speter	ifm->ifm_status = status_callback;
9225428Speter}
9325428Speter
9425428Speter/*
9525428Speter * Add a media configuration to the list of supported media
9625428Speter * for a specific interface instance.
9725428Speter */
9825428Spetervoid
9925428Speterifmedia_add(ifm, mword, data, aux)
10025428Speter	struct ifmedia *ifm;
10125428Speter	int mword;
10225428Speter	int data;
10325428Speter	void *aux;
10425428Speter{
10525428Speter	register struct ifmedia_entry *entry;
10625428Speter
10725428Speter#ifdef IFMEDIA_DEBUG
10825428Speter	if (ifmedia_debug) {
10925428Speter		if (ifm == NULL) {
11025428Speter			printf("ifmedia_add: null ifm\n");
11125428Speter			return;
11225428Speter		}
11325428Speter		printf("Adding entry for ");
11425428Speter		ifmedia_printword(mword);
11525428Speter	}
11625428Speter#endif
11725428Speter
11825428Speter	entry = malloc(sizeof(*entry), M_IFADDR, M_NOWAIT);
11925428Speter	if (entry == NULL)
12025428Speter		panic("ifmedia_add: can't malloc entry");
12125428Speter
12225428Speter	entry->ifm_media = mword;
12325428Speter	entry->ifm_data = data;
12425428Speter	entry->ifm_aux = aux;
12525428Speter
12625428Speter	LIST_INSERT_HEAD(&ifm->ifm_list, entry, ifm_list);
12725428Speter}
12825428Speter
12925428Speter/*
13025428Speter * Add an array of media configurations to the list of
13125428Speter * supported media for a specific interface instance.
13225428Speter */
13325428Spetervoid
13425428Speterifmedia_list_add(ifm, lp, count)
13525428Speter	struct ifmedia *ifm;
13625428Speter	struct ifmedia_entry *lp;
13725428Speter	int count;
13825428Speter{
13925428Speter	int i;
14025428Speter
14125428Speter	for (i = 0; i < count; i++)
14225428Speter		ifmedia_add(ifm, lp[i].ifm_media, lp[i].ifm_data,
14325428Speter		    lp[i].ifm_aux);
14425428Speter}
14525428Speter
14625428Speter/*
14725428Speter * Set the default active media.
14825428Speter *
14925428Speter * Called by device-specific code which is assumed to have already
15025428Speter * selected the default media in hardware.  We do _not_ call the
15125428Speter * media-change callback.
15225428Speter */
15325428Spetervoid
15425428Speterifmedia_set(ifm, target)
15525428Speter	struct ifmedia *ifm;
15625428Speter	int target;
15725428Speter
15825428Speter{
15925428Speter	struct ifmedia_entry *match;
16025428Speter
16125428Speter	match = ifmedia_match(ifm, target, ifm->ifm_mask);
16225428Speter
16325428Speter	if (match == NULL) {
16425428Speter		printf("ifmedia_set: no match for 0x%x/0x%x\n",
16525428Speter		    target, ~ifm->ifm_mask);
16625428Speter		panic("ifmedia_set");
16725428Speter	}
16825428Speter	ifm->ifm_cur = match;
16925428Speter
17025428Speter#ifdef IFMEDIA_DEBUG
17125428Speter	if (ifmedia_debug) {
17225428Speter		printf("ifmedia_set: target ");
17325428Speter		ifmedia_printword(target);
17425428Speter		printf("ifmedia_set: setting to ");
17525428Speter		ifmedia_printword(ifm->ifm_cur->ifm_media);
17625428Speter	}
17725428Speter#endif
17825428Speter}
17925428Speter
18025428Speter/*
18125428Speter * Device-independent media ioctl support function.
18225428Speter */
18325428Speterint
18425428Speterifmedia_ioctl(ifp, ifr, ifm, cmd)
18525428Speter	struct ifnet *ifp;
18625428Speter	struct ifreq *ifr;
18725428Speter	struct ifmedia *ifm;
18825428Speter	u_long cmd;
18925428Speter{
19025428Speter	struct ifmedia_entry *match;
19125428Speter	struct ifmediareq *ifmr = (struct ifmediareq *) ifr;
19225428Speter	int error = 0, sticky;
19325428Speter
19425428Speter	if (ifp == NULL || ifr == NULL || ifm == NULL)
19525428Speter		return(EINVAL);
19625428Speter
19725428Speter	switch (cmd) {
19825428Speter
19925428Speter	/*
20025428Speter	 * Set the current media.
20125428Speter	 */
20225428Speter	case  SIOCSIFMEDIA:
20325428Speter	{
20425428Speter		struct ifmedia_entry *oldentry;
20525428Speter		int oldmedia;
20625428Speter		int newmedia = ifr->ifr_media;
20725428Speter
20825428Speter		match = ifmedia_match(ifm, newmedia, ifm->ifm_mask);
20925428Speter		if (match == NULL) {
21025428Speter#ifdef IFMEDIA_DEBUG
21125428Speter			if (ifmedia_debug) {
21225428Speter				printf(
21325428Speter				    "ifmedia_ioctl: no media found for 0x%x\n",
21425428Speter				    newmedia);
21525428Speter			}
21625428Speter#endif
21725428Speter			return (ENXIO);
21825428Speter		}
21925428Speter
22025428Speter		/*
22125428Speter		 * If no change, we're done.
22225428Speter		 * XXX Automedia may invole software intervention.
22325428Speter		 *     Keep going in case the the connected media changed.
22425428Speter		 *     Similarly, if best match changed (kernel debugger?).
22525428Speter		 */
22625428Speter		if ((IFM_SUBTYPE(newmedia) != IFM_AUTO) &&
22725428Speter		    (newmedia == ifm->ifm_media) &&
22825428Speter		    (match == ifm->ifm_cur))
22925428Speter			return 0;
23025428Speter
23125428Speter		/*
23225428Speter		 * We found a match, now make the driver switch to it.
23325428Speter		 * Make sure to preserve our old media type in case the
23425428Speter		 * driver can't switch.
23525428Speter		 */
23625428Speter#ifdef IFMEDIA_DEBUG
23725428Speter		if (ifmedia_debug) {
23825428Speter			printf("ifmedia_ioctl: switching %s to ",
23925428Speter			    ifp->if_xname);
24025428Speter			ifmedia_printword(match->ifm_media);
24125428Speter		}
24225428Speter#endif
24325428Speter		oldentry = ifm->ifm_cur;
24425428Speter		oldmedia = ifm->ifm_media;
24525428Speter		ifm->ifm_cur = match;
24625428Speter		ifm->ifm_media = newmedia;
24725428Speter		error = (*ifm->ifm_change)(ifp);
24825428Speter		if (error) {
24925428Speter			ifm->ifm_cur = oldentry;
25025428Speter			ifm->ifm_media = oldmedia;
25125428Speter		}
25225428Speter		break;
25325428Speter	}
25425428Speter
25525428Speter	/*
25625428Speter	 * Get list of available media and current media on interface.
25725428Speter	 */
25825428Speter	case  SIOCGIFMEDIA:
25925428Speter	{
26025428Speter		struct ifmedia_entry *ep;
26125428Speter		int *kptr, count;
26225428Speter
26325428Speter		kptr = NULL;		/* XXX gcc */
26425428Speter
26525428Speter		ifmr->ifm_active = ifmr->ifm_current = ifm->ifm_cur ?
26625428Speter		    ifm->ifm_cur->ifm_media : IFM_NONE;
26725428Speter		ifmr->ifm_mask = ifm->ifm_mask;
26825428Speter		ifmr->ifm_status = 0;
26925428Speter		(*ifm->ifm_status)(ifp, ifmr);
27025428Speter
27125428Speter		count = 0;
27225428Speter		ep = ifm->ifm_list.lh_first;
27325428Speter
27425428Speter		if (ifmr->ifm_count != 0) {
27525428Speter			kptr = (int *)malloc(ifmr->ifm_count * sizeof(int),
27625428Speter			    M_TEMP, M_WAITOK);
27725428Speter
27825428Speter			/*
27925428Speter			 * Get the media words from the interface's list.
28025428Speter			 */
28125428Speter			for (; ep != NULL && count < ifmr->ifm_count;
28225428Speter			    ep = ep->ifm_list.le_next, count++)
28325428Speter				kptr[count] = ep->ifm_media;
28425428Speter
28525428Speter			if (ep != NULL)
28625428Speter				error = E2BIG;	/* oops! */
28725428Speter		}
28825428Speter
28925428Speter		/*
29025428Speter		 * If there are more interfaces on the list, count
29125428Speter		 * them.  This allows the caller to set ifmr->ifm_count
29225428Speter		 * to 0 on the first call to know how much space to
29325428Speter		 * callocate.
29425428Speter		 */
29525428Speter		for (; ep != NULL; ep = ep->ifm_list.le_next)
29625428Speter			count++;
29725428Speter
29825428Speter		/*
29925428Speter		 * We do the copyout on E2BIG, because that's
30025428Speter		 * just our way of telling userland that there
30125428Speter		 * are more.  This is the behavior I've observed
30225428Speter		 * under BSD/OS 3.0
30325428Speter		 */
30425428Speter		sticky = error;
30525428Speter		if ((error == 0 || error == E2BIG) && ifmr->ifm_count != 0) {
30625428Speter			error = copyout((caddr_t)kptr,
30725428Speter			    (caddr_t)ifmr->ifm_ulist,
30825428Speter			    ifmr->ifm_count * sizeof(int));
30925428Speter		}
31025428Speter
31125428Speter		if (error == 0)
31225428Speter			error = sticky;
31325428Speter
31425428Speter		if (ifmr->ifm_count != 0)
31525428Speter			free(kptr, M_TEMP);
31625428Speter
31725428Speter		ifmr->ifm_count = count;
31825428Speter		break;
31925428Speter	}
32025428Speter
32125428Speter	default:
32225428Speter		return (EINVAL);
32325428Speter	}
32425428Speter
32525428Speter	return (error);
32625428Speter}
32725428Speter
32825428Speter/*
32925428Speter * Find media entry matching a given ifm word.
33025428Speter *
33125428Speter */
33225428Speterstruct ifmedia_entry *
33325428Speterifmedia_match(ifm, target, mask)
33425428Speter	struct ifmedia *ifm;
33525428Speter	int target;
33625428Speter	int mask;
33725428Speter{
33825428Speter	struct ifmedia_entry *match, *next;
33925428Speter
34025428Speter	match = NULL;
34125428Speter	mask = ~mask;
34225428Speter
34325428Speter	for (next = ifm->ifm_list.lh_first; next != NULL;
34425428Speter	    next = next->ifm_list.le_next) {
34525428Speter		if ((next->ifm_media & mask) == (target & mask)) {
34625428Speter#if defined(IFMEDIA_DEBUG) || defined(DIAGNOSTIC)
34725428Speter			if (match) {
34825428Speter				printf("ifmedia_match: multiple match for "
34925428Speter				    "0x%x/0x%x\n", target, mask);
35025428Speter			}
35125428Speter#endif
35225428Speter			match = next;
35325428Speter		}
35425428Speter	}
35525428Speter
35625428Speter	return match;
35725428Speter}
35825428Speter
35925428Speter#ifdef IFMEDIA_DEBUG
36025428Speterstruct ifmedia_description ifm_type_descriptions[] =
36125428Speter    IFM_TYPE_DESCRIPTIONS;
36225428Speter
36325428Speterstruct ifmedia_description ifm_subtype_ethernet_descriptions[] =
36425428Speter    IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
36525428Speter
36625428Speterstruct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
36725428Speter    IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
36825428Speter
36925428Speterstruct ifmedia_description ifm_subtype_tokenring_descriptions[] =
37025428Speter    IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
37125428Speter
37225428Speterstruct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
37325428Speter    IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
37425428Speter
37525428Speterstruct ifmedia_description ifm_subtype_fddi_descriptions[] =
37625428Speter    IFM_SUBTYPE_FDDI_DESCRIPTIONS;
37725428Speter
37825428Speterstruct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
37925428Speter    IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
38025428Speter
38125428Speterstruct ifmedia_description ifm_subtype_shared_descriptions[] =
38225428Speter    IFM_SUBTYPE_SHARED_DESCRIPTIONS;
38325428Speter
38425428Speterstruct ifmedia_description ifm_shared_option_descriptions[] =
38525428Speter    IFM_SHARED_OPTION_DESCRIPTIONS;
38625428Speter
38725428Speterstruct ifmedia_type_to_subtype {
38825428Speter	struct ifmedia_description *subtypes;
38925428Speter	struct ifmedia_description *options;
39025428Speter};
39125428Speter
39225428Speter/* must be in the same order as IFM_TYPE_DESCRIPTIONS */
39325428Speterstruct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
39425428Speter	{
39525428Speter	  &ifm_subtype_ethernet_descriptions[0],
39625428Speter	  &ifm_subtype_ethernet_option_descriptions[0]
39725428Speter	},
39825428Speter	{
39925428Speter	  &ifm_subtype_tokenring_descriptions[0],
40025428Speter	  &ifm_subtype_tokenring_option_descriptions[0]
40125428Speter	},
40225428Speter	{
40325428Speter	  &ifm_subtype_fddi_descriptions[0],
40425428Speter	  &ifm_subtype_fddi_option_descriptions[0]
40525428Speter	},
40625428Speter};
40725428Speter
40825428Speter/*
40925428Speter * print a media word.
41025428Speter */
41125428Speterstatic void
41225428Speterifmedia_printword(ifmw)
41325428Speter	int ifmw;
41425428Speter{
41525428Speter	struct ifmedia_description *desc;
41625428Speter	struct ifmedia_type_to_subtype *ttos;
41725428Speter	int seen_option = 0;
41825428Speter
41925428Speter	/* Find the top-level interface type. */
42025428Speter	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
42125428Speter	    desc->ifmt_string != NULL; desc++, ttos++)
42225428Speter		if (IFM_TYPE(ifmw) == desc->ifmt_word)
42325428Speter			break;
42425428Speter	if (desc->ifmt_string == NULL) {
42525428Speter		printf("<unknown type>\n");
42625428Speter		return;
42725428Speter	}
42825428Speter	printf(desc->ifmt_string);
42925428Speter
43025428Speter	/*
43125428Speter	 * Check for the shared subtype descriptions first, then the
43225428Speter	 * type-specific ones.
43325428Speter	 */
43425428Speter	for (desc = ifm_subtype_shared_descriptions;
43525428Speter	    desc->ifmt_string != NULL; desc++)
43625428Speter		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
43725428Speter			goto got_subtype;
43825428Speter
43925428Speter	for (desc = ttos->subtypes; desc->ifmt_string != NULL; desc++)
44025428Speter		if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
44125428Speter			break;
44225428Speter	if (desc->ifmt_string == NULL) {
44325428Speter		printf(" <unknown subtype>\n");
44425428Speter		return;
44525428Speter	}
44625428Speter
44725428Speter got_subtype:
44825428Speter	printf(" %s", desc->ifmt_string);
44925428Speter
45025428Speter	/*
45125428Speter	 * Look for shared options.
45225428Speter	 */
45325428Speter	for (desc = ifm_shared_option_descriptions;
45425428Speter	    desc->ifmt_string != NULL; desc++) {
45525428Speter		if (ifmw & desc->ifmt_word) {
45625428Speter			if (seen_option == 0)
45725428Speter				printf(" <");
45825428Speter			printf("%s%s", seen_option++ ? "," : "",
45925428Speter			    desc->ifmt_string);
46025428Speter		}
46125428Speter	}
46225428Speter
46325428Speter	/*
46425428Speter	 * Look for subtype-specific options.
46525428Speter	 */
46625428Speter	for (desc = ttos->options; desc->ifmt_string != NULL; desc++) {
46725428Speter		if (ifmw & desc->ifmt_word) {
46825428Speter			if (seen_option == 0)
46925428Speter				printf(" <");
47025428Speter			printf("%s%s", seen_option++ ? "," : "",
47125428Speter			    desc->ifmt_string);
47225428Speter		}
47325428Speter	}
47425428Speter	printf("%s\n", seen_option ? ">" : "");
47525428Speter}
47625428Speter#endif /* IFMEDIA_DEBUG */
477