Subject: media selection again (a suggestion from BSD/OS)
To: None <tech-net@NetBSD.ORG>
From: Matt Thomas <matt@lkg.dec.com>
List: tech-net
Date: 07/05/1996 14:46:10
The following is from some work that BSDI is going to release as part
of BSD/OS 2.2.  Some of the details have changed but for the most the
following is still accurate.  (If we are lucky, we might be able to
even the BSD/OS code).

Why am I bringing it up here?  Because I'd rather one common way of
doing this for all of the BSD's.

Comments?
-- 
Matt Thomas               Internet:   matt@3am-software.com
3am Software Foundry      WWW URL:    http://www.3am-software.com/bio/matt.html
Westford, MA              Disclaimer: I disavow all knowledge of this message



Network media selection

Problem
-------
	The 3 link flags available to a network driver via ifconfig
do not supply enough information to specify all needed media
selection options on newer network cards. A secondary problem is
that there is no standard for the use of link flags so one must
look up the details for each driver to make use of the card; worse,
some drivers support multiple cards that have different link flag
semantics per card (de).

Goals
-----
	- Provide a way to pass media selection information down to
	  network device drivers in a driver independent way.

	- Provide a way for user mode programs to query the media options
	  available on a given driver.

	- The interface should be extensible to support new types of
	  media and options in the future.

	- Minimal changes to driver code.

	- The old and new systems must coexist (probably indefinitely).

Limitations
------------
	- This is for broadcast media (ethernet, token ring, FDDI) only,
	  point to point interfaces are not addressed.

Design
-------
	A media options word is defined that can unambiguously define a
single way a driver may program a card to connect to the network. This
word is broken up as follows:

  	Bits	Use
  	----	-------
  	0-3	Media variant
  	4	RFU
  	5-7	Network type
  	8-15	Type specific options
  	16-19	RFU
  	20-27	Shared (global) options
  	28-31	Instance

The fields are used for:
    Network type:
	Selects between broad media types (ethernet, token ring, FDDI). This
field currently allows for 8 media types. This field is somewhat
redundant with the if_data.ifi_type field, however putting it here
and possibly allowing it to be set covers the possiblility of a
network interface that supports multiple network types (for instance,
the TI Thunderlan chip can support ethernet and token ring on the
same hardware).

    Media variant:
	The way card is connected over this media. This would typically be
used to choose 10baseT vs. AUI for exmaple. 16 media variants per media
type are currently supported.

    Type specific options:
	A bit vector in which bit meanings are defined by the media type. For
example, this is used to turn on Early Token Release for token ring.

    Global options:
	A bit vector with options that may be applicable to multiple network
types, an example is Full Duplex/Half Duplex.

    Instance:
	Selects between instances of otherwise identical interfaces. For 
