summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/ic/if_wi.c172
-rw-r--r--sys/dev/ic/if_wi_hostap.c42
-rw-r--r--sys/dev/ic/if_wi_ieee.h10
-rw-r--r--sys/dev/ic/if_wivar.h4
4 files changed, 219 insertions, 9 deletions
diff --git a/sys/dev/ic/if_wi.c b/sys/dev/ic/if_wi.c
index 9f9f5b52bf2..52e2b024479 100644
--- a/sys/dev/ic/if_wi.c
+++ b/sys/dev/ic/if_wi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_wi.c,v 1.129 2006/05/22 20:35:12 krw Exp $ */
+/* $OpenBSD: if_wi.c,v 1.130 2006/06/27 09:48:02 reyk Exp $ */
/*
* Copyright (c) 1997, 1998, 1999
@@ -127,7 +127,7 @@ u_int32_t widebug = WIDEBUG;
#if !defined(lint) && !defined(__OpenBSD__)
static const char rcsid[] =
- "$OpenBSD: if_wi.c,v 1.129 2006/05/22 20:35:12 krw Exp $";
+ "$OpenBSD: if_wi.c,v 1.130 2006/06/27 09:48:02 reyk Exp $";
#endif /* lint */
#ifdef foo
@@ -180,6 +180,7 @@ STATIC int wi_alloc_nicmem_io(struct wi_softc *, int, int *);
STATIC int wi_get_fid_io(struct wi_softc *sc, int fid);
STATIC void wi_intr_enable(struct wi_softc *sc, int mode);
STATIC void wi_intr_ack(struct wi_softc *sc, int mode);
+void wi_scan_timeout(void *);
/* Autoconfig definition of driver back-end */
struct cfdriver wi_cd = {
@@ -1541,13 +1542,19 @@ wi_setdef(struct wi_softc *sc, struct wi_req *wreq)
STATIC int
wi_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
{
- int s, error = 0;
+ int s, error = 0, i, j, len;
struct wi_softc *sc;
- struct wi_req wreq;
struct ifreq *ifr;
struct proc *p = curproc;
struct ifaddr *ifa = (struct ifaddr *)data;
- struct ieee80211_nwid nwid;
+ struct wi_scan_res *res;
+ struct wi_scan_p2_hdr *p2;
+ struct wi_req wreq;
+
+ struct ieee80211_nwid nwid;
+ struct ieee80211_nodereq nr;
+ struct ieee80211_nodereq_all *na;
+ struct ieee80211_bssid *bssid;
s = splnet();
@@ -1861,6 +1868,122 @@ wi_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
((struct ieee80211chanreq *)data)->i_channel =
letoh16(wreq.wi_val[0]);
break;
+ case SIOCG80211BSSID:
+ bssid = (struct ieee80211_bssid *)data;
+ wreq.wi_type = WI_RID_CURRENT_BSSID;
+ wreq.wi_len = WI_MAX_DATALEN;
+ if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) {
+ error = EINVAL;
+ break;
+ }
+ IEEE80211_ADDR_COPY(bssid->i_bssid, wreq.wi_val);
+ break;
+ case SIOCS80211SCAN:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ if (sc->wi_ptype == WI_PORTTYPE_HOSTAP)
+ break;
+ if ((ifp->if_flags & IFF_UP) == 0) {
+ error = ENETDOWN;
+ break;
+ }
+ if (sc->sc_firmware_type == WI_LUCENT) {
+ wi_cmd(sc, WI_CMD_INQUIRE,
+ WI_INFO_SCAN_RESULTS, 0, 0);
+ } else {
+ wreq.wi_len = 3;
+ wreq.wi_type = WI_RID_SCAN_REQ;
+ wreq.wi_val[0] = 0x3FFF;
+ wreq.wi_val[1] = 0x000F;
+
+ error = wi_write_record(sc,
+ (struct wi_ltv_gen *)&wreq);
+ if (error)
+ break;
+ }
+ sc->wi_scan_lock = 0;
+ timeout_set(&sc->wi_scan_timeout, wi_scan_timeout, sc);
+ len = WI_WAVELAN_RES_TIMEOUT;
+ if (sc->wi_flags & WI_FLAGS_BUS_USB) {
+ /* Use a longer timeout for wi@usb */
+ len = WI_WAVELAN_RES_TIMEOUT * 4;
+ }
+ timeout_add(&sc->wi_scan_timeout, len);
+
+ /* Let the userspace process wait for completion */
+ error = tsleep(&sc->wi_scan_lock, PCATCH, "wiscan",
+ hz * IEEE80211_SCAN_TIMEOUT);
+ break;
+ case SIOCG80211ALLNODES:
+ if ((error = suser(curproc, 0)) != 0)
+ break;
+ na = (struct ieee80211_nodereq_all *)data;
+ if (sc->wi_ptype == WI_PORTTYPE_HOSTAP) {
+ /* List all associated stations */
+ error = wihap_ioctl(sc, command, data);
+ break;
+ }
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_SCAN_RES;
+ if (sc->sc_firmware_type == WI_LUCENT) {
+ bcopy(sc->wi_scanbuf, wreq.wi_val,
+ sc->wi_scanbuf_len * 2);
+ wreq.wi_len = sc->wi_scanbuf_len;
+ i = 0;
+ len = WI_WAVELAN_RES_SIZE;
+ } else {
+ if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq)) {
+ error = EINVAL;
+ break;
+ }
+ p2 = (struct wi_scan_p2_hdr *)wreq.wi_val;
+ if (p2->wi_reason == 0)
+ break;
+ i = sizeof(*p2);
+ len = WI_PRISM2_RES_SIZE;
+ }
+
+ for (na->na_nodes = j = 0; (i < (wreq.wi_len * 2) - len) &&
+ (na->na_size >= j + sizeof(struct ieee80211_nodereq));
+ i += len) {
+ res = (struct wi_scan_res *)((char *)wreq.wi_val + i);
+ if (res == NULL)
+ break;
+
+ bzero(&nr, sizeof(nr));
+ IEEE80211_ADDR_COPY(nr.nr_macaddr, res->wi_bssid);
+ IEEE80211_ADDR_COPY(nr.nr_bssid, res->wi_bssid);
+ nr.nr_channel = letoh16(res->wi_chan);
+ nr.nr_rssi = letoh16(res->wi_signal);
+ nr.nr_max_rssi = 0; /* XXX */
+ nr.nr_nwid_len = letoh16(res->wi_ssid_len);
+ bcopy(res->wi_ssid, nr.nr_nwid, nr.nr_nwid_len);
+ nr.nr_intval = letoh16(res->wi_interval);
+ nr.nr_capinfo = letoh16(res->wi_capinfo);
+ nr.nr_txrate = res->wi_rate == WI_WAVELAN_RES_1M ? 2 :
+ (res->wi_rate == WI_WAVELAN_RES_2M ? 4 :
+ (res->wi_rate == WI_WAVELAN_RES_5M ? 11 :
+ (res->wi_rate == WI_WAVELAN_RES_11M ? 22 : 0)));
+ nr.nr_nrates = 0;
+ while (res->wi_srates[nr.nr_nrates] != 0) {
+ nr.nr_rates[nr.nr_nrates] =
+ res->wi_srates[nr.nr_nrates] &
+ WI_VAR_SRATES_MASK;
+ nr.nr_nrates++;
+ }
+ nr.nr_flags = 0;
+ if (bcmp(nr.nr_macaddr, nr.nr_bssid,
+ IEEE80211_ADDR_LEN) == 0)
+ nr.nr_flags |= IEEE80211_NODEREQ_AP;
+
+ error = copyout(&nr, (caddr_t)na->na_node + j,
+ sizeof(struct ieee80211_nodereq));
+ if (error)
+ break;
+ j += sizeof(struct ieee80211_nodereq);
+ na->na_nodes++;
+ }
+ break;
case SIOCHOSTAP_ADD:
case SIOCHOSTAP_DEL:
case SIOCHOSTAP_GET:
@@ -1879,6 +2002,45 @@ wi_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
return(error);
}
+void
+wi_scan_timeout(void *arg)
+{
+ struct wi_softc *sc = (struct wi_softc *)arg;
+ struct wi_req wreq;
+
+ if (sc->wi_scan_lock++ < WI_WAVELAN_RES_TRIES &&
+ sc->sc_firmware_type != WI_LUCENT &&
+ (sc->wi_flags & WI_FLAGS_BUS_USB) == 0) {
+ /*
+ * The Prism2/2.5/3 chipsets will set an extra field in the
+ * scan result if the scan request has been completed by the
+ * firmware. This allows to poll for completion and to
+ * wait for some more time if the scan is still in progress.
+ *
+ * XXX This doesn't work with wi@usb because it isn't safe
+ * to call wi_read_record_usb() while beeing in the timeout
+ * handler.
+ */
+ wreq.wi_len = WI_MAX_DATALEN;
+ wreq.wi_type = WI_RID_SCAN_RES;
+
+ if (wi_read_record(sc, (struct wi_ltv_gen *)&wreq) == 0 &&
+ ((struct wi_scan_p2_hdr *)wreq.wi_val)->wi_reason == 0) {
+ /* Wait some more time for scan completion */
+ timeout_add(&sc->wi_scan_timeout, WI_WAVELAN_RES_TIMEOUT);
+ return;
+ }
+ }
+
+ if (sc->sc_ic.ic_if.if_flags & IFF_DEBUG)
+ printf(WI_PRT_FMT ": wi_scan_timeout: %d tries\n",
+ WI_PRT_ARG(sc), sc->wi_scan_lock);
+
+ /* Wakeup the userland */
+ wakeup(&sc->wi_scan_lock);
+ sc->wi_scan_lock = 0;
+}
+
STATIC void
wi_init_io(struct wi_softc *sc)
{
diff --git a/sys/dev/ic/if_wi_hostap.c b/sys/dev/ic/if_wi_hostap.c
index 2b0b1765c59..6e215999857 100644
--- a/sys/dev/ic/if_wi_hostap.c
+++ b/sys/dev/ic/if_wi_hostap.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_wi_hostap.c,v 1.34 2006/04/20 20:31:12 miod Exp $ */
+/* $OpenBSD: if_wi_hostap.c,v 1.35 2006/06/27 09:48:02 reyk Exp $ */
/*
* Copyright (c) 2002
@@ -1209,6 +1209,9 @@ wihap_ioctl(struct wi_softc *sc, u_long command, caddr_t data)
struct hostap_sta stabuf;
int s, error = 0, n, flag;
+ struct ieee80211_nodereq nr;
+ struct ieee80211_nodereq_all *na;
+
if (!(sc->sc_ic.ic_if.if_flags & IFF_RUNNING))
return ENODEV;
@@ -1326,6 +1329,43 @@ wihap_ioctl(struct wi_softc *sc, u_long command, caddr_t data)
error = copyout(&reqall, ifr->ifr_data,
sizeof(reqall));
break;
+
+ case SIOCG80211ALLNODES:
+ na = (struct ieee80211_nodereq_all *)data;
+ na->na_nodes = n = 0;
+ s = splnet();
+ sta = TAILQ_FIRST(&whi->sta_list);
+ while (sta && na->na_size >=
+ n + sizeof(struct ieee80211_nodereq)) {
+ bzero(&nr, sizeof(nr));
+ IEEE80211_ADDR_COPY(nr.nr_macaddr, sta->addr);
+ IEEE80211_ADDR_COPY(nr.nr_bssid,
+ &sc->sc_ic.ic_myaddr);
+ nr.nr_associd = sta->asid;
+ nr.nr_rssi = sta->sig_info >> 8;
+ nr.nr_max_rssi = 0;
+ nr.nr_capinfo = sta->capinfo;
+ nr.nr_nrates = 0;
+ if (sta->rates & WI_SUPPRATES_1M)
+ nr.nr_rates[nr.nr_nrates++] = 2;
+ if (sta->rates & WI_SUPPRATES_2M)
+ nr.nr_rates[nr.nr_nrates++] = 4;
+ if (sta->rates & WI_SUPPRATES_5M)
+ nr.nr_rates[nr.nr_nrates++] = 11;
+ if (sta->rates & WI_SUPPRATES_11M)
+ nr.nr_rates[nr.nr_nrates++] = 22;
+
+ error = copyout(&nr, (caddr_t)na->na_node + n,
+ sizeof(struct ieee80211_nodereq));
+ if (error)
+ break;
+ n += sizeof(struct ieee80211_nodereq);
+ na->na_nodes++;
+ sta = TAILQ_NEXT(sta, list);
+ }
+ splx(s);
+ break;
+
default:
printf("wihap_ioctl: i shouldn't get other ioctls!\n");
error = EINVAL;
diff --git a/sys/dev/ic/if_wi_ieee.h b/sys/dev/ic/if_wi_ieee.h
index 1a7601de36d..aa084f199fa 100644
--- a/sys/dev/ic/if_wi_ieee.h
+++ b/sys/dev/ic/if_wi_ieee.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_wi_ieee.h,v 1.27 2005/12/13 10:30:35 jsg Exp $ */
+/* $OpenBSD: if_wi_ieee.h,v 1.28 2006/06/27 09:48:02 reyk Exp $ */
/*
* Copyright (c) 1997, 1998, 1999
@@ -665,7 +665,14 @@ struct wi_scan_res {
u_int8_t wi_rate;
u_int8_t wi_rsvd;
};
+#define WI_WAVELAN_RES_1M 0x0a
+#define WI_WAVELAN_RES_2M 0x14
+#define WI_WAVELAN_RES_5M 0x37
+#define WI_WAVELAN_RES_11M 0x6e
+
#define WI_WAVELAN_RES_SIZE 50
+#define WI_WAVELAN_RES_TIMEOUT ((hz / 10) * 2) /* 200ms */
+#define WI_WAVELAN_RES_TRIES 100
struct wi_scan_p2_hdr {
u_int16_t wi_rsvd;
@@ -673,7 +680,6 @@ struct wi_scan_p2_hdr {
};
#define WI_PRISM2_RES_SIZE 62
-
/*
* prism2 debug mode definitions
*/
diff --git a/sys/dev/ic/if_wivar.h b/sys/dev/ic/if_wivar.h
index 3baa12fa28c..856a7d16bef 100644
--- a/sys/dev/ic/if_wivar.h
+++ b/sys/dev/ic/if_wivar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_wivar.h,v 1.27 2005/10/31 05:37:13 jsg Exp $ */
+/* $OpenBSD: if_wivar.h,v 1.28 2006/06/27 09:48:02 reyk Exp $ */
/*
* Copyright (c) 1997, 1998, 1999
@@ -70,6 +70,8 @@ struct wi_softc {
u_int8_t wi_rxbuf[1596];
u_int8_t wi_txbuf[1596];
u_int8_t wi_scanbuf[1596];
+ int wi_scan_lock;
+ struct timeout wi_scan_timeout;
u_int8_t wi_scanbuf_len;