+static void phy_parse_ht_capabilities(struct network_phy* phy, __u16 caps) {
+ // RX LDCP
+ if (caps & BIT(0))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_LDCP;
+
+ // HT40
+ if (caps & BIT(1))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40;
+
+ // Static/Dynamic SM Power Save
+ switch ((caps >> 2) & 0x3) {
+ case 0:
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_STATIC;
+ break;
+
+ case 1:
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_SMPS_DYNAMIC;
+ break;
+
+ default:
+ break;
+ }
+
+ // RX Greenfield
+ if (caps & BIT(4))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_GF;
+
+ // RX HT20 Short GI
+ if (caps & BIT(5))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT20_SGI;
+
+ // RX HT40 Short GI
+ if (caps & BIT(6))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_HT40_SGI;
+
+ // TX STBC
+ if (caps & BIT(7))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_TX_STBC;
+
+ // RX STBC
+ switch ((caps >> 8) & 0x3) {
+ case 1:
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC1;
+ break;
+
+ case 2:
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC2;
+ break;
+
+ case 3:
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_RX_STBC3;
+ break;
+
+ default:
+ break;
+ }
+
+ // HT Delayed Block ACK
+ if (caps & BIT(10))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_DELAYED_BA;
+
+ // Max AMSDU length 7935
+ if (caps & BIT(11))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_MAX_AMSDU_7935;
+
+ // DSSS/CCK HT40
+ if (caps & BIT(12))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_DSSS_CCK_HT40;
+
+ // Bit 13 is reserved
+
+ // HT40 Intolerant
+ if (caps & BIT(14))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_HT40_INTOLERANT;
+
+ // L-SIG TXOP protection
+ if (caps & BIT(15))
+ phy->ht_caps |= NETWORK_PHY_HT_CAP_LSIG_TXOP_PROT;
+}
+
+static int phy_parse_info(struct nl_msg* msg, void* data) {
+ struct network_phy* phy = data;
+
+ struct nlattr* attrs[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+ nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (attrs[NL80211_ATTR_WIPHY_BANDS]) {
+ struct nlattr* nl_band;
+ int i;
+
+ nla_for_each_nested(nl_band, attrs[NL80211_ATTR_WIPHY_BANDS], i) {
+ struct nlattr* band_attrs[NL80211_BAND_ATTR_MAX + 1];
+ nla_parse(band_attrs, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+ nla_len(nl_band), NULL);
+
+ // HT Capabilities
+ if (band_attrs[NL80211_BAND_ATTR_HT_CAPA]) {
+ __u16 caps = nla_get_u16(band_attrs[NL80211_BAND_ATTR_HT_CAPA]);
+ phy_parse_ht_capabilities(phy, caps);
+ }
+ }
+ }
+
+ return NL_OK;
+}
+
+static int phy_get_info(struct network_phy* phy) {
+ DEBUG(phy->ctx, "Getting information for %s\n", phy->name);
+
+ struct nl_msg* msg = network_phy_make_netlink_message(phy, NL80211_CMD_GET_WIPHY, 0);
+ if (!msg)
+ return -1;
+
+ return network_send_netlink_message(phy->ctx, msg, phy_parse_info, phy);
+}
+
+static void network_phy_free(struct network_phy* phy) {
+ DEBUG(phy->ctx, "Releasing phy at %p\n", phy);
+
+ if (phy->name)
+ free(phy->name);
+
+ network_unref(phy->ctx);
+ free(phy);
+}
+