1*9c5db199SXin Li /*
2*9c5db199SXin Li * This is a stripped down version of the iw tool designed for
3*9c5db199SXin Li * programmatically checking driver/hw capabilities.
4*9c5db199SXin Li *
5*9c5db199SXin Li * Copyright 2007, 2008 Johannes Berg <[email protected]>
6*9c5db199SXin Li */
7*9c5db199SXin Li
8*9c5db199SXin Li #include <errno.h>
9*9c5db199SXin Li #include <stdio.h>
10*9c5db199SXin Li #include <string.h>
11*9c5db199SXin Li #include <net/if.h>
12*9c5db199SXin Li #include <sys/types.h>
13*9c5db199SXin Li #include <sys/stat.h>
14*9c5db199SXin Li #include <fcntl.h>
15*9c5db199SXin Li #include <unistd.h>
16*9c5db199SXin Li #include <stdbool.h>
17*9c5db199SXin Li
18*9c5db199SXin Li #include <netlink/netlink.h>
19*9c5db199SXin Li #include <netlink/genl/genl.h>
20*9c5db199SXin Li #include <netlink/genl/family.h>
21*9c5db199SXin Li #include <netlink/genl/ctrl.h>
22*9c5db199SXin Li #include <netlink/msg.h>
23*9c5db199SXin Li #include <netlink/attr.h>
24*9c5db199SXin Li
25*9c5db199SXin Li #include "nl80211.h"
26*9c5db199SXin Li
27*9c5db199SXin Li #ifndef CONFIG_LIBNL20
28*9c5db199SXin Li /* libnl 2.0 compatibility code */
29*9c5db199SXin Li
30*9c5db199SXin Li # define nl_sock nl_handle
31*9c5db199SXin Li
nl_socket_alloc(void)32*9c5db199SXin Li static inline struct nl_handle *nl_socket_alloc(void)
33*9c5db199SXin Li {
34*9c5db199SXin Li return nl_handle_alloc();
35*9c5db199SXin Li }
36*9c5db199SXin Li
nl_socket_free(struct nl_sock * h)37*9c5db199SXin Li static inline void nl_socket_free(struct nl_sock *h)
38*9c5db199SXin Li {
39*9c5db199SXin Li nl_handle_destroy(h);
40*9c5db199SXin Li }
41*9c5db199SXin Li
__genl_ctrl_alloc_cache(struct nl_sock * h,struct nl_cache ** cache)42*9c5db199SXin Li static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, struct nl_cache **cache)
43*9c5db199SXin Li {
44*9c5db199SXin Li struct nl_cache *tmp = genl_ctrl_alloc_cache(h);
45*9c5db199SXin Li if (!tmp)
46*9c5db199SXin Li return -ENOMEM;
47*9c5db199SXin Li *cache = tmp;
48*9c5db199SXin Li return 0;
49*9c5db199SXin Li }
50*9c5db199SXin Li #define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache
51*9c5db199SXin Li #endif /* CONFIG_LIBNL20 */
52*9c5db199SXin Li
53*9c5db199SXin Li struct nl80211_state {
54*9c5db199SXin Li struct nl_sock *nl_sock;
55*9c5db199SXin Li struct nl_cache *nl_cache;
56*9c5db199SXin Li struct genl_family *nl80211;
57*9c5db199SXin Li };
58*9c5db199SXin Li
nl80211_init(struct nl80211_state * state)59*9c5db199SXin Li static int nl80211_init(struct nl80211_state *state)
60*9c5db199SXin Li {
61*9c5db199SXin Li int err;
62*9c5db199SXin Li
63*9c5db199SXin Li state->nl_sock = nl_socket_alloc();
64*9c5db199SXin Li if (!state->nl_sock) {
65*9c5db199SXin Li fprintf(stderr, "Failed to allocate netlink socket.\n");
66*9c5db199SXin Li return -ENOMEM;
67*9c5db199SXin Li }
68*9c5db199SXin Li
69*9c5db199SXin Li if (genl_connect(state->nl_sock)) {
70*9c5db199SXin Li fprintf(stderr, "Failed to connect to generic netlink.\n");
71*9c5db199SXin Li err = -ENOLINK;
72*9c5db199SXin Li goto out_handle_destroy;
73*9c5db199SXin Li }
74*9c5db199SXin Li
75*9c5db199SXin Li if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) {
76*9c5db199SXin Li fprintf(stderr, "Failed to allocate generic netlink cache.\n");
77*9c5db199SXin Li err = -ENOMEM;
78*9c5db199SXin Li goto out_handle_destroy;
79*9c5db199SXin Li }
80*9c5db199SXin Li
81*9c5db199SXin Li state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211");
82*9c5db199SXin Li if (!state->nl80211) {
83*9c5db199SXin Li fprintf(stderr, "nl80211 not found.\n");
84*9c5db199SXin Li err = -ENOENT;
85*9c5db199SXin Li goto out_cache_free;
86*9c5db199SXin Li }
87*9c5db199SXin Li
88*9c5db199SXin Li return 0;
89*9c5db199SXin Li
90*9c5db199SXin Li out_cache_free:
91*9c5db199SXin Li nl_cache_free(state->nl_cache);
92*9c5db199SXin Li out_handle_destroy:
93*9c5db199SXin Li nl_socket_free(state->nl_sock);
94*9c5db199SXin Li return err;
95*9c5db199SXin Li }
96*9c5db199SXin Li
nl80211_cleanup(struct nl80211_state * state)97*9c5db199SXin Li static void nl80211_cleanup(struct nl80211_state *state)
98*9c5db199SXin Li {
99*9c5db199SXin Li genl_family_put(state->nl80211);
100*9c5db199SXin Li nl_cache_free(state->nl_cache);
101*9c5db199SXin Li nl_socket_free(state->nl_sock);
102*9c5db199SXin Li }
103*9c5db199SXin Li
104*9c5db199SXin Li static const char *argv0;
105*9c5db199SXin Li
phy_lookup(char * name)106*9c5db199SXin Li static int phy_lookup(char *name)
107*9c5db199SXin Li {
108*9c5db199SXin Li char buf[200];
109*9c5db199SXin Li int fd, pos;
110*9c5db199SXin Li
111*9c5db199SXin Li snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name);
112*9c5db199SXin Li
113*9c5db199SXin Li fd = open(buf, O_RDONLY);
114*9c5db199SXin Li if (fd < 0)
115*9c5db199SXin Li return -1;
116*9c5db199SXin Li pos = read(fd, buf, sizeof(buf) - 1);
117*9c5db199SXin Li if (pos < 0)
118*9c5db199SXin Li return -1;
119*9c5db199SXin Li buf[pos] = '\0';
120*9c5db199SXin Li return atoi(buf);
121*9c5db199SXin Li }
122*9c5db199SXin Li
123*9c5db199SXin Li enum {
124*9c5db199SXin Li CHECK_IS_HT20 = 0x00000001,
125*9c5db199SXin Li CHECK_IS_HT40 = 0x00000002,
126*9c5db199SXin Li CHECK_IS_PSMP = 0x00000004,
127*9c5db199SXin Li CHECK_IS_AMPDU = 0x00000008,
128*9c5db199SXin Li CHECK_IS_AMSDU = 0x00000010,
129*9c5db199SXin Li CHECK_IS_SMPS = 0x00000020,
130*9c5db199SXin Li CHECK_IS_STA = 0x00000040,
131*9c5db199SXin Li CHECK_IS_AP = 0x00000080,
132*9c5db199SXin Li CHECK_IS_IBSS = 0x00000100,
133*9c5db199SXin Li CHECK_IS_MBSS = 0x00000200,
134*9c5db199SXin Li CHECK_IS_MONITOR = 0x00000400,
135*9c5db199SXin Li CHECK_BANDS = 0x00000800,
136*9c5db199SXin Li CHECK_FREQS = 0x00001000,
137*9c5db199SXin Li CHECK_RATES = 0x00002000,
138*9c5db199SXin Li CHECK_MCS = 0x00004000,
139*9c5db199SXin Li CHECK_AMPDU_DENS = 0x00008000,
140*9c5db199SXin Li CHECK_AMPDU_FACT = 0x00010000,
141*9c5db199SXin Li CHECK_AMSDU_LEN = 0x00020000,
142*9c5db199SXin Li CHECK_IS_LPDC = 0x00040000,
143*9c5db199SXin Li CHECK_IS_GREENFIELD = 0x00080000,
144*9c5db199SXin Li CHECK_IS_SGI20 = 0x00100000,
145*9c5db199SXin Li CHECK_IS_SGI40 = 0x00200000,
146*9c5db199SXin Li CHECK_IS_TXSTBC = 0x00400000,
147*9c5db199SXin Li CHECK_RXSTBC = 0x00800000,
148*9c5db199SXin Li CHECK_IS_DELBA = 0x01000000,
149*9c5db199SXin Li /* NB: must be in upper 16-bits to avoid HT caps */
150*9c5db199SXin Li CHECK_IS_24GHZ = 0x02000000,
151*9c5db199SXin Li CHECK_IS_5GHZ = 0x04000000,
152*9c5db199SXin Li CHECK_IS_11B = 0x08000000,
153*9c5db199SXin Li CHECK_IS_11G = 0x10000000,
154*9c5db199SXin Li CHECK_IS_11A = 0x20000000,
155*9c5db199SXin Li CHECK_IS_11N = 0x40000000,
156*9c5db199SXin Li };
157*9c5db199SXin Li
158*9c5db199SXin Li struct check {
159*9c5db199SXin Li const char *name;
160*9c5db199SXin Li int namelen;
161*9c5db199SXin Li int bits;
162*9c5db199SXin Li };
163*9c5db199SXin Li static const struct check checks[] = {
164*9c5db199SXin Li { "24ghz", 5, CHECK_IS_24GHZ },
165*9c5db199SXin Li { "5ghz", 4, CHECK_IS_5GHZ },
166*9c5db199SXin Li { "11b", 3, CHECK_IS_11B },
167*9c5db199SXin Li { "11g", 3, CHECK_IS_11G },
168*9c5db199SXin Li { "11a", 3, CHECK_IS_11A },
169*9c5db199SXin Li { "11n", 3, CHECK_IS_11N },
170*9c5db199SXin Li { "ht20", 4, CHECK_IS_HT20 },
171*9c5db199SXin Li { "ht40", 4, CHECK_IS_HT40 },
172*9c5db199SXin Li { "psmp", 5, CHECK_IS_PSMP },
173*9c5db199SXin Li { "ampdu", 5, CHECK_IS_AMPDU },
174*9c5db199SXin Li { "amsdu", 5, CHECK_IS_AMSDU },
175*9c5db199SXin Li { "smps", 4, CHECK_IS_SMPS },
176*9c5db199SXin Li { "sta", 3, CHECK_IS_STA },
177*9c5db199SXin Li { "ap", 2, CHECK_IS_AP },
178*9c5db199SXin Li { "ibss", 4, CHECK_IS_IBSS },
179*9c5db199SXin Li { "mbss", 4, CHECK_IS_MBSS },
180*9c5db199SXin Li { "mon", 3, CHECK_IS_MONITOR },
181*9c5db199SXin Li { "bands", 4, CHECK_BANDS },
182*9c5db199SXin Li { "freqs", 4, CHECK_FREQS },
183*9c5db199SXin Li { "rates", 4, CHECK_RATES },
184*9c5db199SXin Li { "mcs", 3, CHECK_MCS },
185*9c5db199SXin Li { "ampdu_dens", 10, CHECK_AMPDU_DENS },
186*9c5db199SXin Li { "ampdu_fact", 10, CHECK_AMPDU_FACT },
187*9c5db199SXin Li { "amsdu_len", 9, CHECK_AMSDU_LEN },
188*9c5db199SXin Li { "lpdc", 4, CHECK_IS_LPDC },
189*9c5db199SXin Li { "green", 5, CHECK_IS_GREENFIELD },
190*9c5db199SXin Li { "sgi20", 5, CHECK_IS_SGI20 },
191*9c5db199SXin Li { "sgi40", 5, CHECK_IS_SGI40 },
192*9c5db199SXin Li { "txstbc", 6, CHECK_IS_TXSTBC },
193*9c5db199SXin Li { "rxstbc", 6, CHECK_RXSTBC },
194*9c5db199SXin Li { "delba", 5, CHECK_IS_DELBA },
195*9c5db199SXin Li { "all", 3, -1 },
196*9c5db199SXin Li { NULL }
197*9c5db199SXin Li };
198*9c5db199SXin Li
find_check_byname(const char * name)199*9c5db199SXin Li static const struct check *find_check_byname(const char *name)
200*9c5db199SXin Li {
201*9c5db199SXin Li const struct check *p;
202*9c5db199SXin Li
203*9c5db199SXin Li for (p = checks; p->name != NULL; p++)
204*9c5db199SXin Li if (strncasecmp(p->name, name, p->namelen) == 0)
205*9c5db199SXin Li return p;
206*9c5db199SXin Li return NULL;
207*9c5db199SXin Li }
208*9c5db199SXin Li
209*9c5db199SXin Li #if 0
210*9c5db199SXin Li static const struct check *find_check_bybits(int bits)
211*9c5db199SXin Li {
212*9c5db199SXin Li const struct check *p;
213*9c5db199SXin Li
214*9c5db199SXin Li for (p = checks; p->name != NULL; p++)
215*9c5db199SXin Li if (p->bits == bits)
216*9c5db199SXin Li return p;
217*9c5db199SXin Li return NULL;
218*9c5db199SXin Li }
219*9c5db199SXin Li #endif
220*9c5db199SXin Li
check_iftype(struct nlattr * tb_msg[],int nl_type)221*9c5db199SXin Li static int check_iftype(struct nlattr *tb_msg[], int nl_type)
222*9c5db199SXin Li {
223*9c5db199SXin Li struct nlattr *nl_mode;
224*9c5db199SXin Li int rem_mode;
225*9c5db199SXin Li
226*9c5db199SXin Li if (!tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES])
227*9c5db199SXin Li return 0;
228*9c5db199SXin Li
229*9c5db199SXin Li nla_for_each_nested(nl_mode, tb_msg[NL80211_ATTR_SUPPORTED_IFTYPES], rem_mode)
230*9c5db199SXin Li if (nl_mode->nla_type == nl_type)
231*9c5db199SXin Li return 1;
232*9c5db199SXin Li return 0;
233*9c5db199SXin Li }
234*9c5db199SXin Li
get_max_mcs(unsigned char * mcs)235*9c5db199SXin Li static unsigned int get_max_mcs(unsigned char *mcs)
236*9c5db199SXin Li {
237*9c5db199SXin Li unsigned int mcs_bit, max;
238*9c5db199SXin Li
239*9c5db199SXin Li max = 0;
240*9c5db199SXin Li for (mcs_bit = 0; mcs_bit <= 76; mcs_bit++) {
241*9c5db199SXin Li unsigned int mcs_octet = mcs_bit/8;
242*9c5db199SXin Li unsigned int MCS_RATE_BIT = 1 << mcs_bit % 8;
243*9c5db199SXin Li bool mcs_rate_idx_set;
244*9c5db199SXin Li
245*9c5db199SXin Li mcs_rate_idx_set = !!(mcs[mcs_octet] & MCS_RATE_BIT);
246*9c5db199SXin Li
247*9c5db199SXin Li if (!mcs_rate_idx_set)
248*9c5db199SXin Li continue;
249*9c5db199SXin Li
250*9c5db199SXin Li if (mcs_bit > max)
251*9c5db199SXin Li max = mcs_bit;
252*9c5db199SXin Li }
253*9c5db199SXin Li return max;
254*9c5db199SXin Li }
255*9c5db199SXin Li
pbool(const char * tag,int v)256*9c5db199SXin Li static void pbool(const char *tag, int v)
257*9c5db199SXin Li {
258*9c5db199SXin Li printf("%s: %s\n", tag, v ? "true" : "false");
259*9c5db199SXin Li }
260*9c5db199SXin Li
pint(const char * tag,int v)261*9c5db199SXin Li static void pint(const char *tag, int v)
262*9c5db199SXin Li {
263*9c5db199SXin Li printf("%s: %d\n", tag, v);
264*9c5db199SXin Li }
265*9c5db199SXin Li
prate(const char * tag,int v)266*9c5db199SXin Li static void prate(const char *tag, int v)
267*9c5db199SXin Li {
268*9c5db199SXin Li printf("%s: %2.1f\n", tag, 0.1*v);
269*9c5db199SXin Li }
270*9c5db199SXin Li
check_phy_handler(struct nl_msg * msg,void * arg)271*9c5db199SXin Li static int check_phy_handler(struct nl_msg *msg, void *arg)
272*9c5db199SXin Li {
273*9c5db199SXin Li struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
274*9c5db199SXin Li struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
275*9c5db199SXin Li uintptr_t checks = (uintptr_t) arg;
276*9c5db199SXin Li
277*9c5db199SXin Li struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
278*9c5db199SXin Li
279*9c5db199SXin Li struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
280*9c5db199SXin Li static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
281*9c5db199SXin Li [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
282*9c5db199SXin Li [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
283*9c5db199SXin Li [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
284*9c5db199SXin Li [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
285*9c5db199SXin Li [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
286*9c5db199SXin Li [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
287*9c5db199SXin Li };
288*9c5db199SXin Li
289*9c5db199SXin Li struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
290*9c5db199SXin Li static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
291*9c5db199SXin Li [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
292*9c5db199SXin Li [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
293*9c5db199SXin Li };
294*9c5db199SXin Li
295*9c5db199SXin Li struct nlattr *nl_band;
296*9c5db199SXin Li struct nlattr *nl_freq;
297*9c5db199SXin Li struct nlattr *nl_rate;
298*9c5db199SXin Li int rem_band, rem_freq, rem_rate, phy_caps;
299*9c5db199SXin Li int amsdu_len, ampdu_fact, ampdu_dens, max_mcs, max_rate;
300*9c5db199SXin Li
301*9c5db199SXin Li nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
302*9c5db199SXin Li genlmsg_attrlen(gnlh, 0), NULL);
303*9c5db199SXin Li
304*9c5db199SXin Li if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
305*9c5db199SXin Li return NL_SKIP;
306*9c5db199SXin Li
307*9c5db199SXin Li phy_caps = 0;
308*9c5db199SXin Li amsdu_len = 0;
309*9c5db199SXin Li ampdu_fact = 0;
310*9c5db199SXin Li ampdu_dens = 0;
311*9c5db199SXin Li max_mcs = 0;
312*9c5db199SXin Li max_rate = 0;
313*9c5db199SXin Li /* NB: merge each band's findings; this stuff is silly */
314*9c5db199SXin Li nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
315*9c5db199SXin Li nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
316*9c5db199SXin Li nla_len(nl_band), NULL);
317*9c5db199SXin Li
318*9c5db199SXin Li if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
319*9c5db199SXin Li unsigned short caps = nla_get_u16(tb_band[NL80211_BAND_ATTR_HT_CAPA]);
320*9c5db199SXin Li int len;
321*9c5db199SXin Li
322*9c5db199SXin Li /* XXX not quite right but close enough */
323*9c5db199SXin Li phy_caps |= CHECK_IS_11N | caps;
324*9c5db199SXin Li len = 0xeff + ((caps & 0x0800) << 1);
325*9c5db199SXin Li if (len > amsdu_len)
326*9c5db199SXin Li amsdu_len = len;
327*9c5db199SXin Li }
328*9c5db199SXin Li if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
329*9c5db199SXin Li unsigned char factor = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]);
330*9c5db199SXin Li int fact = (1<<(13+factor))-1;
331*9c5db199SXin Li if (fact > ampdu_fact)
332*9c5db199SXin Li ampdu_fact = fact;
333*9c5db199SXin Li }
334*9c5db199SXin Li if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
335*9c5db199SXin Li unsigned char dens = nla_get_u8(tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]);
336*9c5db199SXin Li if (dens > ampdu_dens)
337*9c5db199SXin Li ampdu_dens = dens;
338*9c5db199SXin Li }
339*9c5db199SXin Li if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
340*9c5db199SXin Li nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]) == 16) {
341*9c5db199SXin Li /* As defined in 7.3.2.57.4 Supported MCS Set field */
342*9c5db199SXin Li unsigned char *mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
343*9c5db199SXin Li int max = get_max_mcs(&mcs[0]);
344*9c5db199SXin Li if (max > max_mcs)
345*9c5db199SXin Li max_mcs = max;
346*9c5db199SXin Li }
347*9c5db199SXin Li
348*9c5db199SXin Li nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
349*9c5db199SXin Li uint32_t freq;
350*9c5db199SXin Li
351*9c5db199SXin Li nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
352*9c5db199SXin Li nla_data(nl_freq), nla_len(nl_freq),
353*9c5db199SXin Li freq_policy);
354*9c5db199SXin Li if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
355*9c5db199SXin Li continue;
356*9c5db199SXin Li #if 0
357*9c5db199SXin Li /* NB: we care about device caps, not regulatory */
358*9c5db199SXin Li if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
359*9c5db199SXin Li continue;
360*9c5db199SXin Li #endif
361*9c5db199SXin Li freq = nla_get_u32(
362*9c5db199SXin Li tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
363*9c5db199SXin Li if (checks & CHECK_FREQS)
364*9c5db199SXin Li pint("freq", freq);
365*9c5db199SXin Li
366*9c5db199SXin Li /* NB: approximate band boundaries, we get no help */
367*9c5db199SXin Li if (2000 <= freq && freq <= 3000)
368*9c5db199SXin Li phy_caps |= CHECK_IS_24GHZ;
369*9c5db199SXin Li else if (4000 <= freq && freq <= 6000)
370*9c5db199SXin Li phy_caps |= CHECK_IS_5GHZ;
371*9c5db199SXin Li }
372*9c5db199SXin Li
373*9c5db199SXin Li nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
374*9c5db199SXin Li int rate;
375*9c5db199SXin Li
376*9c5db199SXin Li nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
377*9c5db199SXin Li nla_len(nl_rate), rate_policy);
378*9c5db199SXin Li if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
379*9c5db199SXin Li continue;
380*9c5db199SXin Li rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
381*9c5db199SXin Li if (rate > max_rate)
382*9c5db199SXin Li max_rate = rate;
383*9c5db199SXin Li }
384*9c5db199SXin Li }
385*9c5db199SXin Li #if 0
386*9c5db199SXin Li /* NB: 11n =>'s legacy support */
387*9c5db199SXin Li if (phy_caps & CHECK_IS_11N) {
388*9c5db199SXin Li if (phy_caps & CHECK_IS_24GHZ)
389*9c5db199SXin Li phy_caps |= CHECK_IS_11B | CHECK_IS_11G;
390*9c5db199SXin Li if (phy_caps & CHECK_IS_5GHZ)
391*9c5db199SXin Li phy_caps |= CHECK_IS_11A;
392*9c5db199SXin Li }
393*9c5db199SXin Li #else
394*9c5db199SXin Li /* XXX no way to figure this out; just force 'em */
395*9c5db199SXin Li if (phy_caps & CHECK_IS_24GHZ)
396*9c5db199SXin Li phy_caps |= CHECK_IS_11B | CHECK_IS_11G;
397*9c5db199SXin Li if (phy_caps & CHECK_IS_5GHZ)
398*9c5db199SXin Li phy_caps |= CHECK_IS_11A;
399*9c5db199SXin Li #endif
400*9c5db199SXin Li
401*9c5db199SXin Li #define PBOOL(c, b, name) if (checks & (c)) pbool(name, phy_caps & (b))
402*9c5db199SXin Li PBOOL(CHECK_IS_24GHZ, CHECK_IS_24GHZ, "24ghz");
403*9c5db199SXin Li PBOOL(CHECK_IS_5GHZ, CHECK_IS_5GHZ, "5ghz");
404*9c5db199SXin Li PBOOL(CHECK_IS_11B, CHECK_IS_11B, "11b");
405*9c5db199SXin Li PBOOL(CHECK_IS_11G, CHECK_IS_11G, "11g");
406*9c5db199SXin Li PBOOL(CHECK_IS_11A, CHECK_IS_11A, "11a");
407*9c5db199SXin Li PBOOL(CHECK_IS_11N, CHECK_IS_11N, "11n");
408*9c5db199SXin Li PBOOL(CHECK_IS_LPDC, 0x1, "lpdc");
409*9c5db199SXin Li PBOOL(CHECK_IS_HT20, CHECK_IS_11N, "ht20");
410*9c5db199SXin Li PBOOL(CHECK_IS_HT40, 0x2, "ht40");
411*9c5db199SXin Li if (checks & CHECK_IS_SMPS)
412*9c5db199SXin Li pbool("smps", ((phy_caps & 0x000c) >> 2) < 2);
413*9c5db199SXin Li PBOOL(CHECK_IS_GREENFIELD, 0x10, "green");
414*9c5db199SXin Li PBOOL(CHECK_IS_SGI20, 0x20, "sgi20");
415*9c5db199SXin Li PBOOL(CHECK_IS_SGI40, 0x40, "sgi40");
416*9c5db199SXin Li PBOOL(CHECK_IS_TXSTBC, 0x40, "txstbc");
417*9c5db199SXin Li PBOOL(CHECK_RXSTBC, 0x300, "rxstbc");
418*9c5db199SXin Li PBOOL(CHECK_IS_DELBA, 0x400, "delba");
419*9c5db199SXin Li #if 0
420*9c5db199SXin Li PBOOL(CHECK_IS_DSSCCK, 0x1000, "dsscck");
421*9c5db199SXin Li #endif
422*9c5db199SXin Li PBOOL(CHECK_IS_PSMP, 0x2000, "psmp");
423*9c5db199SXin Li #if 0
424*9c5db199SXin Li PBOOL(CHECK_IS_INTOL, 0x4000, "intol");
425*9c5db199SXin Li #endif
426*9c5db199SXin Li #if 0
427*9c5db199SXin Li PBOOL(CHECK_IS_LSIGTXOP, 0x8000, "lsigtxop");
428*9c5db199SXin Li #endif
429*9c5db199SXin Li #undef PBOOL
430*9c5db199SXin Li if (checks & CHECK_AMSDU_LEN)
431*9c5db199SXin Li pint("amsdu_len", amsdu_len);
432*9c5db199SXin Li if (checks & CHECK_AMPDU_FACT)
433*9c5db199SXin Li pint("ampdu_fact", ampdu_fact);
434*9c5db199SXin Li if (checks & CHECK_AMPDU_DENS)
435*9c5db199SXin Li pint("ampdu_dens", ampdu_dens);
436*9c5db199SXin Li if (checks & CHECK_RATES)
437*9c5db199SXin Li prate("rate", max_rate);
438*9c5db199SXin Li if (checks & CHECK_MCS)
439*9c5db199SXin Li pint("mcs", max_mcs);
440*9c5db199SXin Li
441*9c5db199SXin Li if (checks & CHECK_IS_STA)
442*9c5db199SXin Li pbool("sta", check_iftype(tb_msg, NL80211_IFTYPE_STATION));
443*9c5db199SXin Li if (checks & CHECK_IS_IBSS)
444*9c5db199SXin Li pbool("ibss", check_iftype(tb_msg, NL80211_IFTYPE_ADHOC));
445*9c5db199SXin Li if (checks & CHECK_IS_AP)
446*9c5db199SXin Li pbool("ap", check_iftype(tb_msg, NL80211_IFTYPE_AP));
447*9c5db199SXin Li if (checks & CHECK_IS_MBSS)
448*9c5db199SXin Li pbool("mbss", check_iftype(tb_msg, NL80211_IFTYPE_MESH_POINT));
449*9c5db199SXin Li if (checks & CHECK_IS_MONITOR)
450*9c5db199SXin Li pbool("mon", check_iftype(tb_msg, NL80211_IFTYPE_MONITOR));
451*9c5db199SXin Li
452*9c5db199SXin Li return NL_SKIP;
453*9c5db199SXin Li }
454*9c5db199SXin Li
check_phy_caps(struct nl80211_state * state,struct nl_cb * cb,struct nl_msg * msg,int argc,char ** argv)455*9c5db199SXin Li static int check_phy_caps(struct nl80211_state *state,
456*9c5db199SXin Li struct nl_cb *cb,
457*9c5db199SXin Li struct nl_msg *msg,
458*9c5db199SXin Li int argc, char **argv)
459*9c5db199SXin Li {
460*9c5db199SXin Li int checks = 0;
461*9c5db199SXin Li for (; argc > 0; argc--, argv++) {
462*9c5db199SXin Li const struct check *p = find_check_byname(argv[0]);
463*9c5db199SXin Li if (p == NULL) {
464*9c5db199SXin Li fprintf(stderr, "invalid check %s\n", argv[0]);
465*9c5db199SXin Li return 3; /* XXX whatever? */
466*9c5db199SXin Li }
467*9c5db199SXin Li checks |= p->bits;
468*9c5db199SXin Li }
469*9c5db199SXin Li nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, check_phy_handler,
470*9c5db199SXin Li (void *)(uintptr_t) checks);
471*9c5db199SXin Li return 0;
472*9c5db199SXin Li }
473*9c5db199SXin Li
error_handler(struct sockaddr_nl * nla,struct nlmsgerr * err,void * arg)474*9c5db199SXin Li static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
475*9c5db199SXin Li void *arg)
476*9c5db199SXin Li {
477*9c5db199SXin Li int *ret = arg;
478*9c5db199SXin Li *ret = err->error;
479*9c5db199SXin Li return NL_STOP;
480*9c5db199SXin Li }
481*9c5db199SXin Li
finish_handler(struct nl_msg * msg,void * arg)482*9c5db199SXin Li static int finish_handler(struct nl_msg *msg, void *arg)
483*9c5db199SXin Li {
484*9c5db199SXin Li int *ret = arg;
485*9c5db199SXin Li *ret = 0;
486*9c5db199SXin Li return NL_SKIP;
487*9c5db199SXin Li }
488*9c5db199SXin Li
ack_handler(struct nl_msg * msg,void * arg)489*9c5db199SXin Li static int ack_handler(struct nl_msg *msg, void *arg)
490*9c5db199SXin Li {
491*9c5db199SXin Li int *ret = arg;
492*9c5db199SXin Li *ret = 0;
493*9c5db199SXin Li return NL_STOP;
494*9c5db199SXin Li }
495*9c5db199SXin Li
__handle_cmd(struct nl80211_state * state,int argc,char ** argv)496*9c5db199SXin Li static int __handle_cmd(struct nl80211_state *state, int argc, char **argv)
497*9c5db199SXin Li {
498*9c5db199SXin Li struct nl_cb *cb;
499*9c5db199SXin Li struct nl_msg *msg;
500*9c5db199SXin Li int devidx, err;
501*9c5db199SXin Li
502*9c5db199SXin Li if (argc <= 1)
503*9c5db199SXin Li return 1;
504*9c5db199SXin Li
505*9c5db199SXin Li devidx = phy_lookup(*argv);
506*9c5db199SXin Li if (devidx < 0)
507*9c5db199SXin Li return -errno;
508*9c5db199SXin Li argc--, argv++;
509*9c5db199SXin Li
510*9c5db199SXin Li msg = nlmsg_alloc();
511*9c5db199SXin Li if (!msg) {
512*9c5db199SXin Li fprintf(stderr, "failed to allocate netlink message\n");
513*9c5db199SXin Li return 2;
514*9c5db199SXin Li }
515*9c5db199SXin Li
516*9c5db199SXin Li cb = nl_cb_alloc(NL_CB_DEFAULT);
517*9c5db199SXin Li if (!cb) {
518*9c5db199SXin Li fprintf(stderr, "failed to allocate netlink callbacks\n");
519*9c5db199SXin Li err = 2;
520*9c5db199SXin Li goto out_free_msg;
521*9c5db199SXin Li }
522*9c5db199SXin Li
523*9c5db199SXin Li genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0,
524*9c5db199SXin Li 0, NL80211_CMD_GET_WIPHY, 0);
525*9c5db199SXin Li NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx);
526*9c5db199SXin Li
527*9c5db199SXin Li err = check_phy_caps(state, cb, msg, argc, argv);
528*9c5db199SXin Li if (err)
529*9c5db199SXin Li goto out;
530*9c5db199SXin Li
531*9c5db199SXin Li err = nl_send_auto_complete(state->nl_sock, msg);
532*9c5db199SXin Li if (err < 0)
533*9c5db199SXin Li goto out;
534*9c5db199SXin Li
535*9c5db199SXin Li err = 1;
536*9c5db199SXin Li
537*9c5db199SXin Li nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
538*9c5db199SXin Li nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
539*9c5db199SXin Li nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
540*9c5db199SXin Li
541*9c5db199SXin Li while (err > 0)
542*9c5db199SXin Li nl_recvmsgs(state->nl_sock, cb);
543*9c5db199SXin Li out:
544*9c5db199SXin Li nl_cb_put(cb);
545*9c5db199SXin Li out_free_msg:
546*9c5db199SXin Li nlmsg_free(msg);
547*9c5db199SXin Li return err;
548*9c5db199SXin Li nla_put_failure:
549*9c5db199SXin Li fprintf(stderr, "building message failed\n");
550*9c5db199SXin Li return 2;
551*9c5db199SXin Li }
552*9c5db199SXin Li
main(int argc,char ** argv)553*9c5db199SXin Li int main(int argc, char **argv)
554*9c5db199SXin Li {
555*9c5db199SXin Li struct nl80211_state nlstate;
556*9c5db199SXin Li int err;
557*9c5db199SXin Li
558*9c5db199SXin Li argc--;
559*9c5db199SXin Li argv0 = *argv++;
560*9c5db199SXin Li
561*9c5db199SXin Li err = nl80211_init(&nlstate);
562*9c5db199SXin Li if (err == 0) {
563*9c5db199SXin Li if (argc > 1 && strncmp(*argv, "phy", 3) == 0) {
564*9c5db199SXin Li err = __handle_cmd(&nlstate, argc, argv);
565*9c5db199SXin Li if (err < 0)
566*9c5db199SXin Li fprintf(stderr, "command failed: %s (%d)\n",
567*9c5db199SXin Li strerror(-err), err);
568*9c5db199SXin Li else if (err)
569*9c5db199SXin Li fprintf(stderr, "command failed: err %d\n", err);
570*9c5db199SXin Li } else {
571*9c5db199SXin Li fprintf(stderr, "usage: %s phyX [args]\n", argv0);
572*9c5db199SXin Li err = 1;
573*9c5db199SXin Li }
574*9c5db199SXin Li nl80211_cleanup(&nlstate);
575*9c5db199SXin Li }
576*9c5db199SXin Li return err;
577*9c5db199SXin Li }
578