summaryrefslogtreecommitdiffstats
path: root/sys/dev/wi/if_wi.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/wi/if_wi.c')
-rw-r--r--sys/dev/wi/if_wi.c376
1 files changed, 376 insertions, 0 deletions
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;
+ }
+}
OpenPOWER on IntegriCloud