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 :-/
>
I have th USB bwfm(4), and I don't see anything different in ifconfig bwfm0.
The hotel wifi is only 11g though... Would I see anything different here?
-ml
> 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;
> + 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];
>