diff options
Diffstat (limited to 'sys/dev/an/if_an.c')
-rw-r--r-- | sys/dev/an/if_an.c | 500 |
1 files changed, 497 insertions, 3 deletions
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; +} |