diff options
-rw-r--r-- | sys/dev/ic/if_wi.c | 172 | ||||
-rw-r--r-- | sys/dev/ic/if_wi_hostap.c | 42 | ||||
-rw-r--r-- | sys/dev/ic/if_wi_ieee.h | 10 | ||||
-rw-r--r-- | sys/dev/ic/if_wivar.h | 4 |
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; |