diff options
author | phk <phk@FreeBSD.org> | 2001-05-26 09:27:08 +0000 |
---|---|---|
committer | phk <phk@FreeBSD.org> | 2001-05-26 09:27:08 +0000 |
commit | 1aacce7a450370feff7a5e098c12e915e4f731f8 (patch) | |
tree | a706c2fc726d426b9f75667c81d6227ef2f9ea18 /sys/dev | |
parent | 2072a71f0e5186928f3cb8cfac61fe8199029442 (diff) | |
download | FreeBSD-src-1aacce7a450370feff7a5e098c12e915e4f731f8.zip FreeBSD-src-1aacce7a450370feff7a5e098c12e915e4f731f8.tar.gz |
Currently, each wireless networking driver has it's own control program
despite the fact that most people want to set exactly the same settings
regardless of which card they have. It has been repeatidly suggested
that this configuration should be done via ifconfig. This patch
implements the required functionality in ifconfig and add support to the
wi and an drivers. It also provides partial, untested support for the
awi driver.
PR: 25577
Submitted by: Brooks Davis <brooks@one-eyed-alien.net>
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/an/if_aironet_ieee.h | 2 | ||||
-rw-r--r-- | sys/dev/an/if_an.c | 500 | ||||
-rw-r--r-- | sys/dev/an/if_an_isa.c | 2 | ||||
-rw-r--r-- | sys/dev/an/if_an_pccard.c | 2 | ||||
-rw-r--r-- | sys/dev/an/if_an_pci.c | 2 | ||||
-rw-r--r-- | sys/dev/an/if_anreg.h | 5 | ||||
-rw-r--r-- | sys/dev/awi/awi.c | 181 | ||||
-rw-r--r-- | sys/dev/wi/if_wavelan_ieee.h | 4 | ||||
-rw-r--r-- | sys/dev/wi/if_wi.c | 376 |
9 files changed, 1068 insertions, 6 deletions
diff --git a/sys/dev/an/if_aironet_ieee.h b/sys/dev/an/if_aironet_ieee.h index bcbd32f..a1eeb26 100644 --- a/sys/dev/an/if_aironet_ieee.h +++ b/sys/dev/an/if_aironet_ieee.h @@ -336,6 +336,7 @@ struct an_ltv_genconfig { u_int8_t an_magic_packet_ctl; /* 0x99 */ u_int16_t an_rsvd9; }; +#endif #define AN_OPMODE_IBSS_ADHOC 0x0000 #define AN_OPMODE_INFRASTRUCTURE_STATION 0x0001 @@ -395,6 +396,7 @@ struct an_ltv_genconfig { #define AN_HOME_NETWORK 0x0001 #define AN_HOME_INSTALL_AP 0x0002 +#ifndef _KERNEL struct an_ltv_ssidlist { u_int16_t an_len; u_int16_t an_type; diff --git a/sys/dev/an/if_an.c b/sys/dev/an/if_an.c index 2b38284..c7843e6 100644 --- a/sys/dev/an/if_an.c +++ b/sys/dev/an/if_an.c @@ -113,6 +113,8 @@ #include <net/ethernet.h> #include <net/if_dl.h> #include <net/if_types.h> +#include <net/if_ieee80211.h> +#include <net/if_media.h> #ifdef INET #include <netinet/in.h> @@ -160,6 +162,9 @@ static void an_cache_store __P((struct an_softc *, struct ether_header *, struct mbuf *, unsigned short)); #endif +static int an_media_change __P((struct ifnet *)); +static void an_media_status __P((struct ifnet *, struct ifmediareq *)); + /* * We probe for an Aironet 4500/4800 card by attempting to * read the default SSID list. On reset, the first entry in @@ -380,6 +385,31 @@ int an_attach(sc, unit, flags) sc->an_tx_rate = 0; bzero((char *)&sc->an_stats, sizeof(sc->an_stats)); + ifmedia_init(&sc->an_ifmedia, 0, an_media_change, an_media_status); +#define ADD(m, c) ifmedia_add(&sc->an_ifmedia, (m), (c), NULL) + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0); + if(sc->an_caps.an_rates[2] == AN_RATE_5_5MBPS) { + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0); + } + if(sc->an_caps.an_rates[3] == AN_RATE_11MBPS) { + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0); + } + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0); +#undef ADD + ifmedia_set(&sc->an_ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, + 0, 0)); + /* * Call MI attach routine. */ @@ -713,10 +743,10 @@ static int an_read_record(sc, ltv) * room to hold all of the returned data). */ len = CSR_READ_2(sc, AN_DATA1); - if (len > ltv->an_len) { + if (len > (ltv->an_len - 2)) { printf("an%d: record length mismatch -- expected %d, " - "got %d\n", sc->an_unit, ltv->an_len, len); - return(ENOSPC); + "got %d\n", sc->an_unit, (ltv->an_len - 2), len); + len = (ltv->an_len - 2); } ltv->an_len = len; @@ -981,14 +1011,29 @@ static int an_ioctl(ifp, command, data) caddr_t data; { int error = 0; + int len; + int i; struct an_softc *sc; struct an_req areq; struct ifreq *ifr; struct proc *p = curproc; + struct ieee80211req *ireq; + u_int8_t tmpstr[IEEE80211_NWID_LEN*2]; + u_int8_t *tmpptr; + struct an_ltv_genconfig *config; + struct an_ltv_key *key; + struct an_ltv_status *status; + struct an_ltv_ssidlist *ssids; sc = ifp->if_softc; AN_LOCK(sc); ifr = (struct ifreq *)data; + ireq = (struct ieee80211req *)data; + + config = (struct an_ltv_genconfig *)&areq; + key = (struct an_ltv_key *)&areq; + status = (struct an_ltv_status *)&areq; + ssids = (struct an_ltv_ssidlist *)&areq; if (sc->an_gone) { error = ENODEV; @@ -1020,6 +1065,10 @@ static int an_ioctl(ifp, command, data) sc->an_if_flags = ifp->if_flags; error = 0; break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->an_ifmedia, command); + break; case SIOCADDMULTI: case SIOCDELMULTI: /* The Aironet has no multicast filter. */ @@ -1059,6 +1108,369 @@ static int an_ioctl(ifp, command, data) break; an_setdef(sc, &areq); break; + case SIOCG80211: + areq.an_len = sizeof(areq); + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + if(ireq->i_val == -1) { + areq.an_type = AN_RID_STATUS; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + len = status->an_ssidlen; + tmpptr = status->an_ssid; + } else if(ireq->i_val >= 0) { + areq.an_type = AN_RID_SSIDLIST; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + if(ireq->i_val == 0) { + len = ssids->an_ssid1_len; + tmpptr = ssids->an_ssid1; + } else if(ireq->i_val == 1) { + len = ssids->an_ssid2_len; + tmpptr = ssids->an_ssid3; + } else if(ireq->i_val == 1) { + len = ssids->an_ssid3_len; + tmpptr = ssids->an_ssid3; + } else { + error = EINVAL; + break; + } + } else { + error = EINVAL; + break; + } + if(len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + ireq->i_len = len; + bzero(tmpstr, IEEE80211_NWID_LEN); + bcopy(tmpptr, tmpstr, len); + error = copyout(tmpstr, ireq->i_data, + IEEE80211_NWID_LEN); + break; + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 3; + break; + case IEEE80211_IOC_WEP: + areq.an_type = AN_RID_ACTUALCFG; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + if(config->an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE) { + if(config->an_authtype & + AN_AUTHTYPE_ALLOW_UNENCRYPTED) + ireq->i_val = IEEE80211_WEP_MIXED; + else + ireq->i_val = IEEE80211_WEP_ON; + + } else { + ireq->i_val = IEEE80211_WEP_OFF; + } + break; + case IEEE80211_IOC_WEPKEY: + /* + * XXX: I'm not entierly convinced this is + * correct, but it's what is implemented in + * ancontrol so it will have to do until we get + * access to actual Cisco code. + */ + if(ireq->i_val < 0 || ireq->i_val > 7) { + error = EINVAL; + break; + } + len = 0; + if(ireq->i_val < 4) { + areq.an_type = AN_RID_WEP_TEMP; + for(i=0; i<4; i++) { + areq.an_len = sizeof(areq); + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + len = key->klen; + if(i == ireq->i_val) + break; + /* Required to get next entry */ + areq.an_type = AN_RID_WEP_PERM; + } + if(error) + break; + } + /* We aren't allowed to read the value of the + * key from the card so we just output zeros + * like we would if we could read the card, but + * denied the user access. + */ + bzero(tmpstr, len); + ireq->i_len = len; + error = copyout(tmpstr, ireq->i_data, len); + break; + case IEEE80211_IOC_NUMWEPKEYS: + ireq->i_val = 8; + break; + case IEEE80211_IOC_WEPTXKEY: + areq.an_type = AN_RID_WEP_PERM; + key->kindex = 0xffff; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + ireq->i_val = key->mac[0]; + break; + case IEEE80211_IOC_AUTHMODE: + areq.an_type = AN_RID_ACTUALCFG; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + if ((config->an_authtype & AN_AUTHTYPE_MASK) == + AN_AUTHTYPE_NONE) { + ireq->i_val = IEEE80211_AUTH_NONE; + } else if ((config->an_authtype & AN_AUTHTYPE_MASK) == + AN_AUTHTYPE_OPEN) { + ireq->i_val = IEEE80211_AUTH_OPEN; + } else if ((config->an_authtype & AN_AUTHTYPE_MASK) == + AN_AUTHTYPE_SHAREDKEY) { + ireq->i_val = IEEE80211_AUTH_SHARED; + } else + error = EINVAL; + break; + case IEEE80211_IOC_STATIONNAME: + areq.an_type = AN_RID_ACTUALCFG; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + ireq->i_len = sizeof(config->an_nodename); + tmpptr = config->an_nodename; + bzero(tmpstr, IEEE80211_NWID_LEN); + bcopy(tmpptr, tmpstr, ireq->i_len); + error = copyout(tmpstr, ireq->i_data, + IEEE80211_NWID_LEN); + break; + case IEEE80211_IOC_CHANNEL: + areq.an_type = AN_RID_STATUS; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + ireq->i_val = status->an_cur_channel; + break; + case IEEE80211_IOC_POWERSAVE: + areq.an_type = AN_RID_ACTUALCFG; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + if(config->an_psave_mode == AN_PSAVE_NONE) { + ireq->i_val = IEEE80211_POWERSAVE_OFF; + } else if(config->an_psave_mode == AN_PSAVE_CAM) { + ireq->i_val = IEEE80211_POWERSAVE_CAM; + } else if(config->an_psave_mode == AN_PSAVE_PSP) { + ireq->i_val = IEEE80211_POWERSAVE_PSP; + } else if(config->an_psave_mode == AN_PSAVE_PSP_CAM) { + ireq->i_val = IEEE80211_POWERSAVE_PSP_CAM; + } else + error = EINVAL; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + areq.an_type = AN_RID_ACTUALCFG; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + ireq->i_val = config->an_listen_interval; + break; + } + break; + case SIOCS80211: + if ((error = suser(p))) + goto out; + areq.an_len = sizeof(areq); + /* + * We need a config structure for everything but the WEP + * key management and SSIDs so we get it now so avoid + * duplicating this code every time. + */ + if (ireq->i_type != IEEE80211_IOC_SSID && + ireq->i_type != IEEE80211_IOC_WEPKEY && + ireq->i_type != IEEE80211_IOC_WEPTXKEY) { + areq.an_type = AN_RID_GENCONFIG; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + } + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + areq.an_type = AN_RID_SSIDLIST; + if (an_read_record(sc, + (struct an_ltv_gen *)&areq)) { + error = EINVAL; + break; + } + if(ireq->i_len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + switch (ireq->i_val) { + case 0: + error = copyin(ireq->i_data, + ssids->an_ssid1, ireq->i_len); + ssids->an_ssid1_len = ireq->i_len; + break; + case 1: + error = copyin(ireq->i_data, + ssids->an_ssid2, ireq->i_len); + ssids->an_ssid2_len = ireq->i_len; + break; + case 2: + error = copyin(ireq->i_data, + ssids->an_ssid3, ireq->i_len); + ssids->an_ssid3_len = ireq->i_len; + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_IOC_WEP: + switch (ireq->i_val) { + case IEEE80211_WEP_OFF: + config->an_authtype &= + ~(AN_AUTHTYPE_PRIVACY_IN_USE | + AN_AUTHTYPE_ALLOW_UNENCRYPTED); + break; + case IEEE80211_WEP_ON: + config->an_authtype |= + AN_AUTHTYPE_PRIVACY_IN_USE; + config->an_authtype &= + ~AN_AUTHTYPE_ALLOW_UNENCRYPTED; + break; + case IEEE80211_WEP_MIXED: + config->an_authtype |= + AN_AUTHTYPE_PRIVACY_IN_USE | + AN_AUTHTYPE_ALLOW_UNENCRYPTED; + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_IOC_WEPKEY: + if (ireq->i_val < 0 || ireq->i_val > 7 || + ireq->i_len > 13) { + error = EINVAL; + break; + } + error = copyin(ireq->i_data, tmpstr, 13); + if(error) + break; + bzero(&areq, sizeof(struct an_ltv_key)); + areq.an_len = sizeof(struct an_ltv_key); + key->mac[0] = 1; /* The others are 0. */ + key->kindex = ireq->i_val % 4; + if(ireq->i_val < 4) + areq.an_type = AN_RID_WEP_TEMP; + else + areq.an_type = AN_RID_WEP_PERM; + key->klen = ireq->i_len; + bcopy(tmpstr, key->key, key->klen); + break; + case IEEE80211_IOC_WEPTXKEY: + if(ireq->i_val < 0 || ireq->i_val > 3) { + error = EINVAL; + break; + } + bzero(&areq, sizeof(struct an_ltv_key)); + areq.an_len = sizeof(struct an_ltv_key); + areq.an_type = AN_RID_WEP_PERM; + key->kindex = 0xffff; + key->mac[0] = ireq->i_val; + break; + case IEEE80211_IOC_AUTHMODE: + switch (ireq->i_val) { + case IEEE80211_AUTH_NONE: + config->an_authtype = AN_AUTHTYPE_NONE | + (config->an_authtype & ~AN_AUTHTYPE_MASK); + break; + case IEEE80211_AUTH_OPEN: + config->an_authtype = AN_AUTHTYPE_OPEN | + (config->an_authtype & ~AN_AUTHTYPE_MASK); + break; + case IEEE80211_AUTH_SHARED: + config->an_authtype = AN_AUTHTYPE_SHAREDKEY | + (config->an_authtype & ~AN_AUTHTYPE_MASK); + break; + default: + error = EINVAL; + } + break; + case IEEE80211_IOC_STATIONNAME: + if(ireq->i_len > 16) { + error = EINVAL; + break; + } + bzero(config->an_nodename, 16); + error = copyin(ireq->i_data, + config->an_nodename, ireq->i_len); + break; + case IEEE80211_IOC_CHANNEL: + /* + * The actual range is 1-14, but if you set it + * to 0 you get the default so we let that work + * too. + */ + if (ireq->i_val < 0 || ireq->i_val >14) { + error = EINVAL; + break; + } + config->an_ds_channel = ireq->i_val; + break; + case IEEE80211_IOC_POWERSAVE: + switch (ireq->i_val) { + case IEEE80211_POWERSAVE_OFF: + config->an_psave_mode = AN_PSAVE_NONE; + break; + case IEEE80211_POWERSAVE_CAM: + config->an_psave_mode = AN_PSAVE_CAM; + break; + case IEEE80211_POWERSAVE_PSP: + config->an_psave_mode = AN_PSAVE_PSP; + break; + case IEEE80211_POWERSAVE_PSP_CAM: + config->an_psave_mode = AN_PSAVE_PSP_CAM; + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_IOC_POWERSAVESLEEP: + config->an_listen_interval = ireq->i_val; + break; + } + + if (!error) + an_setdef(sc, &areq); + break; default: error = EINVAL; break; @@ -1416,6 +1828,7 @@ void an_cache_store (sc, eh, m, rx_quality) static int cache_slot = 0; /* use this cache entry */ static int wrapindex = 0; /* next "free" cache entry */ int saanp=0; + int sig, noise; /* filters: * 1. ip only @@ -1523,3 +1936,84 @@ void an_cache_store (sc, eh, m, rx_quality) return; } #endif + +static int an_media_change(ifp) + struct ifnet *ifp; +{ + struct an_softc *sc = ifp->if_softc; + int otype = sc->an_config.an_opmode; + int orate = sc->an_tx_rate; + + if ((sc->an_ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0) + sc->an_config.an_opmode = AN_OPMODE_IBSS_ADHOC; + else + sc->an_config.an_opmode = AN_OPMODE_INFRASTRUCTURE_STATION; + + switch (IFM_SUBTYPE(sc->an_ifmedia.ifm_cur->ifm_media)) { + case IFM_IEEE80211_DS1: + sc->an_tx_rate = AN_RATE_1MBPS; + break; + case IFM_IEEE80211_DS2: + sc->an_tx_rate = AN_RATE_2MBPS; + break; + case IFM_IEEE80211_DS5: + sc->an_tx_rate = AN_RATE_5_5MBPS; + break; + case IFM_IEEE80211_DS11: + sc->an_tx_rate = AN_RATE_11MBPS; + break; + case IFM_AUTO: + sc->an_tx_rate = 0; + break; + } + + if (otype != sc->an_config.an_opmode || + orate != sc->an_tx_rate) + an_init(sc); + + return(0); +} + +static void an_media_status(ifp, imr) + struct ifnet *ifp; + struct ifmediareq *imr; +{ + struct an_ltv_status status; + struct an_softc *sc = ifp->if_softc; + + status.an_len = sizeof(status); + status.an_type = AN_RID_STATUS; + if (an_read_record(sc, (struct an_ltv_gen *)&status)) { + /* If the status read fails, just lie. */ + imr->ifm_active = sc->an_ifmedia.ifm_cur->ifm_media; + imr->ifm_status = IFM_AVALID|IFM_ACTIVE; + } + + if(sc->an_tx_rate == 0) { + imr->ifm_active = IFM_IEEE80211|IFM_AUTO; + if (sc->an_config.an_opmode == AN_OPMODE_IBSS_ADHOC) + imr->ifm_active |= IFM_IEEE80211_ADHOC; + switch(status.an_current_tx_rate) { + case AN_RATE_1MBPS: + imr->ifm_active |= IFM_IEEE80211_DS1; + break; + case AN_RATE_2MBPS: + imr->ifm_active |= IFM_IEEE80211_DS2; + break; + case AN_RATE_5_5MBPS: + imr->ifm_active |= IFM_IEEE80211_DS5; + break; + case AN_RATE_11MBPS: + imr->ifm_active |= IFM_IEEE80211_DS11; + break; + } + } else { + imr->ifm_active = sc->an_ifmedia.ifm_cur->ifm_media; + } + + imr->ifm_status = IFM_AVALID; + if (sc->an_config.an_opmode == AN_OPMODE_IBSS_ADHOC) + imr->ifm_status |= IFM_ACTIVE; + else if (status.an_opmode & AN_STATUS_OPMODE_ASSOCIATED) + imr->ifm_status |= IFM_ACTIVE; +} diff --git a/sys/dev/an/if_an_isa.c b/sys/dev/an/if_an_isa.c index 29e6c28..8ece54d 100644 --- a/sys/dev/an/if_an_isa.c +++ b/sys/dev/an/if_an_isa.c @@ -63,6 +63,7 @@ #include <net/ethernet.h> #include <net/if_dl.h> #include <net/if_types.h> +#include <net/if_media.h> #include <isa/isavar.h> #include <isa/pnpvar.h> @@ -137,6 +138,7 @@ an_detach_isa(device_t dev) struct ifnet *ifp = &sc->arpcom.ac_if; an_stop(sc); + ifmedia_removeall(&sc->an_ifmedia); ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); bus_teardown_intr(dev, sc->irq_res, sc->irq_handle); an_release_resources(dev); diff --git a/sys/dev/an/if_an_pccard.c b/sys/dev/an/if_an_pccard.c index ebb7803..dc91b0f 100644 --- a/sys/dev/an/if_an_pccard.c +++ b/sys/dev/an/if_an_pccard.c @@ -64,6 +64,7 @@ #include <net/ethernet.h> #include <net/if_dl.h> #include <net/if_types.h> +#include <net/if_media.h> #ifndef lint static const char rcsid[] = @@ -111,6 +112,7 @@ an_pccard_detach(device_t dev) return(0); } an_stop(sc); + ifmedia_removeall(&sc->an_ifmedia); ifp->if_flags &= ~IFF_RUNNING; ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); sc->an_gone = 1; diff --git a/sys/dev/an/if_an_pci.c b/sys/dev/an/if_an_pci.c index bde0f3d..47099e8 100644 --- a/sys/dev/an/if_an_pci.c +++ b/sys/dev/an/if_an_pci.c @@ -76,6 +76,7 @@ #include <net/if_arp.h> #include <net/ethernet.h> #include <net/if_dl.h> +#include <net/if_media.h> #include <pci/pcireg.h> #include <pci/pcivar.h> @@ -195,6 +196,7 @@ an_detach_pci(device_t dev) struct ifnet *ifp = &sc->arpcom.ac_if; an_stop(sc); + ifmedia_removeall(&sc->an_ifmedia); ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); bus_teardown_intr(dev, sc->irq_res, sc->irq_handle); an_release_resources(dev); diff --git a/sys/dev/an/if_anreg.h b/sys/dev/an/if_anreg.h index 844d480..e86e354 100644 --- a/sys/dev/an/if_anreg.h +++ b/sys/dev/an/if_anreg.h @@ -34,8 +34,8 @@ #define AN_TIMEOUT 65536 -/* Default network name: ANY */ -#define AN_DEFAULT_NETNAME "ANY" +/* Default network name: <empty string> */ +#define AN_DEFAULT_NETNAME "" /* The nodename must be less than 16 bytes */ #define AN_DEFAULT_NODENAME "FreeBSD" @@ -846,6 +846,7 @@ struct an_softc { struct callout_handle an_stat_ch; struct mtx an_mtx; device_t an_dev; + struct ifmedia an_ifmedia; }; #define AN_LOCK(_sc) mtx_lock(&(_sc)->an_mtx) diff --git a/sys/dev/awi/awi.c b/sys/dev/awi/awi.c index 4dab87b..fdfa822 100644 --- a/sys/dev/awi/awi.c +++ b/sys/dev/awi/awi.c @@ -455,9 +455,12 @@ awi_ioctl(ifp, cmd, data) struct awi_softc *sc = ifp->if_softc; struct ifreq *ifr = (struct ifreq *)data; struct ifaddr *ifa = (struct ifaddr *)data; + struct ieee80211req *ireq = (struct ieee80211req *)data; int s, error; struct ieee80211_nwid nwid; u_int8_t *p; + int len; + u_int8_t tmpstr[IEEE80211_NWID_LEN*2]; s = splnet(); @@ -566,6 +569,184 @@ awi_ioctl(ifp, cmd, data) error = ifmedia_ioctl(ifp, ifr, &sc->sc_media, cmd); break; #endif +#ifdef __FreeBSD__ + case SIOCG80211: + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + bzero(tmpstr, IEEE80211_NWID_LEN); + if(ireq->i_val == -1 && ifp->if_flags & IFF_RUNNING) { + if (sc->sc_mib_local.Network_Mode) { + p = sc->sc_bss.essid; + len = p[1]; + p += 2; + } else { + len = ETHER_ADDR_LEN; + p = sc->sc_bss.bssid; + } + } else if(ireq->i_val == 0) { + if (sc->sc_mib_local.Network_Mode) + p = sc->sc_mib_mac.aDesired_ESS_ID; + else + p = sc->sc_ownssid; + len = p[1]; + p += 2; + } else { + error = EINVAL; + break; + } + if(len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + bcopy(p, tmpstr, len); + error = copyout(tmpstr, ireq->i_data, + IEEE80211_NWID_LEN); + break; + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 1; + break; + case IEEE80211_IOC_WEP: + /* XXX: I'm not sure this is entierly correct */ + ireq->i_val = awi_wep_getalgo(sc); + if(ireq->i_val != IEEE80211_WEP_OFF) + ireq->i_val = IEEE80211_WEP_ON; + break; + case IEEE80211_IOC_WEPKEY: + if(ireq->i_val < 0 || ireq->i_val > 3) { + error = EINVAL; + break; + } + len = sizeof(tmpstr); + error = awi_wep_getkey(sc, ireq->i_val, tmpstr, &len); + if(error) + break; + if(!suser(curproc)) + bzero(tmpstr, len); + ireq->i_val = len; + error = copyout(tmpstr, ireq->i_data, len); + break; + case IEEE80211_IOC_NUMWEPKEYS: + ireq->i_val = 4; + break; + case IEEE80211_IOC_WEPTXKEY: + ireq->i_val = sc->sc_wep_defkid; + break; + case IEEE80211_IOC_AUTHMODE: + /* XXX: Is this correct? */ + ireq->i_val = IEEE80211_AUTH_OPEN; + break; + case IEEE80211_IOC_STATIONNAME: + bzero(tmpstr, IEEE80211_NWID_LEN); + p = hostname; + len = strlen(hostname); + if(len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + bcopy(p, tmpstr, len); + error = copyout(tmpstr, ireq->i_data, + IEEE80211_NWID_LEN); + break; + case IEEE80211_IOC_CHANNEL: + /* XXX: Handle FH cards */ + ireq->i_val = sc->sc_bss.chanset; + break; + case IEEE80211_IOC_POWERSAVE: + /* + * There appears to be a mib for this in the + * softc, but since there's no way to enable + * powersaving reporting it's value isn't really + * meaningfull. + */ + ireq->i_val = IEEE80211_POWERSAVE_NOSUP; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + error = EINVAL; + break; + default: + error = EINVAL; + break; + } + break; + case SIOCS80211: + error = suser(curproc); + if(error) + break; + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + if(ireq->i_val != 0) { + error = EINVAL; + break; + } + bzero(tmpstr, AWI_ESS_ID_SIZE); + tmpstr[0] = IEEE80211_ELEMID_SSID; + tmpstr[1] = ireq->i_val; + error = copyin(ireq->i_data, tmpstr+2, ireq->i_val); + if(error) + break; + bcopy(tmpstr, sc->sc_mib_mac.aDesired_ESS_ID, + AWI_ESS_ID_SIZE); + bcopy(tmpstr, sc->sc_ownssid, AWI_ESS_ID_SIZE); + break; + case IEEE80211_IOC_WEP: + if(ireq->i_val == IEEE80211_WEP_OFF) + error = awi_wep_setalgo(sc, 0); + else + error = awi_wep_setalgo(sc, 1); + break; + case IEEE80211_IOC_WEPKEY: + error = copyin(ireq->i_data, tmpstr, 14); + if(error) + break; + if(ireq->i_val < 0 || ireq->i_val > 3 || + tmpstr[0] > 13) { + error = EINVAL; + break; + } + error = awi_wep_setkey(sc, ireq->i_val, tmpstr+1, + tmpstr[0]); + break; + case IEEE80211_IOC_WEPTXKEY: + if(ireq->i_val < 0 || ireq->i_val > 3) { + error = EINVAL; + break; + } + sc->sc_wep_defkid = ireq->i_val; + break; + case IEEE80211_IOC_AUTHMODE: + error = EINVAL; + break; + case IEEE80211_IOC_STATIONNAME: + error = EPERM; + break; + case IEEE80211_IOC_CHANNEL: + if(ireq->i_val < sc->sc_scan_min || + ireq->i_val > sc->sc_scan_max) { + error = EINVAL; + break; + } + sc->sc_ownch = ireq->i_val; + break; + case IEEE80211_IOC_POWERSAVE: + if(ireq->i_val != 0) + error = EINVAL; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + error = EINVAL; + break; + default: + error = EINVAL; + break; + } + /* Restart the card so the change takes effect */ + if(!error) { + if(sc->sc_enabled) { + awi_stop(sc); + error = awi_init(sc); + } + } + break; +#endif /* __FreeBSD__ */ default: error = awi_wicfg(ifp, cmd, data); break; diff --git a/sys/dev/wi/if_wavelan_ieee.h b/sys/dev/wi/if_wavelan_ieee.h index 5e1046a..0454898 100644 --- a/sys/dev/wi/if_wavelan_ieee.h +++ b/sys/dev/wi/if_wavelan_ieee.h @@ -169,6 +169,7 @@ struct wi_counters { u_int32_t wi_rx_msg_in_msg_frags; u_int32_t wi_rx_msg_in_bad_msg_frags; }; +#endif /* * These are all the LTV record types that we can read or write @@ -247,6 +248,7 @@ struct wi_counters { #define WI_RID_TX_CRYPT_KEY 0xFCB1 #define WI_RID_TICK_TIME 0xFCE0 +#ifndef _KERNEL struct wi_key { u_int16_t wi_keylen; u_int8_t wi_keydat[14]; @@ -257,6 +259,7 @@ struct wi_ltv_keys { u_int16_t wi_type; struct wi_key wi_keys[4]; }; +#endif /* * NIC information @@ -315,7 +318,6 @@ struct wi_ltv_keys { #define WI_RID_CCA_TIME 0xFDC4 /* clear chan assess time */ #define WI_RID_MAC_PROC_DELAY 0xFDC5 /* MAC processing delay time */ #define WI_RID_DATA_RATES 0xFDC6 /* supported data rates */ -#endif #endif diff --git a/sys/dev/wi/if_wi.c b/sys/dev/wi/if_wi.c index 2ae8fd1..40cdbc5 100644 --- a/sys/dev/wi/if_wi.c +++ b/sys/dev/wi/if_wi.c @@ -92,6 +92,7 @@ #include <net/if_dl.h> #include <net/if_media.h> #include <net/if_types.h> +#include <net/if_ieee80211.h> #include <netinet/in.h> #include <netinet/in_systm.h> @@ -161,6 +162,10 @@ static void wi_shutdown __P((device_t)); static int wi_alloc __P((device_t, int)); static void wi_free __P((device_t)); +static int wi_get_cur_ssid __P((struct wi_softc *, char *, int *)); +static int wi_media_change __P((struct ifnet *)); +static void wi_media_status __P((struct ifnet *, struct ifmediareq *)); + static device_method_t wi_pccard_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pccard_compat_probe), @@ -282,6 +287,9 @@ static int wi_pccard_detach(dev) wi_stop(sc); + /* Delete all remaining media. */ + ifmedia_removeall(&sc->ifmedia); + ether_ifdetach(ifp, ETHER_BPF_SUPPORTED); bus_teardown_intr(dev, sc->irq, sc->wi_intrhand); wi_free(dev); @@ -511,6 +519,29 @@ wi_generic_attach(device_t dev) wi_init(sc); wi_stop(sc); + ifmedia_init(&sc->ifmedia, 0, wi_media_change, wi_media_status); + /* XXX: Should read from card capabilities */ +#define ADD(m, c) ifmedia_add(&sc->ifmedia, (m), (c), NULL) + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS1, 0, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS2, 0, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS5, 0, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_IEEE80211_DS11, 0, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, + IFM_IEEE80211_ADHOC, 0), 0); + ADD(IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, 0, 0), 0); +#undef ADD + ifmedia_set(&sc->ifmedia, IFM_MAKEWORD(IFM_IEEE80211, IFM_AUTO, + 0, 0)); + + /* * Call MI attach routine. */ @@ -1297,14 +1328,19 @@ static int wi_ioctl(ifp, command, data) caddr_t data; { int error = 0; + int len; + u_int8_t tmpkey[14]; + char tmpssid[IEEE80211_NWID_LEN]; struct wi_softc *sc; struct wi_req wreq; struct ifreq *ifr; + struct ieee80211req *ireq; struct proc *p = curproc; sc = ifp->if_softc; WI_LOCK(sc); ifr = (struct ifreq *)data; + ireq = (struct ieee80211req *)data; if (sc->wi_gone) { error = ENODEV; @@ -1337,6 +1373,10 @@ static int wi_ioctl(ifp, command, data) sc->wi_if_flags = ifp->if_flags; error = 0; break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, command); + break; case SIOCADDMULTI: case SIOCDELMULTI: wi_setmulti(sc); @@ -1398,6 +1438,203 @@ static int wi_ioctl(ifp, command, data) wi_setdef(sc, &wreq); } break; + case SIOCG80211: + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + if(ireq->i_val == -1) { + bzero(tmpssid, IEEE80211_NWID_LEN); + error = wi_get_cur_ssid(sc, tmpssid, &len); + if (error != 0) + break; + error = copyout(tmpssid, ireq->i_data, + IEEE80211_NWID_LEN); + ireq->i_len = len; + } else if (ireq->i_val == 0) { + error = copyout(sc->wi_net_name, + ireq->i_data, + IEEE80211_NWID_LEN); + ireq->i_len = IEEE80211_NWID_LEN; + } else + error = EINVAL; + break; + case IEEE80211_IOC_NUMSSIDS: + ireq->i_val = 1; + break; + case IEEE80211_IOC_WEP: + if(!sc->wi_has_wep) { + ireq->i_val = IEEE80211_WEP_NOSUP; + } else { + if(sc->wi_use_wep) { + ireq->i_val = + IEEE80211_WEP_MIXED; + } else { + ireq->i_val = + IEEE80211_WEP_OFF; + } + } + break; + case IEEE80211_IOC_WEPKEY: + if(!sc->wi_has_wep || + ireq->i_val < 0 || ireq->i_val > 3) { + error = EINVAL; + break; + } + len = sc->wi_keys.wi_keys[ireq->i_val].wi_keylen; + if (suser(p)) + bcopy(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, + tmpkey, len); + else + bzero(tmpkey, len); + + ireq->i_len = len; + error = copyout(tmpkey, ireq->i_data, len); + + break; + case IEEE80211_IOC_NUMWEPKEYS: + if(!sc->wi_has_wep) + error = EINVAL; + else + ireq->i_val = 4; + break; + case IEEE80211_IOC_WEPTXKEY: + if(!sc->wi_has_wep) + error = EINVAL; + else + ireq->i_val = sc->wi_tx_key; + break; + case IEEE80211_IOC_AUTHMODE: + ireq->i_val = IEEE80211_AUTH_NONE; + break; + case IEEE80211_IOC_STATIONNAME: + error = copyout(sc->wi_node_name, + ireq->i_data, IEEE80211_NWID_LEN); + ireq->i_len = IEEE80211_NWID_LEN; + break; + case IEEE80211_IOC_CHANNEL: + wreq.wi_type = WI_RID_CURRENT_CHAN; + wreq.wi_len = WI_MAX_DATALEN; + if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) + error = EINVAL; + else { + ireq->i_val = wreq.wi_val[0]; + } + break; + case IEEE80211_IOC_POWERSAVE: + if(sc->wi_pm_enabled) + ireq->i_val = IEEE80211_POWERSAVE_ON; + else + ireq->i_val = IEEE80211_POWERSAVE_OFF; + break; + case IEEE80211_IOC_POWERSAVESLEEP: + ireq->i_val = sc->wi_max_sleep; + break; + default: + error = EINVAL; + } + break; + case SIOCS80211: + if ((error = suser(p))) + goto out; + switch(ireq->i_type) { + case IEEE80211_IOC_SSID: + if (ireq->i_val != 0 || + ireq->i_len > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + /* We set both of them */ + bzero(sc->wi_net_name, IEEE80211_NWID_LEN); + error = copyin(ireq->i_data, + sc->wi_net_name, ireq->i_len); + bcopy(sc->wi_net_name, sc->wi_ibss_name, IEEE80211_NWID_LEN); + break; + case IEEE80211_IOC_WEP: + /* + * These cards only support one mode so + * we just turn wep on what ever is + * passed in if it's not OFF. + */ + if (ireq->i_val == IEEE80211_WEP_OFF) { + sc->wi_use_wep = 0; + } else { + sc->wi_use_wep = 1; + } + break; + case IEEE80211_IOC_WEPKEY: + if (ireq->i_val < 0 || ireq->i_val > 3 || + ireq->i_len > 13) { + error = EINVAL; + break; + } + bzero(sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, 13); + error = copyin(ireq->i_data, + sc->wi_keys.wi_keys[ireq->i_val].wi_keydat, + ireq->i_len); + if(error) + break; + sc->wi_keys.wi_keys[ireq->i_val].wi_keylen = + ireq->i_len; + break; + case IEEE80211_IOC_WEPTXKEY: + if (ireq->i_val < 0 || ireq->i_val > 3) { + error = EINVAL; + break; + } + sc->wi_tx_key = ireq->i_val; + break; + case IEEE80211_IOC_AUTHMODE: + error = EINVAL; + break; + case IEEE80211_IOC_STATIONNAME: + if (ireq->i_len > 32) { + error = EINVAL; + break; + } + bzero(sc->wi_node_name, 32); + error = copyin(ireq->i_data, + sc->wi_node_name, ireq->i_len); + break; + case IEEE80211_IOC_CHANNEL: + /* + * The actual range is 1-14, but if you + * set it to 0 you get the default. So + * we let that work too. + */ + if (ireq->i_val < 0 || ireq->i_val > 14) { + error = EINVAL; + break; + } + sc->wi_channel = ireq->i_val; + break; + case IEEE80211_IOC_POWERSAVE: + switch (ireq->i_val) { + case IEEE80211_POWERSAVE_OFF: + sc->wi_pm_enabled = 0; + break; + case IEEE80211_POWERSAVE_ON: + sc->wi_pm_enabled = 1; + break; + default: + error = EINVAL; + break; + } + break; + case IEEE80211_IOC_POWERSAVESLEEP: + if (ireq->i_val < 0) { + error = EINVAL; + break; + } + sc->wi_max_sleep = ireq->i_val; + break; + default: + error = EINVAL; + break; + } + + /* Reinitialize WaveLAN. */ + wi_init(sc); + + break; default: error = EINVAL; break; @@ -1966,3 +2203,142 @@ void wi_cache_store (struct wi_softc *sc, struct ether_header *eh, return; } #endif + +static int wi_get_cur_ssid(sc, ssid, len) + struct wi_softc *sc; + char *ssid; + int *len; +{ + int error = 0; + struct wi_req wreq; + + wreq.wi_len = WI_MAX_DATALEN; + switch (sc->wi_ptype) { + case WI_PORTTYPE_ADHOC: + wreq.wi_type = WI_RID_CURRENT_SSID; + error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); + if (error != 0) + break; + if (wreq.wi_val[0] > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + *len = wreq.wi_val[0]; + bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN); + break; + case WI_PORTTYPE_BSS: + wreq.wi_type = WI_RID_COMMQUAL; + error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); + if (error != 0) + break; + if (wreq.wi_val[0] != 0) /* associated */ { + wreq.wi_type = WI_RID_CURRENT_SSID; + wreq.wi_len = WI_MAX_DATALEN; + error = wi_read_record(sc, (struct wi_ltv_gen *)&wreq); + if (error != 0) + break; + if (wreq.wi_val[0] > IEEE80211_NWID_LEN) { + error = EINVAL; + break; + } + *len = wreq.wi_val[0]; + bcopy(&wreq.wi_val[1], ssid, IEEE80211_NWID_LEN); + } else { + *len = IEEE80211_NWID_LEN; + bcopy(sc->wi_net_name, ssid, IEEE80211_NWID_LEN); + } + break; + default: + error = EINVAL; + break; + } + + return error; +} + +static int wi_media_change(ifp) + struct ifnet *ifp; +{ + struct wi_softc *sc = ifp->if_softc; + int otype = sc->wi_ptype; + int orate = sc->wi_tx_rate; + + if ((sc->ifmedia.ifm_cur->ifm_media & IFM_IEEE80211_ADHOC) != 0) + sc->wi_ptype = WI_PORTTYPE_ADHOC; + else + sc->wi_ptype = WI_PORTTYPE_BSS; + + switch (IFM_SUBTYPE(sc->ifmedia.ifm_cur->ifm_media)) { + case IFM_IEEE80211_DS1: + sc->wi_tx_rate = 1; + break; + case IFM_IEEE80211_DS2: + sc->wi_tx_rate = 2; + break; + case IFM_IEEE80211_DS5: + sc->wi_tx_rate = 5; + break; + case IFM_IEEE80211_DS11: + sc->wi_tx_rate = 11; + break; + case IFM_AUTO: + sc->wi_tx_rate = 3; + break; + } + + if (otype != sc->wi_ptype || + orate != sc->wi_tx_rate) + wi_init(sc); + + return(0); +} + +static void wi_media_status(ifp, imr) + struct ifnet *ifp; + struct ifmediareq *imr; +{ + struct wi_req wreq; + struct wi_softc *sc = ifp->if_softc; + + if (sc->wi_tx_rate == 3) { + imr->ifm_active = IFM_IEEE80211|IFM_AUTO; + if (sc->wi_ptype == WI_PORTTYPE_ADHOC) + imr->ifm_active |= IFM_IEEE80211_ADHOC; + wreq.wi_type = WI_RID_CUR_TX_RATE; + wreq.wi_len = WI_MAX_DATALEN; + if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0) { + switch(wreq.wi_val[0]) { + case 1: + imr->ifm_active |= IFM_IEEE80211_DS1; + break; + case 2: + imr->ifm_active |= IFM_IEEE80211_DS2; + break; + case 6: + imr->ifm_active |= IFM_IEEE80211_DS5; + break; + case 11: + imr->ifm_active |= IFM_IEEE80211_DS11; + break; + } + } + } else { + imr->ifm_active = sc->ifmedia.ifm_cur->ifm_media; + } + + imr->ifm_status = IFM_AVALID; + if (sc->wi_ptype == WI_PORTTYPE_ADHOC) + /* + * XXX: It would be nice if we could give some actually + * useful status like whether we joined another IBSS or + * created one ourselves. + */ + imr->ifm_status |= IFM_ACTIVE; + else { + wreq.wi_type = WI_RID_COMMQUAL; + wreq.wi_len = WI_MAX_DATALEN; + if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0 && + wreq.wi_val[0] != 0) + imr->ifm_status |= IFM_ACTIVE; + } +} |