On Tue, Jan 15, 2019 at 10:01:42PM +0100, Stefan Sperling wrote: > This diff makes 'ifconfig bwfm0' display whether 11n is active or not, > what the current Tx rate is, and show up-to-date RSSI measurements. > I'm leaving display of 11ac Tx rates for future work because that will > require a change in struct ieet80211_node. > > Tested with 'bwfm0 at pci1 dev 0 function 0 "Broadcom BCM4350" rev 0x08: msi' > as a client with 11a and 11n APs, and in hostap mode with an iwn(4) client. > > I had a bwfm(4) USB device as well somewhere, but can't find it :-/ > > Index: bwfm.c > =================================================================== > RCS file: /cvs/src/sys/dev/ic/bwfm.c,v > retrieving revision 1.54 > diff -u -p -r1.54 bwfm.c > --- bwfm.c 25 Jul 2018 20:37:11 -0000 1.54 > +++ bwfm.c 15 Jan 2019 20:39:22 -0000 > @@ -59,6 +59,8 @@ void bwfm_start(struct ifnet *); > void bwfm_init(struct ifnet *); > void bwfm_stop(struct ifnet *); > void bwfm_watchdog(struct ifnet *); > +void bwfm_update_node(void *, struct ieee80211_node *); > +void bwfm_update_nodes(struct bwfm_softc *); > int bwfm_ioctl(struct ifnet *, u_long, caddr_t); > int bwfm_media_change(struct ifnet *); > > @@ -549,10 +551,136 @@ bwfm_watchdog(struct ifnet *ifp) > ieee80211_watchdog(ifp); > } > > +extern struct ifmedia_baudrate ifmedia_baudrate_descriptions[]; > + > +/* > + * This function might lie because some MCS have equivalent baudrates. > + * However, this is the best we can do given that the firmware only > + * reports Tx baudrates. > + */ > +uint64_t > +bwfm_rate2mword(uint64_t baudrate) > +{ > + int i; > + uint64_t mword; > + > + for (i = 0; ifmedia_baudrate_descriptions[i].ifmb_word != 0; i++) { > + mword = ifmedia_baudrate_descriptions[i].ifmb_word; > + if (!IFM_TYPE_MATCH(IFM_IEEE80211, mword)) > + continue; > + if (ifmedia_baudrate_descriptions[i].ifmb_baudrate == baudrate) > + return mword; > + } > + > + /* Not known. */ > + return 0; > +} > + > +void > +bwfm_update_node(void *arg, struct ieee80211_node *ni) > +{ > + struct bwfm_softc *sc = arg; > + struct ieee80211com *ic = &sc->sc_ic; > + struct bwfm_sta_info sta; > + uint32_t flags; > + int8_t rssi; > + uint32_t txrate; > + uint64_t mword; > + int i; > + > + memset(&sta, 0, sizeof(sta)); > + memcpy((uint8_t *)&sta, ni->ni_macaddr, sizeof(ni->ni_macaddr)); > + > + if (bwfm_fwvar_var_get_data(sc, "sta_info", &sta, sizeof(sta))) > + return; > + > + if (!IEEE80211_ADDR_EQ(ni->ni_macaddr, sta.ea)) > + return; > + > + if (le16toh(sta.ver) < 4) > + return; > + > + flags = le32toh(sta.flags); > + if ((flags & BWFM_STA_SCBSTATS) == 0) > + return; > + > + rssi = 0; > + for (i = 0; i < BWFM_ANT_MAX; i++) { > + if (sta.rssi[i] >= 0) > + continue; > + if (rssi == 0 || sta.rssi[i] > rssi) > + rssi = sta.rssi[i]; > + } > + if (rssi) > + ni->ni_rssi = rssi; > + > + txrate = le32toh(sta.tx_rate); > + if (txrate == 0xffffffff) /* Seen this happening during association. */ > + return; > + > + mword = bwfm_rate2mword(txrate * 1000); > + if ((sta.flags & BWFM_STA_N_CAP) && > + IFM_SUBTYPE(mword) >= IFM_IEEE80211_HT_MCS0) { > + /* Tell net80211 that firmware has negotiated 11n. */ > + ni->ni_flags |= IEEE80211_NODE_HT; > + if (ic->ic_curmode < IEEE80211_MODE_11N) > + ieee80211_setmode(ic, IEEE80211_MODE_11N); > + > + if ((sta.flags & BWFM_STA_VHT_CAP) && > + IFM_SUBTYPE(mword) >= IFM_IEEE80211_VHT_MCS0) { > + /* TODO: Can't store VHT MCS in ni yet... */ > + } else > + ni->ni_txmcs = ieee80211_media2mcs(mword); > + } else { > + /* > + * In 11n mode a fallback to legacy rates is transparent > + * to net80211. Just pretend we were using MCS 0. > + */ > + if (ni->ni_flags & IEEE80211_NODE_HT) > + ni->ni_txmcs = 0; > + else { > + /* We're in 11a/g mode. Map to a legacy rate. */ > + for (i = 0; i < ni->ni_rates.rs_nrates; i++) { > + uint8_t rate = ni->ni_rates.rs_rates[i]; > + rate &= IEEE80211_RATE_VAL; > + if (rate / 2 >= txrate / 1000) { > + ni->ni_txrate = i; > + break; > + } > + } > + } > + } > +} > + > +void > +bwfm_update_nodes(struct bwfm_softc *sc) > +{ > + struct ieee80211com *ic = &sc->sc_ic; > + struct ieee80211_node *ni; > + > + switch (ic->ic_opmode) { > + case IEEE80211_M_STA: > + bwfm_update_node(sc, ic->ic_bss); > + /* Update cached copy in the nodes tree as well. */ > + ni = ieee80211_find_node(ic, ic->ic_bss->ni_macaddr); > + if (ni) { > + ni->ni_rssi = ic->ic_bss->ni_rssi; > + } > + break; > + case IEEE80211_M_HOSTAP: > + ieee80211_iterate_nodes(ic, bwfm_update_node, sc); > + break;
I think HOSTAP needs #ifndef IEEE80211_STA_ONLY otherwise ramdisk kernels don't compile. With that fixed, ok patrick@. :) > + default: > + break; > + } > +} > + > int > bwfm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) > { > int s, error = 0; > + struct bwfm_softc *sc = ifp->if_softc; > + struct ieee80211com *ic = &sc->sc_ic; > > s = splnet(); > switch (cmd) { > @@ -568,6 +696,12 @@ bwfm_ioctl(struct ifnet *ifp, u_long cmd > bwfm_stop(ifp); > } > break; > + case SIOCGIFMEDIA: > + case SIOCG80211NODE: > + case SIOCG80211ALLNODES: > + if (ic->ic_state == IEEE80211_S_RUN) > + bwfm_update_nodes(sc); > + /* fall through */ > default: > error = ieee80211_ioctl(ifp, cmd, data); > } > Index: bwfmreg.h > =================================================================== > RCS file: /cvs/src/sys/dev/ic/bwfmreg.h,v > retrieving revision 1.16 > diff -u -p -r1.16 bwfmreg.h > --- bwfmreg.h 7 Feb 2018 21:44:09 -0000 1.16 > +++ bwfmreg.h 15 Jan 2019 18:42:33 -0000 > @@ -370,6 +370,137 @@ struct bwfm_bss_info { > uint16_t snr; > }; > > +#define BWFM_MAXRATES_IN_SET BWFM_MCSSET_LEN > +#define BWFM_ANT_MAX 4 > +#define BWFM_VHT_CAP_MCS_MAP_NSS_MAX 8 > +#define BWFM_HE_CAP_MCS_MAP_NSS_MAX BWFM_VHT_CAP_MCS_MAP_NSS_MAX > + > +struct bwfm_sta_rateset_v5 { > + uint32_t count; > + /* rates in 500kbps units w/hi bit set if basic */ > + uint8_t rates[BWFM_MAXRATES_IN_SET]; > + uint8_t mcs[BWFM_MCSSET_LEN]; > + uint16_t vht_mcs[BWFM_VHT_CAP_MCS_MAP_NSS_MAX]; > +}; > + > +struct bwfm_sta_rateset_v7 { > + uint16_t version; > + uint16_t len; > + uint32_t count; > + /* rates in 500kbps units w/hi bit set if basic */ > + uint8_t rates[BWFM_MAXRATES_IN_SET]; > + uint8_t mcs[BWFM_MCSSET_LEN]; > + uint16_t vht_mcs[BWFM_VHT_CAP_MCS_MAP_NSS_MAX]; > + uint16_t he_mcs[BWFM_HE_CAP_MCS_MAP_NSS_MAX]; > +}; > + > +struct bwfm_sta_info { > + uint16_t ver; > + uint16_t len; > + uint16_t cap; /* sta's advertised capabilities */ > + > + uint32_t flags; > +#define BWFM_STA_BRCM 0x00000001 /* Running a Broadcom driver > */ > +#define BWFM_STA_WME 0x00000002 /* WMM association */ > +#define BWFM_STA_NONERP 0x00000004 /* No ERP */ > +#define BWFM_STA_AUTHE 0x00000008 /* Authenticated */ > +#define BWFM_STA_ASSOC 0x00000010 /* Associated */ > +#define BWFM_STA_AUTHO 0x00000020 /* Authorized */ > +#define BWFM_STA_WDS 0x00000040 /* Wireless Distribution System */ > +#define BWFM_STA_WDS_LINKUP 0x00000080 /* WDS traffic/probes flowing */ > +#define BWFM_STA_PS 0x00000100 /* STA in power save mode, says AP */ > +#define BWFM_STA_APSD_BE 0x00000200 /* APSD for AC_BE default enabled */ > +#define BWFM_STA_APSD_BK 0x00000400 /* APSD for AC_BK default enabled */ > +#define BWFM_STA_APSD_VI 0x00000800 /* APSD for AC_VI default enabled */ > +#define BWFM_STA_APSD_VO 0x00001000 /* APSD for AC_VO default enabled */ > +#define BWFM_STA_N_CAP 0x00002000 /* STA 802.11n capable */ > +#define BWFM_STA_SCBSTATS 0x00004000 /* Per STA debug stats */ > +#define BWFM_STA_AMPDU_CAP 0x00008000 /* STA AMPDU capable */ > +#define BWFM_STA_AMSDU_CAP 0x00010000 /* STA AMSDU capable */ > +#define BWFM_STA_MIMO_PS 0x00020000 /* mimo ps mode is enabled */ > +#define BWFM_STA_MIMO_RTS 0x00040000 /* send rts in mimo ps mode */ > +#define BWFM_STA_RIFS_CAP 0x00080000 /* rifs enabled */ > +#define BWFM_STA_VHT_CAP 0x00100000 /* STA VHT(11ac) capable */ > +#define BWFM_STA_WPS 0x00200000 /* WPS state */ > +#define BWFM_STA_DWDS_CAP 0x01000000 /* DWDS CAP */ > +#define BWFM_STA_DWDS 0x02000000 /* DWDS active */ > + > + uint32_t idle; /* time since data pkt rx'd from sta */ > + uint8_t ea[ETHER_ADDR_LEN]; > + uint32_t count; /* # rates in this set */ > + uint8_t rates[BWFM_MAXRATES_IN_SET]; /* rates in 500kbps units */ > + /* w/hi bit set if basic */ > + uint32_t in; /* seconds elapsed since associated */ > + uint32_t listen_interval_inms; /* Min Listen interval in ms for STA */ > + > + /* Fields valid for ver >= 3 */ > + uint32_t tx_pkts; /* # of packets transmitted */ > + uint32_t tx_failures; /* # of packets failed */ > + uint32_t rx_ucast_pkts; /* # of unicast packets received */ > + uint32_t rx_mcast_pkts; /* # of multicast packets received */ > + uint32_t tx_rate; /* Rate of last successful tx frame, in bps */ > + uint32_t rx_rate; /* Rate of last successful rx frame, in bps */ > + uint32_t rx_decrypt_succeeds; /* # of packet decrypted successfully */ > + uint32_t rx_decrypt_failures; /* # of packet decrypted failed */ > + > + /* Fields valid for ver >= 4 */ > + uint32_t tx_tot_pkts; /* # of tx pkts (ucast + mcast) */ > + uint32_t rx_tot_pkts; /* # of data packets recvd (uni + mcast) */ > + uint32_t tx_mcast_pkts; /* # of mcast pkts txed */ > + uint64_t tx_tot_bytes; /* data bytes txed (ucast + mcast) */ > + uint64_t rx_tot_bytes; /* data bytes recvd (ucast + mcast) */ > + uint64_t tx_ucast_bytes; /* data bytes txed (ucast) */ > + uint64_t tx_mcast_bytes; /* # data bytes txed (mcast) */ > + uint64_t rx_ucast_bytes; /* data bytes recvd (ucast) */ > + uint64_t rx_mcast_bytes; /* data bytes recvd (mcast) */ > + int8_t rssi[BWFM_ANT_MAX]; /* per antenna rssi */ > + int8_t nf[BWFM_ANT_MAX]; /* per antenna noise floor */ > + uint16_t aid; /* association ID */ > + uint16_t ht_capabilities; /* advertised ht caps */ > + uint16_t vht_flags; /* converted vht flags */ > + uint32_t tx_pkts_retry_cnt; /* # of frames where a retry was > + * exhausted. > + */ > + uint32_t tx_pkts_retry_exhausted; /* # of user frames where a retry > + * was exhausted > + */ > + int8_t rx_lastpkt_rssi[BWFM_ANT_MAX]; /* Per antenna RSSI of last > + * received data frame. > + */ > + /* TX WLAN retry/failure statistics: > + * Separated for host requested frames and locally generated frames. > + * Include unicast frame only where the retries/failures can be counted. > + */ > + uint32_t tx_pkts_total; /* # user frames sent successfully */ > + uint32_t tx_pkts_retries; /* # user frames retries */ > + uint32_t tx_pkts_fw_total; /* # FW generated sent successfully */ > + uint32_t tx_pkts_fw_retries; /* # retries for FW generated frames */ > + uint32_t tx_pkts_fw_retry_exhausted; /* # FW generated where a retry > + * was exhausted > + */ > + uint32_t rx_pkts_retried; /* # rx with retry bit set */ > + uint32_t tx_rate_fallback; /* lowest fallback TX rate */ > + > + union { > + struct { > + struct bwfm_sta_rateset_v5 rateset_adv; > + } v5; > + > + struct { > + uint32_t rx_dur_total; /* user RX duration (estimate) */ > + uint16_t chanspec; > + uint16_t pad_1; > + struct bwfm_sta_rateset_v7 rateset_adv; > + uint16_t wpauth; /* authentication type */ > + uint8_t algo; /* crypto alogorithm */ > + uint8_t pad_2; > + uint32_t tx_rspec;/* Rate of last successful tx frame */ > + uint32_t rx_rspec;/* Rate of last successful rx frame */ > + uint32_t wnm_cap; > + } v7; > + }; > +}; > + > struct bwfm_ssid { > uint32_t len; > uint8_t ssid[BWFM_MAX_SSID_LEN]; >