summaryrefslogtreecommitdiff
path: root/sys/dev/ic/if_wi.c
diff options
context:
space:
mode:
authorReyk Floeter <reyk@cvs.openbsd.org>2006-06-27 09:48:03 +0000
committerReyk Floeter <reyk@cvs.openbsd.org>2006-06-27 09:48:03 +0000
commit97bd295e6618bcd4b720143d30c5f2235d070c89 (patch)
treeaf57079b7a5ed295c20071bd682a0e699dd450d7 /sys/dev/ic/if_wi.c
parent1da690cdd04505eb2ceb359438f1e44b21fa89e9 (diff)
add some more net80211 compatibility glue:
- support scanning and node listing ("ifconfig -M"). this will replace wicontrol -L in station mode and wicontrol -l in hostap mode. - allow to print the BSSID tested by quite a few people ok jsg@
Diffstat (limited to 'sys/dev/ic/if_wi.c')
-rw-r--r--sys/dev/ic/if_wi.c172
1 files changed, 167 insertions, 5 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)
{