example, it is possible (and common) to have an internal and external
10baseT PHY connected to 802.3u MII busses on the newer network interface
chips (Intel, DEC, TI, and 3COM all allow the connection of an MII bus
with several PHY's).

	The placement of the RFU bits is such that the fields on either
side may grow into them as needed; the options word is defined as an
int.

	The definitions for the options word (and the utility routines
that support this interface inside the kernel) are in sys/net/ifmedia.h
(copy below).

>From user mode the interface is via 2 ioctls that are handled at the
device driver:

    SIOCGIFMEDIA:
	Returns current media status and optionally a list of supported media 
	options. The structure below sits in the ifreq.ifr_ifru union (this
	is a bidirectional parameter):
		struct 	ifmediareq {
			char	ifm_name[IFNAMSIZ];
			int	ifm_current;	/* Current media options */
			int	ifm_mask;	/* Don't care mask */
			int	ifm_status;	/* Media status */
			int	ifm_count;	/* # of words in ifm_types */
			union {			/* Return structure */
				caddr_t	ifm_buf;
				int	*ifm_list;
			} ifm_ifmu;
		};

	Ifm_current receives the current media options word.

	Ifm_mask receives the current don't care mask (options that are
		always valid for this interface).

	Ifm_status receives current status, currently there is a single
		flag that indicates the network is attached and appears
		to be active.

	Ifm_count on input is the number of words available for interface
		information in ifm_types. On output contains the number of
		words filled in to ifm_types. If 0 no interface type 
		information is returned, however the number of options
		words needed is still filled in.

	Ifm_list points to an array of media option words (int's) that
		is ifm_count words long. This array will be filled in with
		valid interface option words if ifm_types is non-NULL and
		large enough to hold all the options. If there is not
		enough space an E2BIG is returned (and the array is left
		partially filled); even in this case ifm_count receives
		the number of media words that would be needed.

    SIOCSIFMEDIA:
	This directs driver to switch media options, the
	ifreq.ifr_ifru.ifru_metric word is used to pass in the
	media options word. If the operation fails:
		EIO	- Error attempting change
		ENXIO	- Requested options not available on interface.

Command line interface
----------------------
	The standard command line interface for this feature is via the
ifconfig command. Two types of operations are supported, getting interface
media status and setting the current media options.

	To get status:
		media

	new link types:
		linktype ether
		linktype token
		linktype fddi

	 valid for linktype ether:
		aui, 10base5		- Aui connector
		bnc, 10base2, coax	- 10base2
		10baset, utp, tp	- 10baseT
		100basetx, tx		- 100baseTX, RJ45
		100basefx, fx		- 100baseFX, Fiber
		100baset4, t4		- 100baseT4, 4 pair cat 3
		100vganylan, vg, anylan	- 100VG-AnyLAN

	valid for linktype token:
		stp			- shielded twisted pair
		utp			- unshielded twisted pair
		16mbit, 16m		- 16mbit token ring speed
		4mbit, 4m		- 4mbit token ring speed
		etr, early		- Early token releast
		srt, source_route	- Token ring source routing
		allbc, all_broadcast	- All routes broadcast

	valid for linktype fddi:
		fiber			- Fiber connection
		utp, cddi		- Fddi over copper
		da, dual		- Dual ring attach

	valid for any link type:
		fdx, full_duplex	- Full duplex (to switch)
		flag0, loopback		- Debug
		flag1			- Debug
		flag2			- Debug
		automedia, auto		- Automatically select media
		nomedia, disc		- Detach from current media

	debug use only:
		mopt val		- Pass in raw media options value
		inst num

	An example of 'ifconfig xxN media' output on a 100baseTX de card:
	
de0: ether flags=8863<UP,BROADCAST,NOTRAILERS,RUNNING,SIMPLEX,MULTICAST>
        inet 205.230.226.129 netmask 0xffffff80 broadcast 205.230.226.255

	automedia
	linktype ether 10baset
	linktype ether 100basetx
	linktype ether 100basetx fdx

 ... or for a 3c509 combo:

	linktype ether 10baset
	linktype ether aui
	linktype ether bnc

 ... or for an SMC TokenCard Elite:

	linktype token 16mbit utp
	linktype token 16mbit stp
	linktype token 16mbit utp etr
	linktype token 16mbit stp etr
	linktype token 4mbit utp
	linktype token 4mbit stp
	[ srt allbc ] 

Kernel interface
----------------
	The intent is to require as few changes as possible to
network drivers.  In current drivers media selection is done via
the ioctl entry point and the SIOCSIFFLAGS call and sometimes a
default is chosen during attach time. To use this system a driver
must handle the SIOCGIFMEDIA and SIOSIFMEDIA ioctls.

To minimize changes support routines are available to do most of the
work. To use these a driver must:

	- Include a 'struct ifmedia' (from if.h) in its softc.
	- During attach:
		- Init ifm_mask (don't care) mask in the ifmedia structure
		- Init ifm_change (media change callback)
		- Init ifm_status (media status callback)
		- Add possible media options to the ifmedia structure at attach
		  time via ifmedia_add() or ifmedia_list_add()
	- At ioctl time, if SIOCSIFMEDIA or SIOCGIFMEDIA call ifmedia_ioctl().
	- Provide a callback that ifmedia_ioctl() will call if the interface
		options appear valid and have changed. This is called 
		synchronously from ifmedia_ioctl().
	- Provide a media status callback (also called from ifmedia_ioctl())
		which returns a status word for SIOCGIFMEDIA.

Support routines:
    ifmedia_init(...)

    ifmedia_add(struct ifmedia *mp, int mword, int data, void *aux)
	This adds a supported media configuration to a list kept by the
	support code. 

    ifmedia_list_add(struct ifmedia *mp, struct ifmedia_entry *lp, int count);
	Similar to above but passed the start of an array of mword/data/aux
	elements. Use this if the card supports only a static set of
	options (the ifmedia list can be initialized by the compiler). The
	link fields in the ifmedia_entries can be left uninitialized.

    ifmedia_ioctl(struct ifnet *ifp, struct ifreq *ifr, int cmd,
      ifmedia_cb_t *cbf)
	Handle SIOCSIFMEDIA and SIOCGIFMEDIA. Return value should be returned
	to caller. The callback function is called with the old and new media
	options and may reject the change by returning an errno.

	For SIOCSIFMEDIA, the ioctl routine masks the don't care
	bits (from ifm_mask) then tries to find a matching ifmedia_entry
	entry. If one is found the driver is called back with a pointer
	to the current and new entries.
    
    The callback routines are called with an ifp pointer. The driver
    may examine the ifm_cur and ifm_media fields in the ifmedia structure
    to determine the current state of the media. The media change callback
    is not called if the media word has not changed.

Alternatives
--------------
- Expanding if_flags to an int from a short would provide an additional
  16 bits to work with, however this has the following drawbacks:

	- 16 bits do not leave space for expansion while keeping the bit
	  definitions fairly generic.

	- The if_flags field is already overloaded with a mixture of user
	  supplied and driver internal data.

	- A seperate interface would still need to be created to
	  allow a user mode program to query what network interfaces
	  were available.

- Using a sockaddr_dl and SIOCSIFADDR provides a nice method to deliver
  option words to the device driver, however this would overload
  the (future) use of this function for altering MAC addresses on
  interfaces.  Also, the interface via getifaddrs/sysctl would
  require numerous addresses to be maintained for the various media
  selection possibilities, which is somewhat kludgy.

Include files
-------------------

sys/net/if_media.h:

/*
 * Options word:
 *	Bits	Use
 *	----	-------
 *	0-3	Media variant
 *	4	RFU
 *	5-7	Media type
 *	8-15	Type specific options
 *	16-19	RFU
 *	20-27	Shared (global) options
 *	28-31	Instance
 */

/*
 * Ethernet
 */
#define IFM_ETHER	0x00000020
#define	IFM_10_T	0		/* 10BaseT - RJ45 */
#define	IFM_10_2	1		/* 10Base2 - Thinnet */
#define	IFM_10_5	2		/* 10Base5 - AUI */
#define	IFM_100_TX	3		/* 100BaseTX - RJ45 */
#define	IFM_100_FX	4		/* 100BaseFX - Fiber */
#define	IFM_100_T4	5		/* 100BaseT4 - 4 pair cat 3 */
#define	IFM_100_VG	6		/* 100VG-AnyLAN */

/*
 * Token ring
 */
#define	IFM_TOKEN	0x00000040
#define	IFM_TOK_STP	0 		/* Shielded twisted pair - DB9 */
#define IFM_TOK_UTP	1		/* Unshielded twisted pair - RJ45 */
#define IFM_TOK_16	0x00000100	/* 16mbit / 4 mbit */
#define	IFM_TOK_ETR	0x00000200	/* Early token release */
#define	IFM_TOK_SRCRT	0x00000400	/* Enable source routing features */
#define	IFM_TOK_ALLR	0x00000800	/* All routes / Single route bcast */

/*
 * FDDI
 */
#define	IFM_FDDI	0x00000060
#define	IFM_FDDI_FIBER	0		/* Fiber */
#define IFM_FDDI_UTP	1		/* CDDI / UTP */
#define IFM_FDDI_DA	0x00000100	/* Dual attach / single attach */

/*
 * Shared options
 */
#define IFM_FDX		0x00100000	/* Full duplex / half duplex */
#define IFM_FLAG0	0x01000000	/* Driver defined flag */
#define IFM_FLAG1	0x02000000	/* Driver defined flag */
#define IFM_FLAG2	0x04000000	/* Driver defined flag */

/*
 * Masks
 */
#define	IFM_NMASK	0x000000e0	/* Network type */
#define	IFM_TMASK	0x0000000f	/* Media sub-type */
#define	IFM_IMASK	0xf0000000	/* Instance */

/* Special media types */
#define	IFM_AUTOSEL	0x00000000	/* Autoselect best interface */
#define	IFM_NONE	0xffffffff	/* No/unknown interface selected */

/*
 * Status bits
 */
#define	IFM_ACTIVE	0x00000001	/* Interface attached to working net */


------------------------

Some notes on porting drivers to the new system:


	- Include net/if_media.h:
		#include <net/if.h>
		#include <net/if_media.h>
		#include <net/netisr.h>

	- Add the if_media structure to the softc:
	        struct arpcom exp_ac;           /* per-interface network data */
		struct ifmedia exp_media;       /* media settings */
		struct atshutdown exp_ats;      /* shutdown vector */

	- In the attach routine:
	    Init the media structure:
		/* Publish our media options */
		ifmedia_init(&sc->exp_media, IFM_FDX, expmediachg,
		    expmediastat);

		The mask argument indicates options that may be set or 
		cleared for any of the registered media subtypes.

	    Register each media possibility:
		ifmedia_add(&sc->exp_media, IFM_ETHER | IFM_AUTO, 0, NULL);
		[...]

		ifmedia_add() each unique supported media, don't add each
		possibility involving changes in flags masked by the
		don't care mask passed to ifmedia_init().

		If an option bit does not apply to all media then an
		entry should be made for all valid media types with and w/o
		the option bit selected.

	    Set the default media:
		ifmedia_set(&sc->exp_media, IFM_ETHER | IFM_AUTO);

		Call ifmedia_set() to set the default (init) media. This
		does not do the callback. It is an error (panic) to call
		this for media settings that have not been registered.

	- Find all areas in driver where it was using LINK flags (or you
		would like to allow options to be set). At these locations
		substitute checks of ifp->if_flags with tests of
		sc->xx_media.ifm_media, sc->xx_media.ifm_cur->ifm_data, or
		sc->xx_media.ifm_cur->ifm_aux (the latter two are set during
		the ifmedia_add call). Example:

		Before (in xxinit()):
			if ((ifp->if_flags & IFF_LINK0) != 0) {
				[set full duplex mode in hardware]
			}

		After:
			if ((sc->xx_media.ifm_media & IFM_FDX) != 0) {
				[set full duplex mode in hardware]
			}

	- Add the ioctls to the xxioctl routine:

		case SIOCSIFMEDIA:
		case SIOCGIFMEDIA:
			error = ifmedia_ioctl(ifp, (struct ifreq *)data, 
			    &sc->xx_media, cmd);
			break;

	- Write a media change routine, generally this will just reinit
		the interface with the new flags as determined by 
		ifmedia.ifm_media. If additional data is needed 
		ifmedia.ifm_cur points to the ifmedia_entry with that
		caused a match. A common error has been to call the
		init routine here even if the interface is not running.
		It is permissible for a media change operation to
		fail with an errno. Ex:

			/* Media change callback */
			int
			exp_mediachg(struct ifnet *ifp)
			{
				exp_softc_t *sc = expcd.cd_devs[ifp->if_unit];

				if (ifp->if_flags & IFF_UP)  
					exp_init(ifp->if_unit);
				return (0);
			}
	

	- Write a media status routine. This can be the trickiest part of
	 	the change due to the difficulty some hardware has in giving
		accurate status. It is preferrable to return an answer of			"I don't know" 
(0) over possibly reporting an erroneous
		status. If the hardware supports checking the state of
		its connection to the media in realtime then IFM_AVALID
		should be returned ored with IFM_ACTIVE if the interface
		is on the network or alone if not.

		Keep in mind this routine can be called any time
		the ioctl routine is called (including before the
		interface is initialized).

		For MII (802.3u) compliant drivers this routine may
		have to talk with a subordinate device driver or the
		hardware to request status from the PHY. Care should
		taken that this does not disrupt normal operations that
		may be in progress.