Luis R. Rodriguez | 21878f3 | 2009-02-02 15:39:06 -0800 | [diff] [blame] | 1 | #include <stdbool.h> |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 2 | #include <errno.h> |
Johannes Berg | 2ef1be6 | 2008-04-02 17:40:11 +0200 | [diff] [blame] | 3 | #include <net/if.h> |
| 4 | |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 5 | #include <netlink/genl/genl.h> |
| 6 | #include <netlink/genl/family.h> |
| 7 | #include <netlink/genl/ctrl.h> |
| 8 | #include <netlink/msg.h> |
| 9 | #include <netlink/attr.h> |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 10 | |
Johannes Berg | f408e01 | 2008-09-18 19:32:11 +0200 | [diff] [blame] | 11 | #include "nl80211.h" |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 12 | #include "iw.h" |
| 13 | |
| 14 | static void print_flag(const char *name, int *open) |
| 15 | { |
| 16 | if (!*open) |
| 17 | printf(" ("); |
| 18 | else |
| 19 | printf(", "); |
Johannes Berg | 6928312 | 2008-11-23 20:55:23 +0100 | [diff] [blame] | 20 | printf("%s", name); |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 21 | *open = 1; |
| 22 | } |
| 23 | |
Luis R. Rodriguez | 21878f3 | 2009-02-02 15:39:06 -0800 | [diff] [blame] | 24 | static void print_mcs_index(unsigned char *mcs) |
| 25 | { |
| 26 | unsigned int mcs_bit; |
| 27 | |
| 28 | for (mcs_bit = 0; mcs_bit <= 76; mcs_bit++) { |
| 29 | unsigned int mcs_octet = mcs_bit/8; |
| 30 | unsigned int MCS_RATE_BIT = 1 << mcs_bit % 8; |
| 31 | bool mcs_rate_idx_set; |
| 32 | |
| 33 | mcs_rate_idx_set = !!(mcs[mcs_octet] & MCS_RATE_BIT); |
| 34 | |
| 35 | if (!mcs_rate_idx_set) |
| 36 | continue; |
| 37 | |
| 38 | printf("\t\t\tMCS index %d\n", mcs_bit); |
| 39 | } |
| 40 | } |
| 41 | |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 42 | static int print_phy_handler(struct nl_msg *msg, void *arg) |
| 43 | { |
| 44 | struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; |
| 45 | struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); |
| 46 | |
| 47 | struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; |
| 48 | |
| 49 | struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; |
| 50 | static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { |
| 51 | [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, |
| 52 | [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, |
| 53 | [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, |
| 54 | [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, |
| 55 | [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, |
Johannes Berg | c1081c2 | 2008-11-23 12:11:26 +0100 | [diff] [blame] | 56 | [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 }, |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 57 | }; |
| 58 | |
| 59 | struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; |
| 60 | static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { |
| 61 | [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, |
| 62 | [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, |
| 63 | }; |
| 64 | |
| 65 | struct nlattr *nl_band; |
| 66 | struct nlattr *nl_freq; |
| 67 | struct nlattr *nl_rate; |
Johannes Berg | 6367e71 | 2008-09-05 23:01:11 +0200 | [diff] [blame] | 68 | struct nlattr *nl_mode; |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 69 | int bandidx = 1; |
Johannes Berg | 6367e71 | 2008-09-05 23:01:11 +0200 | [diff] [blame] | 70 | int rem_band, rem_freq, rem_rate, rem_mode; |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 71 | int open; |
| 72 | |
| 73 | nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), |
| 74 | genlmsg_attrlen(gnlh, 0), NULL); |
| 75 | |
| 76 | if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) |
| 77 | return NL_SKIP; |
| 78 | |
Johannes Berg | d631650 | 2008-09-16 17:05:33 +0200 | [diff] [blame] | 79 | if (tb_msg[NL80211_ATTR_WIPHY_NAME]) |
| 80 | printf("Wiphy %s\n", nla_get_string(tb_msg[NL80211_ATTR_WIPHY_NAME])); |
| 81 | |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 82 | nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { |
Johannes Berg | d631650 | 2008-09-16 17:05:33 +0200 | [diff] [blame] | 83 | printf("\tBand %d:\n", bandidx); |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 84 | bandidx++; |
| 85 | |
| 86 | nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), |
| 87 | nla_len(nl_band), NULL); |
| 88 | |
Johannes Berg | 3dd781c | 2008-10-14 18:46:23 +0200 | [diff] [blame] | 89 | #ifdef NL80211_BAND_ATTR_HT_CAPA |
| 90 | if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) { |
| 91 | unsigned short cap = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]); |
pat-lkml | 4233fca | 2009-02-02 19:25:39 -0500 | [diff] [blame] | 92 | #define PCOM(fmt, args...) do { printf("\t\t\t* " fmt "\n", ##args); } while (0) |
Johannes Berg | 3dd781c | 2008-10-14 18:46:23 +0200 | [diff] [blame] | 93 | #define PBCOM(bit, args...) if (cap & (bit)) PCOM(args) |
| 94 | printf("\t\tHT capabilities: 0x%.4x\n", cap); |
| 95 | PBCOM(0x0001, "LPDC coding"); |
| 96 | if (cap & 0x0002) |
| 97 | PCOM("20/40 MHz operation"); |
| 98 | else |
| 99 | PCOM("20 MHz operation"); |
| 100 | switch ((cap & 0x000c) >> 2) { |
| 101 | case 0: |
| 102 | PCOM("static SM PS"); |
| 103 | break; |
| 104 | case 1: |
| 105 | PCOM("dynamic SM PS"); |
| 106 | break; |
| 107 | case 2: |
| 108 | PCOM("reserved SM PS"); |
| 109 | break; |
| 110 | case 3: |
| 111 | PCOM("SM PS disabled"); |
| 112 | break; |
| 113 | } |
| 114 | PBCOM(0x0010, "HT-greenfield"); |
| 115 | PBCOM(0x0020, "20 MHz short GI"); |
| 116 | PBCOM(0x0040, "40 MHz short GI"); |
| 117 | PBCOM(0x0080, "TX STBC"); |
| 118 | if (cap & 0x300) |
| 119 | PCOM("RX STBC %d streams", (cap & 0x0300) >> 8); |
| 120 | PBCOM(0x0400, "HT-delayed block-ack"); |
| 121 | PCOM("max A-MSDU len %d", 0xeff + ((cap & 0x0800) << 1)); |
| 122 | PBCOM(0x1000, "DSSS/CCK 40 MHz"); |
| 123 | PBCOM(0x2000, "PSMP support"); |
| 124 | PBCOM(0x4000, "40 MHz intolerant"); |
| 125 | PBCOM(0x8000, "L-SIG TXOP protection support"); |
| 126 | } |
| 127 | if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) { |
| 128 | unsigned char factor = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]); |
| 129 | printf("\t\tHT A-MPDU factor: 0x%.4x (%d bytes)\n", factor, (1<<(13+factor))-1); |
| 130 | } |
| 131 | if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) { |
| 132 | unsigned char dens = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]); |
| 133 | printf("\t\tHT A-MPDU density: 0x%.4x (", dens); |
| 134 | switch (dens) { |
| 135 | case 0: |
| 136 | printf("no restriction)\n"); |
| 137 | break; |
| 138 | case 1: |
| 139 | printf("1/4 usec)\n"); |
| 140 | break; |
| 141 | case 2: |
| 142 | printf("1/2 usec)\n"); |
| 143 | break; |
| 144 | default: |
| 145 | printf("%d usec)\n", 1<<(dens - 3)); |
| 146 | } |
| 147 | } |
| 148 | if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] && |
| 149 | nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) == 16) { |
Luis R. Rodriguez | 21878f3 | 2009-02-02 15:39:06 -0800 | [diff] [blame] | 150 | /* As defined in 7.3.2.57.4 Supported MCS Set field */ |
| 151 | unsigned int tx_max_num_spatial_streams, max_rx_supp_data_rate; |
Johannes Berg | 3dd781c | 2008-10-14 18:46:23 +0200 | [diff] [blame] | 152 | unsigned char *mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]); |
Luis R. Rodriguez | 21878f3 | 2009-02-02 15:39:06 -0800 | [diff] [blame] | 153 | bool tx_mcs_set_defined, tx_mcs_set_equal, tx_unequal_modulation; |
| 154 | |
Johannes Berg | 3dd781c | 2008-10-14 18:46:23 +0200 | [diff] [blame] | 155 | printf("\t\tHT MCS set: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", |
| 156 | mcs[0], mcs[1], mcs[2], mcs[3], mcs[4], mcs[5], mcs[6], mcs[7], |
| 157 | mcs[8], mcs[9], mcs[10], mcs[11], mcs[12], mcs[13], mcs[14], mcs[15]); |
Luis R. Rodriguez | 21878f3 | 2009-02-02 15:39:06 -0800 | [diff] [blame] | 158 | |
| 159 | max_rx_supp_data_rate = ((mcs[10] >> 8) & ((mcs[11] & 0x3) << 8)); |
| 160 | tx_mcs_set_defined = !!(mcs[12] & (1 << 0)); |
| 161 | tx_mcs_set_equal = !(mcs[12] & (1 << 1)); |
Gabor Juhos | 2b26aa5 | 2009-08-11 08:47:30 +0200 | [diff] [blame] | 162 | tx_max_num_spatial_streams = (mcs[12] & ((1 << 2) | (1 << 3))) + 1; |
| 163 | tx_unequal_modulation = !!(mcs[12] & (1 << 4)); |
Luis R. Rodriguez | 21878f3 | 2009-02-02 15:39:06 -0800 | [diff] [blame] | 164 | |
| 165 | if (max_rx_supp_data_rate) |
| 166 | printf("\t\tHT Max RX data rate: %d Mbps\n", max_rx_supp_data_rate); |
| 167 | /* XXX: else see 9.6.0e.5.3 how to get this I think */ |
| 168 | |
| 169 | if (tx_mcs_set_defined) { |
| 170 | if (tx_mcs_set_equal) { |
| 171 | printf("\t\tHT TX/RX MCS rate indexes supported:\n"); |
| 172 | print_mcs_index(&mcs[0]); |
| 173 | } else { |
| 174 | printf("\t\tHT RX MCS rate indexes supported:\n"); |
| 175 | print_mcs_index(&mcs[0]); |
| 176 | |
| 177 | if (tx_unequal_modulation) |
| 178 | printf("TX unequal modulation supported\n"); |
| 179 | else |
| 180 | printf("TX unequal modulation not supported\n"); |
| 181 | |
| 182 | printf("\t\tHT TX Max spatiel streams: %d\n", |
| 183 | tx_max_num_spatial_streams); |
| 184 | |
| 185 | printf("\t\tHT TX MCS rate indexes supported may differ\n"); |
| 186 | } |
| 187 | } |
| 188 | else { |
| 189 | printf("\t\tHT RX MCS rate indexes supported:\n"); |
| 190 | print_mcs_index(&mcs[0]); |
| 191 | printf("\t\tHT TX MCS rates indexes are undefined\n"); |
| 192 | } |
| 193 | |
Johannes Berg | 3dd781c | 2008-10-14 18:46:23 +0200 | [diff] [blame] | 194 | } |
| 195 | #endif |
| 196 | |
Johannes Berg | d631650 | 2008-09-16 17:05:33 +0200 | [diff] [blame] | 197 | printf("\t\tFrequencies:\n"); |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 198 | |
| 199 | nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { |
Johannes Berg | 379f839 | 2008-12-08 12:53:58 +0100 | [diff] [blame] | 200 | uint32_t freq; |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 201 | nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), |
| 202 | nla_len(nl_freq), freq_policy); |
| 203 | if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) |
| 204 | continue; |
Johannes Berg | 379f839 | 2008-12-08 12:53:58 +0100 | [diff] [blame] | 205 | freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); |
| 206 | printf("\t\t\t* %d MHz [%d]", freq, ieee80211_frequency_to_channel(freq)); |
Johannes Berg | c1081c2 | 2008-11-23 12:11:26 +0100 | [diff] [blame] | 207 | |
Johannes Berg | d102c0b | 2009-03-24 08:41:42 +0100 | [diff] [blame] | 208 | if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] && |
| 209 | !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) |
| 210 | printf(" (%.1f dBm)", 0.01 * nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])); |
Johannes Berg | c1081c2 | 2008-11-23 12:11:26 +0100 | [diff] [blame] | 211 | |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 212 | open = 0; |
Johannes Berg | ee9cd98 | 2009-01-18 18:13:54 +0100 | [diff] [blame] | 213 | if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) { |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 214 | print_flag("disabled", &open); |
Johannes Berg | ee9cd98 | 2009-01-18 18:13:54 +0100 | [diff] [blame] | 215 | goto next; |
| 216 | } |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 217 | if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) |
| 218 | print_flag("passive scanning", &open); |
| 219 | if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) |
| 220 | print_flag("no IBSS", &open); |
| 221 | if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR]) |
| 222 | print_flag("radar detection", &open); |
Johannes Berg | ee9cd98 | 2009-01-18 18:13:54 +0100 | [diff] [blame] | 223 | next: |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 224 | if (open) |
| 225 | printf(")"); |
| 226 | printf("\n"); |
| 227 | } |
| 228 | |
Johannes Berg | 75dddcc | 2009-06-30 10:16:43 +0200 | [diff] [blame] | 229 | printf("\t\tBitrates (non-HT):\n"); |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 230 | |
| 231 | nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { |
| 232 | nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), |
| 233 | nla_len(nl_rate), rate_policy); |
| 234 | if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) |
| 235 | continue; |
Johannes Berg | d631650 | 2008-09-16 17:05:33 +0200 | [diff] [blame] | 236 | printf("\t\t\t* %2.1f Mbps", 0.1 * nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE])); |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 237 | open = 0; |
| 238 | if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) |
| 239 | print_flag("short preamble supported", &open); |
| 240 | if (open) |
| 241 | printf(")"); |
| 242 | printf("\n"); |
| 243 | } |
| 244 | } |
| 245 | |
Johannes Berg | 41be37f | 2008-09-19 17:34:55 +0200 | [diff] [blame] | 246 | if (tb_msg[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]) |
| 247 | printf("\tmax # scan SSIDs: %d\n", |
| 248 | nla_get_u8(tb_msg[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])); |
| 249 | |
Johannes Berg | 625aa4a | 2009-08-11 11:26:42 +0200 | [diff] [blame] | 250 | if (tb_msg[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) { |
| 251 | unsigned int frag; |
| 252 | |
| 253 | frag = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]); |
| 254 | if (frag != (unsigned int)-1) |
| 255 | printf("\tFragmentation threshold: %d\n", frag); |
| 256 | } |
| 257 | |
| 258 | if (tb_msg[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) { |
| 259 | unsigned int rts; |
| 260 | |
| 261 | rts = nla_get_u32(tb_msg[NL80211_ATTR_WIPHY_RTS_THRESHOLD]); |
| 262 | if (rts != (unsigned int)-1) |
| 263 | printf("\tRTS threshold: %d\n", rts); |
| 264 | } |
| 265 | |
Johannes Berg | 6367e71 | 2008-09-05 23:01:11 +0200 | [diff] [blame] | 266 | if (!tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES]) |
| 267 | return NL_SKIP; |
| 268 | |
Johannes Berg | d631650 | 2008-09-16 17:05:33 +0200 | [diff] [blame] | 269 | printf("\tSupported interface modes:\n"); |
Johannes Berg | 541ef42 | 2008-09-16 14:50:11 +0200 | [diff] [blame] | 270 | nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode) |
Johannes Berg | d631650 | 2008-09-16 17:05:33 +0200 | [diff] [blame] | 271 | printf("\t\t * %s\n", iftype_name(nl_mode->nla_type)); |
Johannes Berg | 6367e71 | 2008-09-05 23:01:11 +0200 | [diff] [blame] | 272 | |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 273 | return NL_SKIP; |
| 274 | } |
| 275 | |
Johannes Berg | 7c37a24 | 2009-04-08 13:13:28 +0200 | [diff] [blame] | 276 | static int handle_info(struct nl80211_state *state, |
| 277 | struct nl_cb *cb, |
Johannes Berg | d631650 | 2008-09-16 17:05:33 +0200 | [diff] [blame] | 278 | struct nl_msg *msg, |
| 279 | int argc, char **argv) |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 280 | { |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 281 | nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, print_phy_handler, NULL); |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 282 | |
Johannes Berg | 70391cc | 2008-09-16 18:35:06 +0200 | [diff] [blame] | 283 | return 0; |
Johannes Berg | 79f99b9 | 2008-01-16 00:21:59 +0100 | [diff] [blame] | 284 | } |
Johannes Berg | 4698bfc | 2009-08-24 12:53:34 +0200 | [diff] [blame^] | 285 | __COMMAND(NULL, info, "info", NULL, NL80211_CMD_GET_WIPHY, 0, 0, CIB_PHY, handle_info, |
Johannes Berg | ea35fc0 | 2009-05-05 14:56:21 +0200 | [diff] [blame] | 286 | "Show capabilities for the specified wireless device."); |
| 287 | TOPLEVEL(list, NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, CIB_NONE, handle_info, |
| 288 | "List all wireless devices and their capabilities."); |
Johannes Berg | 01ae06f | 2009-05-05 14:48:16 +0200 | [diff] [blame] | 289 | TOPLEVEL(phy, NULL, NL80211_CMD_GET_WIPHY, NLM_F_DUMP, CIB_NONE, handle_info, NULL); |