Subject: media selection again (a suggestion from BSD/OS)
To: None <tech-net@NetBSD.ORG>
From: Matt Thomas <matt@lkg.dec.com>
List: current-users
Date: 02/15/1997 12:07:18
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.