• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/lib/nl80211.c

  1 /*
  2 Copyright 2021 Jo-Philipp Wich <jo@mein.io>
  3 
  4 Licensed under the Apache License, Version 2.0 (the "License");
  5 you may not use this file except in compliance with the License.
  6 You may obtain a copy of the License at
  7 
  8         http://www.apache.org/licenses/LICENSE-2.0
  9 
 10 Unless required by applicable law or agreed to in writing, software
 11 distributed under the License is distributed on an "AS IS" BASIS,
 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 See the License for the specific language governing permissions and
 14 limitations under the License.
 15 */
 16 
 17 /**
 18 * # Wireless Netlink
 19 *
 20 * The `nl80211` module provides functions for interacting with the nl80211 netlink interface
 21 * for wireless networking configuration and management.
 22 *
 23 * Functions can be individually imported and directly accessed using the
 24 * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#named_import named import}
 25 * syntax:
 26 *
 27 *   ```javascript
 28 *   import { error, request, listener, waitfor, const } from 'nl80211';
 29 *
 30 *   // Send a nl80211 request
 31 *   let response = request(const.NL80211_CMD_GET_WIPHY, 0, { wiphy: 0 });
 32 *
 33 *   // Create a listener for wireless events
 34 *   let wifiListener = listener((msg) => {
 35 *       print('Received wireless event:', msg, '\n');
 36 *   }, [const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_DEL_INTERFACE]);
 37 *
 38 *   // Wait for a specific nl80211 event
 39 *   let event = waitfor([const.NL80211_CMD_NEW_SCAN_RESULTS], 5000);
 40 *   if (event)
 41 *       print('Received scan results:', event.msg, '\n');
 42 *   ```
 43 *
 44 * Alternatively, the module namespace can be imported
 45 * using a wildcard import statement:
 46 *
 47 *   ```javascript
 48 *   import * as nl80211 from 'nl80211';
 49 *
 50 *   // Send a nl80211 request
 51 *   let response = nl80211.request(nl80211.const.NL80211_CMD_GET_WIPHY, 0, { wiphy: 0 });
 52 *
 53 *   // Create a listener for wireless events
 54 *   let listener = nl80211.listener((msg) => {
 55 *       print('Received wireless event:', msg, '\n');
 56 *   }, [nl80211.const.NL80211_CMD_NEW_INTERFACE, nl80211.const.NL80211_CMD_DEL_INTERFACE]);
 57 *   ```
 58 *
 59 * Additionally, the nl80211 module namespace may also be imported by invoking
 60 * the `ucode` interpreter with the `-lnl80211` switch.
 61 *
 62 * @module nl80211
 63 */
 64 
 65 #include <stdio.h>
 66 #include <stdint.h>
 67 #include <stdbool.h>
 68 #include <stdarg.h>
 69 #include <unistd.h>
 70 #include <errno.h>
 71 #include <string.h>
 72 #include <limits.h>
 73 #include <math.h>
 74 #include <assert.h>
 75 #include <fcntl.h>
 76 #include <poll.h>
 77 #include <time.h>
 78 
 79 #include <net/if.h>
 80 #include <netinet/ether.h>
 81 #include <arpa/inet.h>
 82 #include <netlink/msg.h>
 83 #include <netlink/attr.h>
 84 #include <netlink/socket.h>
 85 #include <netlink/genl/genl.h>
 86 #include <netlink/genl/family.h>
 87 #include <netlink/genl/ctrl.h>
 88 
 89 #include <linux/nl80211.h>
 90 #include <linux/ieee80211.h>
 91 #include <linux/mac80211_hwsim.h>
 92 #include <libubox/uloop.h>
 93 
 94 #include "ucode/module.h"
 95 #include "ucode/platform.h"
 96 
 97 #define DIV_ROUND_UP(n, d)      (((n) + (d) - 1) / (d))
 98 
 99 #define err_return(code, ...) do { set_error(code, __VA_ARGS__); return NULL; } while(0)
100 
101 /* Modified downstream nl80211.h headers may disable certain unsupported
102  * attributes by setting the corresponding defines to 0x10000 without having
103  * to patch the attribute dictionaries within this file. */
104 
105 #define NL80211_ATTR_NOT_IMPLEMENTED 0x10000
106 
107 #define NL80211_CMDS_BITMAP_SIZE        DIV_ROUND_UP(NL80211_CMD_MAX + 1, 32)
108 
109 static struct {
110         int code;
111         char *msg;
112 } last_error;
113 
114 __attribute__((format(printf, 2, 3))) static void
115 set_error(int errcode, const char *fmt, ...) {
116         va_list ap;
117 
118         if (errcode == -(NLE_MAX + 1))
119                 return;
120 
121         free(last_error.msg);
122 
123         last_error.code = errcode;
124         last_error.msg = NULL;
125 
126         if (fmt) {
127                 va_start(ap, fmt);
128                 xvasprintf(&last_error.msg, fmt, ap);
129                 va_end(ap);
130         }
131 }
132 
133 static uc_resource_type_t *listener_type;
134 static uc_value_t *listener_registry;
135 static uc_vm_t *listener_vm;
136 
137 typedef struct {
138         uint32_t cmds[NL80211_CMDS_BITMAP_SIZE];
139         size_t index;
140 } uc_nl_listener_t;
141 
142 static bool
143 uc_nl_parse_u32(uc_value_t *val, uint32_t *n)
144 {
145         uint64_t u;
146 
147         u = ucv_to_unsigned(val);
148 
149         if (errno != 0 || u > UINT32_MAX)
150                 return false;
151 
152         *n = (uint32_t)u;
153 
154         return true;
155 }
156 
157 static bool
158 uc_nl_parse_s32(uc_value_t *val, uint32_t *n)
159 {
160         int64_t i;
161 
162         i = ucv_to_integer(val);
163 
164         if (errno != 0 || i < INT32_MIN || i > INT32_MAX)
165                 return false;
166 
167         *n = (uint32_t)i;
168 
169         return true;
170 }
171 
172 static bool
173 uc_nl_parse_u64(uc_value_t *val, uint64_t *n)
174 {
175         *n = ucv_to_unsigned(val);
176 
177         return (errno == 0);
178 }
179 
180 static bool
181 uc_nl_parse_ipaddr(uc_vm_t *vm, uc_value_t *val, struct in_addr *in)
182 {
183         char *s = ucv_to_string(vm, val);
184         bool valid = true;
185 
186         if (!s)
187                 return false;
188 
189         valid = (inet_pton(AF_INET, s, in) == 1);
190 
191         free(s);
192 
193         return valid;
194 }
195 
196 typedef enum {
197         DT_FLAG,
198         DT_BOOL,
199         DT_U8,
200         DT_S8,
201         DT_U16,
202         DT_U32,
203         DT_S32,
204         DT_U64,
205         DT_STRING,
206         DT_NETDEV,
207         DT_LLADDR,
208         DT_INADDR,
209         DT_NESTED,
210         DT_HT_MCS,
211         DT_HT_CAP,
212         DT_VHT_MCS,
213         DT_HE_MCS,
214         DT_IE,
215 } uc_nl_attr_datatype_t;
216 
217 enum {
218         DF_NO_SET = (1 << 0),
219         DF_MULTIPLE = (1 << 1),
220         DF_AUTOIDX = (1 << 2),
221         DF_TYPEIDX = (1 << 3),
222         DF_OFFSET1 = (1 << 4),
223         DF_ARRAY = (1 << 5),
224         DF_BINARY = (1 << 6),
225         DF_RELATED = (1 << 7),
226         DF_REPEATED = (1 << 8),
227 };
228 
229 typedef struct uc_nl_attr_spec {
230         size_t attr;
231         const char *key;
232         uc_nl_attr_datatype_t type;
233         uint32_t flags;
234         const void *auxdata;
235 } uc_nl_attr_spec_t;
236 
237 typedef struct uc_nl_nested_spec {
238         size_t headsize;
239         size_t nattrs;
240         const uc_nl_attr_spec_t attrs[];
241 } uc_nl_nested_spec_t;
242 
243 #define SIZE(type) (void *)(uintptr_t)sizeof(struct type)
244 #define MEMBER(type, field) (void *)(uintptr_t)offsetof(struct type, field)
245 #define ATTRID(id) (void *)(uintptr_t)(id)
246 
247 static const uc_nl_nested_spec_t nl80211_cqm_nla = {
248         .headsize = 0,
249         .nattrs = 5,
250         .attrs = {
251                 { NL80211_ATTR_CQM_PKT_LOSS_EVENT, "cqm_pkt_loss_event", DT_U32, 0, NULL },
252                 { NL80211_ATTR_CQM_RSSI_HYST, "cqm_rssi_hyst", DT_U32, 0, NULL },
253                 { NL80211_ATTR_CQM_RSSI_LEVEL, "cqm_rssi_level", DT_S32, 0, NULL },
254                 { NL80211_ATTR_CQM_RSSI_THOLD, "cqm_rssi_thold", DT_U32, 0, NULL },
255                 { NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, "cqm_rssi_threshold_event", DT_U32, 0, NULL },
256         }
257 };
258 
259 static const uc_nl_nested_spec_t nl80211_ftm_responder_stats_nla = {
260         .headsize = 0,
261         .nattrs = 9,
262         .attrs = {
263                 { NL80211_FTM_STATS_SUCCESS_NUM, "success_num", DT_U32, 0, NULL },
264                 { NL80211_FTM_STATS_PARTIAL_NUM, "partial_num", DT_U32, 0, NULL },
265                 { NL80211_FTM_STATS_FAILED_NUM, "failed_num", DT_U32, 0, NULL },
266                 { NL80211_FTM_STATS_ASAP_NUM, "asap_num", DT_U32, 0, NULL },
267                 { NL80211_FTM_STATS_NON_ASAP_NUM, "non_asap_num", DT_U32, 0, NULL },
268                 { NL80211_FTM_STATS_TOTAL_DURATION_MSEC, "total_duration_msec", DT_U64, 0, NULL },
269                 { NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM, "unknown_triggers_num", DT_U32, 0, NULL },
270                 { NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM, "reschedule_requests_num", DT_U32, 0, NULL },
271                 { NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM, "out_of_window_triggers_num", DT_U32, 0, NULL },
272         }
273 };
274 
275 static const uc_nl_nested_spec_t nl80211_ifcomb_limit_types_nla = {
276         .headsize = 0,
277         .nattrs = 12,
278         .attrs = {
279                 { 1, "ibss", DT_FLAG, 0, NULL },
280                 { 2, "managed", DT_FLAG, 0, NULL },
281                 { 3, "ap", DT_FLAG, 0, NULL },
282                 { 4, "ap_vlan", DT_FLAG, 0, NULL },
283                 { 5, "wds", DT_FLAG, 0, NULL },
284                 { 6, "monitor", DT_FLAG, 0, NULL },
285                 { 7, "mesh_point", DT_FLAG, 0, NULL },
286                 { 8, "p2p_client", DT_FLAG, 0, NULL },
287                 { 9, "p2p_go", DT_FLAG, 0, NULL },
288                 { 10, "p2p_device", DT_FLAG, 0, NULL },
289                 { 11, "outside_bss_context", DT_FLAG, 0, NULL },
290                 { 12, "nan", DT_FLAG, 0, NULL },
291         }
292 };
293 
294 static const uc_nl_nested_spec_t nl80211_ifcomb_limits_nla = {
295         .headsize = 0,
296         .nattrs = 2,
297         .attrs = {
298                 { NL80211_IFACE_LIMIT_TYPES, "types", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla },
299                 { NL80211_IFACE_LIMIT_MAX, "max", DT_U32, 0, NULL },
300         }
301 };
302 
303 static const uc_nl_nested_spec_t nl80211_ifcomb_nla = {
304         .headsize = 0,
305         .nattrs = 5,
306         .attrs = {
307                 { NL80211_IFACE_COMB_LIMITS, "limits", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_ifcomb_limits_nla },
308                 { NL80211_IFACE_COMB_MAXNUM, "maxnum", DT_U32, 0, NULL },
309                 { NL80211_IFACE_COMB_STA_AP_BI_MATCH, "sta_ap_bi_match", DT_FLAG, 0, NULL },
310                 { NL80211_IFACE_COMB_NUM_CHANNELS, "num_channels", DT_U32, 0, NULL },
311                 { NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS, "radar_detect_widths", DT_U32, 0, NULL },
312         }
313 };
314 
315 static const uc_nl_nested_spec_t nl80211_ftm_responder_nla = {
316         .headsize = 0,
317         .nattrs = 3,
318         .attrs = {
319                 { NL80211_FTM_RESP_ATTR_ENABLED, "enabled", DT_FLAG, 0, NULL },
320                 { NL80211_FTM_RESP_ATTR_LCI, "lci", DT_STRING, DF_BINARY, NULL },
321                 { NL80211_FTM_RESP_ATTR_CIVICLOC, "civicloc", DT_STRING, DF_BINARY, NULL },
322         }
323 };
324 
325 static const uc_nl_nested_spec_t nl80211_keys_nla = {
326         .headsize = 0,
327         .nattrs = 4,
328         .attrs = {
329                 { NL80211_KEY_DEFAULT, "default", DT_FLAG, 0, NULL },
330                 { NL80211_KEY_IDX, "idx", DT_U8, 0, NULL },
331                 { NL80211_KEY_CIPHER, "cipher", DT_U32, 0, NULL },
332                 { NL80211_KEY_DATA, "data", DT_STRING, DF_BINARY, NULL },
333         }
334 };
335 
336 static const uc_nl_nested_spec_t nl80211_mesh_params_nla = {
337         .headsize = 0,
338         .nattrs = 29,
339         .attrs = {
340                 { NL80211_MESHCONF_RETRY_TIMEOUT, "retry_timeout", DT_U16, 0, NULL },
341                 { NL80211_MESHCONF_CONFIRM_TIMEOUT, "confirm_timeout", DT_U16, 0, NULL },
342                 { NL80211_MESHCONF_HOLDING_TIMEOUT, "holding_timeout", DT_U16, 0, NULL },
343                 { NL80211_MESHCONF_MAX_PEER_LINKS, "max_peer_links", DT_U16, 0, NULL },
344                 { NL80211_MESHCONF_MAX_RETRIES, "max_retries", DT_U8, 0, NULL },
345                 { NL80211_MESHCONF_TTL, "ttl", DT_U8, 0, NULL },
346                 { NL80211_MESHCONF_ELEMENT_TTL, "element_ttl", DT_U8, 0, NULL },
347                 { NL80211_MESHCONF_AUTO_OPEN_PLINKS, "auto_open_plinks", DT_BOOL, 0, NULL },
348                 { NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, "hwmp_max_preq_retries", DT_U8, 0, NULL },
349                 { NL80211_MESHCONF_PATH_REFRESH_TIME, "path_refresh_time", DT_U32, 0, NULL },
350                 { NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, "min_discovery_timeout", DT_U16, 0, NULL },
351                 { NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, "hwmp_active_path_timeout", DT_U32, 0, NULL },
352                 { NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, "hwmp_preq_min_interval", DT_U16, 0, NULL },
353                 { NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, "hwmp_net_diam_trvs_time", DT_U16, 0, NULL },
354                 { NL80211_MESHCONF_HWMP_ROOTMODE, "hwmp_rootmode", DT_U8, 0, NULL },
355                 { NL80211_MESHCONF_HWMP_RANN_INTERVAL, "hwmp_rann_interval", DT_U16, 0, NULL },
356                 { NL80211_MESHCONF_GATE_ANNOUNCEMENTS, "gate_announcements", DT_U8, 0, NULL },
357                 { NL80211_MESHCONF_FORWARDING, "forwarding", DT_BOOL, 0, NULL },
358                 { NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR, "sync_offset_max_neighbor", DT_U32, 0, NULL },
359                 { NL80211_MESHCONF_RSSI_THRESHOLD, "rssi_threshold", DT_S32, 0, NULL },
360                 { NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT, "hwmp_path_to_root_timeout", DT_U32, 0, NULL },
361                 { NL80211_MESHCONF_HWMP_ROOT_INTERVAL, "hwmp_root_interval", DT_U16, 0, NULL },
362                 { NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL, "hwmp_confirmation_interval", DT_U16, 0, NULL },
363                 { NL80211_MESHCONF_POWER_MODE, "power_mode", DT_U32, 0, NULL },
364                 { NL80211_MESHCONF_AWAKE_WINDOW, "awake_window", DT_U16, 0, NULL },
365                 { NL80211_MESHCONF_PLINK_TIMEOUT, "plink_timeout", DT_U32, 0, NULL },
366                 { NL80211_MESHCONF_CONNECTED_TO_GATE, "connected_to_gate", DT_BOOL, 0, NULL },
367                 { NL80211_MESHCONF_NOLEARN, "nolearn", DT_BOOL, 0, NULL },
368                 { NL80211_MESHCONF_CONNECTED_TO_AS, "connected_to_as", DT_BOOL, 0, NULL },
369         }
370 };
371 
372 static const uc_nl_nested_spec_t nl80211_mesh_setup_nla = {
373         .headsize = 0,
374         .nattrs = 1,
375         .attrs = {
376                 { NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC, "enable_vendor_sync", DT_BOOL, 0, NULL },
377         }
378 };
379 
380 static const uc_nl_nested_spec_t nl80211_mntr_flags_nla = {
381         .headsize = 0,
382         .nattrs = 7,
383         .attrs = {
384                 { NL80211_MNTR_FLAG_FCSFAIL, "fcsfail", DT_FLAG, 0, NULL },
385                 { NL80211_MNTR_FLAG_PLCPFAIL, "plcpfail", DT_FLAG, 0, NULL },
386                 { NL80211_MNTR_FLAG_CONTROL, "control", DT_FLAG, 0, NULL },
387                 { NL80211_MNTR_FLAG_OTHER_BSS, "other_bss", DT_FLAG, 0, NULL },
388                 { NL80211_MNTR_FLAG_COOK_FRAMES, "cook_frames", DT_FLAG, 0, NULL },
389                 { NL80211_MNTR_FLAG_ACTIVE, "active", DT_FLAG, 0, NULL },
390                 { NL80211_MNTR_FLAG_SKIP_TX, "skip_tx", DT_FLAG, 0, NULL },
391         }
392 };
393 
394 static const uc_nl_nested_spec_t nl80211_nan_func_srf_nla = {
395         .headsize = 0,
396         .nattrs = 4,
397         .attrs = {
398                 { NL80211_NAN_SRF_INCLUDE, "include", DT_FLAG, 0, NULL },
399                 { NL80211_NAN_SRF_BF_IDX, "bf_idx", DT_U8, 0, NULL },
400                 { NL80211_NAN_SRF_BF, "bf", DT_STRING, DF_BINARY, NULL },
401                 { NL80211_NAN_SRF_MAC_ADDRS, "mac_addrs", DT_LLADDR, DF_MULTIPLE|DF_AUTOIDX, NULL },
402         }
403 };
404 
405 static const uc_nl_nested_spec_t nl80211_nan_func_nla = {
406         .headsize = 0,
407         .nattrs = 16,
408         .attrs = {
409                 { NL80211_NAN_FUNC_TYPE, "type", DT_U8, 0, NULL },
410                 { NL80211_NAN_FUNC_SERVICE_ID, "service_id", DT_STRING, DF_BINARY, NULL },
411                 { NL80211_NAN_FUNC_PUBLISH_TYPE, "publish_type", DT_U8, 0, NULL },
412                 { NL80211_NAN_FUNC_PUBLISH_BCAST, "publish_bcast", DT_FLAG, 0, NULL },
413                 { NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE, "subscribe_active", DT_FLAG, 0, NULL },
414                 { NL80211_NAN_FUNC_FOLLOW_UP_ID, "follow_up_id", DT_U8, 0, NULL },
415                 { NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID, "follow_up_req_id", DT_U8, 0, NULL },
416                 { NL80211_NAN_FUNC_FOLLOW_UP_DEST, "follow_up_dest", DT_LLADDR, 0, NULL },
417                 { NL80211_NAN_FUNC_CLOSE_RANGE, "close_range", DT_FLAG, 0, NULL },
418                 { NL80211_NAN_FUNC_TTL, "ttl", DT_U32, 0, NULL },
419                 { NL80211_NAN_FUNC_SERVICE_INFO, "service_info", DT_STRING, 0, NULL },
420                 { NL80211_NAN_FUNC_SRF, "srf", DT_NESTED, 0, &nl80211_nan_func_srf_nla },
421                 { NL80211_NAN_FUNC_RX_MATCH_FILTER, "rx_match_filter", DT_STRING, DF_MULTIPLE|DF_AUTOIDX, NULL },
422                 { NL80211_NAN_FUNC_TX_MATCH_FILTER, "tx_match_filter", DT_STRING, DF_MULTIPLE|DF_AUTOIDX, NULL },
423                 { NL80211_NAN_FUNC_INSTANCE_ID, "instance_id", DT_U8, 0, NULL },
424                 { NL80211_NAN_FUNC_TERM_REASON, "term_reason", DT_U8, 0, NULL },
425         }
426 };
427 
428 static const uc_nl_nested_spec_t nl80211_peer_measurements_type_ftm_nla = {
429         .headsize = 0,
430         .nattrs = 13,
431         .attrs = {
432                 { NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP, "num_bursts_exp", DT_U8, 0, NULL },
433                 { NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD, "burst_period", DT_U16, 0, NULL },
434                 { NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES, "num_ftmr_retries", DT_U8, 0, NULL },
435                 { NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION, "burst_duration", DT_U8, 0, NULL },
436                 { NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST, "ftms_per_burst", DT_U8, 0, NULL },
437                 { NL80211_PMSR_FTM_REQ_ATTR_ASAP, "asap", DT_FLAG, 0, NULL },
438                 { NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC, "request_civicloc", DT_FLAG, 0, NULL },
439                 { NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI, "request_lci", DT_FLAG, 0, NULL },
440                 { NL80211_PMSR_FTM_REQ_ATTR_TRIGGER_BASED, "trigger_based", DT_FLAG, 0, NULL },
441                 { NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE, "preamble", DT_U32, 0, NULL },
442                 { NL80211_PMSR_FTM_REQ_ATTR_NON_TRIGGER_BASED, "non_trigger_based", DT_FLAG, 0, NULL },
443                 { NL80211_PMSR_FTM_REQ_ATTR_LMR_FEEDBACK, "lmr_feedback", DT_FLAG, 0, NULL },
444                 { NL80211_PMSR_FTM_REQ_ATTR_BSS_COLOR, "bss_color", DT_U8, 0, NULL },
445         }
446 };
447 
448 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_req_data_nla = {
449         .headsize = 0,
450         .nattrs = 2,
451         .attrs = {
452                 { NL80211_PMSR_TYPE_FTM, "ftm", DT_NESTED, 0, &nl80211_peer_measurements_type_ftm_nla },
453                 { NL80211_PMSR_REQ_ATTR_GET_AP_TSF, "get_ap_tsf", DT_FLAG, 0, NULL },
454         }
455 };
456 
457 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_req_nla = {
458         .headsize = 0,
459         .nattrs = 1,
460         .attrs = {
461                 { NL80211_PMSR_REQ_ATTR_DATA, "data", DT_NESTED, 0, &nl80211_peer_measurements_peers_req_data_nla },
462         }
463 };
464 
465 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_chan_nla = {
466         .headsize = 0,
467         .nattrs = 4,
468         .attrs = {
469                 { NL80211_ATTR_WIPHY_FREQ, "freq", DT_U32, 0, NULL },
470                 { NL80211_ATTR_CENTER_FREQ1, "center_freq1", DT_U32, 0, NULL },
471                 { NL80211_ATTR_CENTER_FREQ2, "center_freq2", DT_U32, 0, NULL },
472                 { NL80211_ATTR_CHANNEL_WIDTH, "channel_width", DT_U32, 0, NULL },
473         }
474 };
475 
476 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_resp_data_nla = {
477         .headsize = 0,
478         .nattrs = 1,
479         .attrs = {
480                 { NL80211_PMSR_TYPE_FTM, "ftm", DT_NESTED, 0, &nl80211_peer_measurements_type_ftm_nla },
481         }
482 };
483 
484 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_resp_nla = {
485         .headsize = 0,
486         .nattrs = 5,
487         .attrs = {
488                 { NL80211_PMSR_RESP_ATTR_STATUS, "status", DT_U32, 0, NULL },
489                 { NL80211_PMSR_RESP_ATTR_HOST_TIME, "host_time", DT_U64, 0, NULL },
490                 { NL80211_PMSR_RESP_ATTR_AP_TSF, "ap_tsf", DT_U64, 0, NULL },
491                 { NL80211_PMSR_RESP_ATTR_FINAL, "final", DT_FLAG, 0, NULL },
492                 { NL80211_PMSR_RESP_ATTR_DATA, "data", DT_NESTED, 0, &nl80211_peer_measurements_peers_resp_data_nla },
493         }
494 };
495 
496 static const uc_nl_nested_spec_t nl80211_peer_measurements_peers_nla = {
497         .headsize = 0,
498         .nattrs = 4,
499         .attrs = {
500                 { NL80211_PMSR_PEER_ATTR_ADDR, "addr", DT_LLADDR, 0, NULL },
501                 { NL80211_PMSR_PEER_ATTR_REQ, "req", DT_NESTED, 0, &nl80211_peer_measurements_peers_req_nla },
502                 { NL80211_PMSR_PEER_ATTR_CHAN, "chan", DT_NESTED, 0, &nl80211_peer_measurements_peers_chan_nla },
503                 { NL80211_PMSR_PEER_ATTR_RESP, "resp", DT_NESTED, 0, &nl80211_peer_measurements_peers_resp_nla }
504         }
505 };
506 
507 static const uc_nl_nested_spec_t nl80211_peer_measurements_nla = {
508         .headsize = 0,
509         .nattrs = 1,
510         .attrs = {
511                 { NL80211_PMSR_ATTR_PEERS, "peers", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_peer_measurements_peers_nla },
512         }
513 };
514 
515 static const uc_nl_nested_spec_t nl80211_reg_rules_nla = {
516         .headsize = 0,
517         .nattrs = 7,
518         .attrs = {
519                 { NL80211_ATTR_REG_RULE_FLAGS, "reg_rule_flags", DT_U32, 0, NULL },
520                 { NL80211_ATTR_FREQ_RANGE_START, "freq_range_start", DT_U32, 0, NULL },
521                 { NL80211_ATTR_FREQ_RANGE_END, "freq_range_end", DT_U32, 0, NULL },
522                 { NL80211_ATTR_FREQ_RANGE_MAX_BW, "freq_range_max_bw", DT_U32, 0, NULL },
523                 { NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, "power_rule_max_ant_gain", DT_U32, 0, NULL },
524                 { NL80211_ATTR_POWER_RULE_MAX_EIRP, "power_rule_max_eirp", DT_U32, 0, NULL },
525                 { NL80211_ATTR_DFS_CAC_TIME, "dfs_cac_time", DT_U32, 0, NULL },
526         }
527 };
528 
529 static const uc_nl_nested_spec_t nl80211_frame_types_nla = {
530         .headsize = 0,
531         .nattrs = 12,
532         .attrs = {
533                 { 1, "ibss", DT_U16, DF_MULTIPLE, NULL },
534                 { 2, "managed", DT_U16, DF_MULTIPLE, NULL },
535                 { 3, "ap", DT_U16, DF_MULTIPLE, NULL },
536                 { 4, "ap_vlan", DT_U16, DF_MULTIPLE, NULL },
537                 { 5, "wds", DT_U16, DF_MULTIPLE, NULL },
538                 { 6, "monitor", DT_U16, DF_MULTIPLE, NULL },
539                 { 7, "mesh_point", DT_U16, DF_MULTIPLE, NULL },
540                 { 8, "p2p_client", DT_U16, DF_MULTIPLE, NULL },
541                 { 9, "p2p_go", DT_U16, DF_MULTIPLE, NULL },
542                 { 10, "p2p_device", DT_U16, DF_MULTIPLE, NULL },
543                 { 11, "outside_bss_context", DT_U16, DF_MULTIPLE, NULL },
544                 { 12, "nan", DT_U16, DF_MULTIPLE, NULL },
545         }
546 };
547 
548 static const uc_nl_nested_spec_t nl80211_sched_scan_match_nla = {
549         .headsize = 0,
550         .nattrs = 1,
551         .attrs = {
552                 { NL80211_SCHED_SCAN_MATCH_ATTR_SSID, "ssid", DT_STRING, DF_BINARY, NULL },
553         }
554 };
555 
556 static const uc_nl_nested_spec_t nl80211_sched_scan_plan_nla = {
557         .headsize = 0,
558         .nattrs = 2,
559         .attrs = {
560                 { NL80211_SCHED_SCAN_PLAN_INTERVAL, "interval", DT_U32, 0, NULL },
561                 { NL80211_SCHED_SCAN_PLAN_ITERATIONS, "iterations", DT_U32, 0, NULL },
562         }
563 };
564 
565 enum {
566         HWSIM_TM_ATTR_CMD = 1,
567         HWSIM_TM_ATTR_PS  = 2,
568 };
569 
570 static const uc_nl_nested_spec_t nl80211_testdata_nla = {
571         .headsize = 0,
572         .nattrs = 2,
573         .attrs = {
574                 { HWSIM_TM_ATTR_CMD, "cmd", DT_U32, 0, NULL },
575                 { HWSIM_TM_ATTR_PS, "ps", DT_U32, 0, NULL },
576         }
577 };
578 
579 static const uc_nl_nested_spec_t nl80211_tid_config_nla = {
580         .headsize = 0,
581         .nattrs = 1,
582         .attrs = {
583                 { NL80211_TID_CONFIG_ATTR_TIDS, "tids", DT_U16, 0, NULL },
584         }
585 };
586 
587 static const uc_nl_nested_spec_t nl80211_wiphy_bands_freqs_wmm_nla = {
588         .headsize = 0,
589         .nattrs = 4,
590         .attrs = {
591                 { NL80211_WMMR_CW_MIN, "cw_min", DT_U16, 0, NULL },
592                 { NL80211_WMMR_CW_MAX, "cw_max", DT_U16, 0, NULL },
593                 { NL80211_WMMR_AIFSN, "aifsn", DT_U8, 0, NULL },
594                 { NL80211_WMMR_TXOP, "txop", DT_U16, 0, NULL },
595         }
596 };
597 
598 static const uc_nl_nested_spec_t nl80211_wiphy_bands_freqs_nla = {
599         .headsize = 0,
600         .nattrs = 28,
601         .attrs = {
602                 { NL80211_FREQUENCY_ATTR_FREQ, "freq", DT_U32, 0, NULL },
603                 { NL80211_FREQUENCY_ATTR_DISABLED, "disabled", DT_FLAG, 0, NULL },
604                 { NL80211_FREQUENCY_ATTR_NO_IR, "no_ir", DT_FLAG, 0, NULL },
605                 { __NL80211_FREQUENCY_ATTR_NO_IBSS, "no_ibss", DT_FLAG, 0, NULL },
606                 { NL80211_FREQUENCY_ATTR_RADAR, "radar", DT_FLAG, 0, NULL },
607                 { NL80211_FREQUENCY_ATTR_MAX_TX_POWER, "max_tx_power", DT_U32, 0, NULL },
608                 { NL80211_FREQUENCY_ATTR_DFS_STATE, "dfs_state", DT_U32, 0, NULL },
609                 { NL80211_FREQUENCY_ATTR_DFS_TIME, "dfs_time", DT_U32, 0, NULL },
610                 { NL80211_FREQUENCY_ATTR_NO_HT40_MINUS, "no_ht40_minus", DT_FLAG, 0, NULL },
611                 { NL80211_FREQUENCY_ATTR_NO_HT40_PLUS, "no_ht40_plus", DT_FLAG, 0, NULL },
612                 { NL80211_FREQUENCY_ATTR_NO_80MHZ, "no_80mhz", DT_FLAG, 0, NULL },
613                 { NL80211_FREQUENCY_ATTR_NO_160MHZ, "no_160mhz", DT_FLAG, 0, NULL },
614                 { NL80211_FREQUENCY_ATTR_DFS_CAC_TIME, "dfs_cac_time", DT_U32, 0, NULL },
615                 { NL80211_FREQUENCY_ATTR_INDOOR_ONLY, "indoor_only", DT_FLAG, 0, NULL },
616                 { NL80211_FREQUENCY_ATTR_IR_CONCURRENT, "ir_concurrent", DT_FLAG, 0, NULL },
617                 { NL80211_FREQUENCY_ATTR_NO_20MHZ, "no_20mhz", DT_FLAG, 0, NULL },
618                 { NL80211_FREQUENCY_ATTR_NO_10MHZ, "no_10mhz", DT_FLAG, 0, NULL },
619                 { NL80211_FREQUENCY_ATTR_WMM, "wmm", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_bands_freqs_wmm_nla },
620                 { NL80211_FREQUENCY_ATTR_NO_HE, "no_he", DT_FLAG, 0, NULL },
621                 { NL80211_FREQUENCY_ATTR_OFFSET, "offset", DT_U32, 0, NULL },
622                 { NL80211_FREQUENCY_ATTR_1MHZ, "1mhz", DT_FLAG, 0, NULL },
623                 { NL80211_FREQUENCY_ATTR_2MHZ, "2mhz", DT_FLAG, 0, NULL },
624                 { NL80211_FREQUENCY_ATTR_4MHZ, "4mhz", DT_FLAG, 0, NULL },
625                 { NL80211_FREQUENCY_ATTR_8MHZ, "8mhz", DT_FLAG, 0, NULL },
626                 { NL80211_FREQUENCY_ATTR_16MHZ, "16mhz", DT_FLAG, 0, NULL },
627                 { NL80211_FREQUENCY_ATTR_NO_320MHZ, "no_320mhz", DT_FLAG, 0, NULL },
628                 { NL80211_FREQUENCY_ATTR_NO_EHT, "no_eht", DT_FLAG, 0, NULL },
629                 { NL80211_FREQUENCY_ATTR_PSD, "psd", DT_S8, 0, NULL },
630         }
631 };
632 
633 static const uc_nl_nested_spec_t nl80211_wiphy_bands_rates_nla = {
634         .headsize = 0,
635         .nattrs = 2,
636         .attrs = {
637                 { NL80211_BITRATE_ATTR_RATE, "rate", DT_U32, 0, NULL },
638                 { NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, "2ghz_shortpreamble", DT_FLAG, 0, NULL },
639         }
640 };
641 
642 static const uc_nl_nested_spec_t nl80211_wiphy_bands_iftype_data_nla = {
643         .headsize = 0,
644         .nattrs = 11,
645         .attrs = {
646                 { NL80211_BAND_IFTYPE_ATTR_IFTYPES, "iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla },
647                 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC, "he_cap_mac", DT_U8, DF_ARRAY, NULL },
648                 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY, "he_cap_phy", DT_U8, DF_ARRAY, NULL },
649                 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_MCS_SET, "he_cap_mcs_set", DT_HE_MCS, DF_RELATED, ATTRID(NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY) },
650                 { NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE, "he_cap_ppe", DT_U8, DF_ARRAY, NULL },
651                 { NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA, "he_6ghz_capa", DT_U16, 0, NULL },
652                 { NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS, "vendor_elems", DT_STRING, DF_BINARY, NULL },
653                 { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC, "eht_cap_mac", DT_U8, DF_ARRAY, NULL },
654                 { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY, "eht_cap_phy", DT_U8, DF_ARRAY, NULL },
655                 { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MCS_SET, "eht_cap_mcs_set", DT_U8, DF_ARRAY, NULL },
656                 { NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PPE, "eht_cap_ppe", DT_U8, DF_ARRAY, NULL },
657         }
658 };
659 
660 static const uc_nl_nested_spec_t nl80211_wiphy_bands_nla = {
661         .headsize = 0,
662         .nattrs = 11,
663         .attrs = {
664                 { NL80211_BAND_ATTR_FREQS, "freqs", DT_NESTED, DF_MULTIPLE|DF_TYPEIDX, &nl80211_wiphy_bands_freqs_nla },
665                 { NL80211_BAND_ATTR_RATES, "rates", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_bands_rates_nla },
666                 { NL80211_BAND_ATTR_HT_MCS_SET, "ht_mcs_set", DT_HT_MCS, 0, NULL },
667                 { NL80211_BAND_ATTR_HT_CAPA, "ht_capa", DT_U16, 0, NULL },
668                 { NL80211_BAND_ATTR_HT_AMPDU_FACTOR, "ht_ampdu_factor", DT_U8, 0, NULL },
669                 { NL80211_BAND_ATTR_HT_AMPDU_DENSITY, "ht_ampdu_density", DT_U8, 0, NULL },
670                 { NL80211_BAND_ATTR_VHT_MCS_SET, "vht_mcs_set", DT_VHT_MCS, 0, NULL },
671                 { NL80211_BAND_ATTR_VHT_CAPA, "vht_capa", DT_U32, 0, NULL },
672                 { NL80211_BAND_ATTR_IFTYPE_DATA, "iftype_data", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_bands_iftype_data_nla },
673                 { NL80211_BAND_ATTR_EDMG_CHANNELS, "edmg_channels", DT_U8, 0, NULL },
674                 { NL80211_BAND_ATTR_EDMG_BW_CONFIG, "edmg_bw_config", DT_U8, 0, NULL },
675         }
676 };
677 
678 static const uc_nl_nested_spec_t nl80211_wowlan_triggers_tcp_nla = {
679         .headsize = 0,
680         .nattrs = 11,
681         .attrs = {
682                 { NL80211_WOWLAN_TCP_SRC_IPV4, "src_ipv4", DT_INADDR, 0, NULL },
683                 { NL80211_WOWLAN_TCP_SRC_PORT, "src_port", DT_U16, 0, NULL },
684                 { NL80211_WOWLAN_TCP_DST_IPV4, "dst_ipv4", DT_INADDR, 0, NULL },
685                 { NL80211_WOWLAN_TCP_DST_PORT, "dst_port", DT_U16, 0, NULL },
686                 { NL80211_WOWLAN_TCP_DST_MAC, "dst_mac", DT_LLADDR, 0, NULL },
687                 { NL80211_WOWLAN_TCP_DATA_PAYLOAD, "data_payload", DT_STRING, DF_BINARY, NULL },
688                 { NL80211_WOWLAN_TCP_DATA_INTERVAL, "data_interval", DT_U32, 0, NULL },
689                 { NL80211_WOWLAN_TCP_WAKE_MASK, "wake_mask", DT_STRING, DF_BINARY, NULL },
690                 { NL80211_WOWLAN_TCP_WAKE_PAYLOAD, "wake_payload", DT_STRING, DF_BINARY, NULL },
691                 { NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ, "data_payload_seq", DT_U32, DF_ARRAY, NULL },
692                 { NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN, "data_payload_token", DT_STRING, DF_BINARY, NULL }, /* XXX: struct nl80211_wowlan_tcp_data_token */
693         }
694 };
695 
696 static const uc_nl_nested_spec_t nl80211_pkt_pattern_nla = {
697         .headsize = 0,
698         .nattrs = 3,
699         .attrs = {
700                 { NL80211_PKTPAT_MASK, "mask", DT_STRING, DF_BINARY, NULL },
701                 { NL80211_PKTPAT_PATTERN, "pattern", DT_STRING, DF_BINARY, NULL },
702                 { NL80211_PKTPAT_OFFSET, "offset", DT_U32, 0, NULL },
703         }
704 };
705 
706 static const uc_nl_nested_spec_t nl80211_wowlan_triggers_nla = {
707         .headsize = 0,
708         .nattrs = 9,
709         .attrs = {
710                 { NL80211_WOWLAN_TRIG_ANY, "any", DT_FLAG, 0, NULL },
711                 { NL80211_WOWLAN_TRIG_DISCONNECT, "disconnect", DT_FLAG, 0, NULL },
712                 { NL80211_WOWLAN_TRIG_MAGIC_PKT, "magic_pkt", DT_FLAG, 0, NULL },
713                 { NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE, "gtk_rekey_failure", DT_FLAG, 0, NULL },
714                 { NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST, "eap_ident_request", DT_FLAG, 0, NULL },
715                 { NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE, "4way_handshake", DT_FLAG, 0, NULL },
716                 { NL80211_WOWLAN_TRIG_RFKILL_RELEASE, "rfkill_release", DT_FLAG, 0, NULL },
717                 { NL80211_WOWLAN_TRIG_TCP_CONNECTION, "tcp_connection", DT_NESTED, 0, &nl80211_wowlan_triggers_tcp_nla },
718                 { NL80211_WOWLAN_TRIG_PKT_PATTERN, "pkt_pattern", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX|DF_OFFSET1, &nl80211_pkt_pattern_nla },
719         }
720 };
721 
722 static const uc_nl_nested_spec_t nl80211_coalesce_rule_nla = {
723         .headsize = 0,
724         .nattrs = 3,
725         .attrs = {
726                 { NL80211_ATTR_COALESCE_RULE_CONDITION, "coalesce_rule_condition", DT_U32, 0, NULL },
727                 { NL80211_ATTR_COALESCE_RULE_DELAY, "coalesce_rule_delay", DT_U32, 0, NULL },
728                 { NL80211_ATTR_COALESCE_RULE_PKT_PATTERN, "coalesce_rule_pkt_pattern", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX|DF_OFFSET1, &nl80211_pkt_pattern_nla },
729         }
730 };
731 
732 static const uc_nl_nested_spec_t nl80211_bss_nla = {
733         .headsize = 0,
734         .nattrs = 13,
735         .attrs = {
736                 { NL80211_BSS_BSSID, "bssid", DT_LLADDR, 0, NULL },
737                 { NL80211_BSS_STATUS, "status", DT_U32, 0, NULL },
738                 { NL80211_BSS_LAST_SEEN_BOOTTIME, "last_seen_boottime", DT_U64, 0, NULL },
739                 { NL80211_BSS_TSF, "tsf", DT_U64, 0, NULL },
740                 { NL80211_BSS_FREQUENCY, "frequency", DT_U32, 0, NULL },
741                 { NL80211_BSS_BEACON_INTERVAL, "beacon_interval", DT_U16, 0, NULL },
742                 { NL80211_BSS_CAPABILITY, "capability", DT_U16, 0, NULL },
743                 { NL80211_BSS_SIGNAL_MBM, "signal_mbm", DT_S32, 0, NULL },
744                 { NL80211_BSS_SIGNAL_UNSPEC, "signal_unspec", DT_U8, 0, NULL },
745                 { NL80211_BSS_SEEN_MS_AGO, "seen_ms_ago", DT_S32, 0, NULL },
746                 { NL80211_BSS_INFORMATION_ELEMENTS, "information_elements", DT_IE, 0, NULL },
747                 { NL80211_BSS_BEACON_IES, "beacon_ies", DT_IE, 0, NULL },
748                 { NL80211_BSS_MLD_ADDR, "mld_addr", DT_LLADDR, 0, NULL },
749         }
750 };
751 
752 static const uc_nl_nested_spec_t nl80211_sta_info_bitrate_nla = {
753         .headsize = 0,
754         .nattrs = 23,
755         .attrs = {
756                 { NL80211_RATE_INFO_BITRATE, "bitrate", DT_U16, 0, NULL },
757                 { NL80211_RATE_INFO_BITRATE32, "bitrate32", DT_U32, 0, NULL },
758                 { NL80211_RATE_INFO_MCS, "mcs", DT_U8, 0, NULL },
759                 { NL80211_RATE_INFO_40_MHZ_WIDTH, "40_mhz_width", DT_FLAG, 0, NULL },
760                 { NL80211_RATE_INFO_SHORT_GI, "short_gi", DT_FLAG, 0, NULL },
761                 { NL80211_RATE_INFO_VHT_MCS, "vht_mcs", DT_U8, 0, NULL },
762                 { NL80211_RATE_INFO_VHT_NSS, "vht_nss", DT_U8, 0, NULL },
763                 { NL80211_RATE_INFO_HE_MCS, "he_mcs", DT_U8, 0, NULL },
764                 { NL80211_RATE_INFO_HE_NSS, "he_nss", DT_U8, 0, NULL },
765                 { NL80211_RATE_INFO_HE_GI, "he_gi", DT_U8, 0, NULL },
766                 { NL80211_RATE_INFO_HE_DCM, "he_dcm", DT_U8, 0, NULL },
767                 { NL80211_RATE_INFO_HE_RU_ALLOC, "he_ru_alloc", DT_U8, 0, NULL },
768                 { NL80211_RATE_INFO_EHT_MCS, "eht_mcs", DT_U8, 0, NULL },
769                 { NL80211_RATE_INFO_EHT_NSS, "eht_nss", DT_U8, 0, NULL },
770                 { NL80211_RATE_INFO_EHT_GI, "eht_gi", DT_U8, 0, NULL },
771                 { NL80211_RATE_INFO_EHT_RU_ALLOC, "eht_ru_alloc", DT_U8, 0, NULL },
772                 { NL80211_RATE_INFO_40_MHZ_WIDTH, "width_40", DT_FLAG, 0, NULL },
773                 { NL80211_RATE_INFO_80_MHZ_WIDTH, "width_80", DT_FLAG, 0, NULL },
774                 { NL80211_RATE_INFO_80P80_MHZ_WIDTH, "width_80p80", DT_FLAG, 0, NULL },
775                 { NL80211_RATE_INFO_160_MHZ_WIDTH, "width_160", DT_FLAG, 0, NULL },
776                 { NL80211_RATE_INFO_320_MHZ_WIDTH, "width_320", DT_FLAG, 0, NULL },
777                 { NL80211_RATE_INFO_10_MHZ_WIDTH, "width_10", DT_FLAG, 0, NULL },
778                 { NL80211_RATE_INFO_5_MHZ_WIDTH, "width_5", DT_FLAG, 0, NULL },
779         }
780 };
781 
782 static const uc_nl_nested_spec_t nl80211_tid_txq_stats_nla = {
783         .headsize = 0,
784         .nattrs = 9,
785         .attrs = {
786                 { NL80211_TXQ_STATS_BACKLOG_BYTES, "backlog_bytes", DT_U32, 0, NULL },
787                 { NL80211_TXQ_STATS_BACKLOG_PACKETS, "backlog_packets", DT_U32, 0, NULL },
788                 { NL80211_TXQ_STATS_FLOWS, "flows", DT_U32, 0, NULL },
789                 { NL80211_TXQ_STATS_DROPS, "drops", DT_U32, 0, NULL },
790                 { NL80211_TXQ_STATS_ECN_MARKS, "ecn_marks", DT_U32, 0, NULL },
791                 { NL80211_TXQ_STATS_OVERLIMIT, "overlimit", DT_U32, 0, NULL },
792                 { NL80211_TXQ_STATS_COLLISIONS, "collisions", DT_U32, 0, NULL },
793                 { NL80211_TXQ_STATS_TX_BYTES, "tx_bytes", DT_U32, 0, NULL },
794                 { NL80211_TXQ_STATS_TX_PACKETS, "tx_packets", DT_U32, 0, NULL },
795         }
796 };
797 
798 static const uc_nl_nested_spec_t nl80211_tid_stats_nla = {
799         .headsize = 0,
800         .nattrs = 5,
801         .attrs = {
802                 { NL80211_TID_STATS_RX_MSDU, "rx_msdu", DT_U64, 0, NULL },
803                 { NL80211_TID_STATS_TX_MSDU, "tx_msdu", DT_U64, 0, NULL },
804                 { NL80211_TID_STATS_TX_MSDU_RETRIES, "tx_msdu_retries", DT_U64, 0, NULL },
805                 { NL80211_TID_STATS_TX_MSDU_FAILED, "tx_msdu_failed", DT_U64, 0, NULL },
806                 { NL80211_TID_STATS_TXQ_STATS, "txq_stats", DT_NESTED, 0, &nl80211_tid_txq_stats_nla },
807         }
808 };
809 
810 static const uc_nl_nested_spec_t nl80211_bss_param_nla = {
811         .headsize = 0,
812         .nattrs = 5,
813         .attrs = {
814                 { NL80211_STA_BSS_PARAM_CTS_PROT, "cts_prot", DT_FLAG, 0, NULL },
815                 { NL80211_STA_BSS_PARAM_SHORT_PREAMBLE, "short_preamble", DT_FLAG, 0, NULL },
816                 { NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME, "short_slot_time", DT_FLAG, 0, NULL },
817                 { NL80211_STA_BSS_PARAM_DTIM_PERIOD, "dtim_period", DT_U8, 0, NULL },
818                 { NL80211_STA_BSS_PARAM_BEACON_INTERVAL, "beacon_interval", DT_U16, 0, NULL },
819         }
820 };
821 
822 static const uc_nl_nested_spec_t nl80211_sta_info_nla = {
823         .headsize = 0,
824         .nattrs = 40,
825         .attrs = {
826                 { NL80211_STA_INFO_INACTIVE_TIME, "inactive_time", DT_U32, 0, NULL },
827                 { NL80211_STA_INFO_RX_BYTES, "rx_bytes", DT_U32, 0, NULL },
828                 { NL80211_STA_INFO_TX_BYTES, "tx_bytes", DT_U32, 0, NULL },
829                 { NL80211_STA_INFO_RX_BYTES64, "rx_bytes64", DT_U64, 0, NULL },
830                 { NL80211_STA_INFO_TX_BYTES64, "tx_bytes64", DT_U64, 0, NULL },
831                 { NL80211_STA_INFO_RX_PACKETS, "rx_packets", DT_U32, 0, NULL },
832                 { NL80211_STA_INFO_TX_PACKETS, "tx_packets", DT_U32, 0, NULL },
833                 { NL80211_STA_INFO_BEACON_RX, "beacon_rx", DT_U64, 0, NULL },
834                 { NL80211_STA_INFO_SIGNAL, "signal", DT_S8, 0, NULL },
835                 { NL80211_STA_INFO_T_OFFSET, "t_offset", DT_U64, 0, NULL },
836                 { NL80211_STA_INFO_TX_BITRATE, "tx_bitrate", DT_NESTED, 0, &nl80211_sta_info_bitrate_nla },
837                 { NL80211_STA_INFO_RX_BITRATE, "rx_bitrate", DT_NESTED, 0, &nl80211_sta_info_bitrate_nla },
838                 { NL80211_STA_INFO_LLID, "llid", DT_U16, 0, NULL },
839                 { NL80211_STA_INFO_PLID, "plid", DT_U16, 0, NULL },
840                 { NL80211_STA_INFO_PLINK_STATE, "plink_state", DT_U8, 0, NULL },
841                 { NL80211_STA_INFO_TX_RETRIES, "tx_retries", DT_U32, 0, NULL },
842                 { NL80211_STA_INFO_TX_FAILED, "tx_failed", DT_U32, 0, NULL },
843                 { NL80211_STA_INFO_BEACON_LOSS, "beacon_loss", DT_U32, 0, NULL },
844                 { NL80211_STA_INFO_RX_DROP_MISC, "rx_drop_misc", DT_U64, 0, NULL },
845                 { NL80211_STA_INFO_STA_FLAGS, "sta_flags", DT_U32, DF_ARRAY, NULL },
846                 { NL80211_STA_INFO_LOCAL_PM, "local_pm", DT_U32, 0, NULL },
847                 { NL80211_STA_INFO_PEER_PM, "peer_pm", DT_U32, 0, NULL },
848                 { NL80211_STA_INFO_NONPEER_PM, "nonpeer_pm", DT_U32, 0, NULL },
849                 { NL80211_STA_INFO_CHAIN_SIGNAL, "chain_signal", DT_S8, DF_MULTIPLE|DF_AUTOIDX, NULL },
850                 { NL80211_STA_INFO_CHAIN_SIGNAL_AVG, "chain_signal_avg", DT_S8, DF_MULTIPLE|DF_AUTOIDX, NULL },
851                 { NL80211_STA_INFO_TID_STATS, "tid_stats", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_tid_stats_nla },
852                 { NL80211_STA_INFO_BSS_PARAM, "bss_param", DT_NESTED, 0, &nl80211_bss_param_nla },
853                 { NL80211_STA_INFO_RX_DURATION, "rx_duration", DT_U64, 0, NULL },
854                 { NL80211_STA_INFO_TX_DURATION, "tx_duration", DT_U64, 0, NULL },
855                 { NL80211_STA_INFO_ACK_SIGNAL, "ack_signal", DT_S8, 0, NULL },
856                 { NL80211_STA_INFO_ACK_SIGNAL_AVG, "ack_signal_avg", DT_S8, 0, NULL },
857                 { NL80211_STA_INFO_AIRTIME_LINK_METRIC, "airtime_link_metric", DT_U32, 0, NULL },
858                 { NL80211_STA_INFO_AIRTIME_WEIGHT, "airtime_weight", DT_U16, 0, NULL },
859                 { NL80211_STA_INFO_CONNECTED_TO_AS, "connected_to_as", DT_BOOL, 0, NULL },
860                 { NL80211_STA_INFO_CONNECTED_TO_GATE, "connected_to_gate", DT_BOOL, 0, NULL },
861                 { NL80211_STA_INFO_CONNECTED_TIME, "connected_time", DT_U32, 0, NULL },
862                 { NL80211_STA_INFO_ASSOC_AT_BOOTTIME, "assoc_at_boottime", DT_U64, 0, NULL },
863                 { NL80211_STA_INFO_BEACON_SIGNAL_AVG, "beacon_signal_avg", DT_S8, 0, NULL },
864                 { NL80211_STA_INFO_EXPECTED_THROUGHPUT, "expected_throughput", DT_U32, 0, NULL },
865                 { NL80211_STA_INFO_SIGNAL_AVG, "signal_avg", DT_S8, 0, NULL },
866         }
867 };
868 
869 static const uc_nl_nested_spec_t nl80211_survey_info_nla = {
870         .headsize = 0,
871         .nattrs = 11,
872         .attrs = {
873                 { NL80211_SURVEY_INFO_FREQUENCY, "frequency", DT_U32, 0, NULL },
874                 { NL80211_SURVEY_INFO_NOISE, "noise", DT_S8, 0, NULL },
875                 { NL80211_SURVEY_INFO_IN_USE, "in_use", DT_FLAG, 0, NULL },
876                 { NL80211_SURVEY_INFO_TIME, "time", DT_U64, 0, NULL },
877                 { NL80211_SURVEY_INFO_TIME_BUSY, "busy", DT_U64, 0, NULL },
878                 { NL80211_SURVEY_INFO_TIME_EXT_BUSY, "ext_busy", DT_U64, 0, NULL },
879                 { NL80211_SURVEY_INFO_TIME_RX, "time_rx", DT_U64, 0, NULL },
880                 { NL80211_SURVEY_INFO_TIME_TX, "time_tx", DT_U64, 0, NULL },
881                 { NL80211_SURVEY_INFO_TIME_SCAN, "scan", DT_U64, 0, NULL },
882                 { NL80211_SURVEY_INFO_TIME_BSS_RX, "time_bss_rx", DT_U64, 0, NULL },
883                 { NL80211_SURVEY_INFO_FREQUENCY_OFFSET, "frequency_offset", DT_U32, 0, NULL },
884         }
885 };
886 
887 static const uc_nl_nested_spec_t nl80211_mpath_info_nla = {
888         .headsize = 0,
889         .nattrs = 8,
890         .attrs = {
891                 { NL80211_MPATH_INFO_SN, "sn", DT_U32, 0, NULL },
892                 { NL80211_MPATH_INFO_METRIC, "metric", DT_U32, 0, NULL },
893                 { NL80211_MPATH_INFO_EXPTIME, "expire", DT_U32, 0, NULL },
894                 { NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, "discovery_timeout", DT_U32, 0, NULL },
895                 { NL80211_MPATH_INFO_DISCOVERY_RETRIES, "discovery_retries", DT_U8, 0, NULL },
896                 { NL80211_MPATH_INFO_FLAGS, "flags", DT_U8, 0, NULL },
897                 { NL80211_MPATH_INFO_HOP_COUNT, "hop_count", DT_U8, 0, NULL },
898                 { NL80211_MPATH_INFO_PATH_CHANGE, "path_change", DT_U32, 0, NULL },
899         }
900 };
901 
902 static const uc_nl_nested_spec_t nl80211_radio_freq_range_nla = {
903         .headsize = 0,
904         .nattrs = 2,
905         .attrs = {
906                 { NL80211_WIPHY_RADIO_FREQ_ATTR_START, "start", DT_U32, 0, NULL },
907                 { NL80211_WIPHY_RADIO_FREQ_ATTR_END, "end", DT_U32, 0, NULL },
908         }
909 };
910 
911 static const uc_nl_nested_spec_t nl80211_wiphy_radio_nla = {
912         .headsize = 0,
913         .nattrs = 4,
914         .attrs = {
915                 { NL80211_WIPHY_RADIO_ATTR_INDEX, "index", DT_U32, 0, NULL },
916                 { NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE, "freq_ranges", DT_NESTED, DF_REPEATED, &nl80211_radio_freq_range_nla },
917                 { NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION, "interface_combinations", DT_NESTED, DF_REPEATED, &nl80211_ifcomb_nla },
918                 { NL80211_WIPHY_RADIO_ATTR_ANTENNA_MASK, "antenna_mask", DT_U32, 0, NULL },
919         }
920 };
921 
922 
923 static const uc_nl_nested_spec_t nl80211_mlo_link_nla = {
924         .headsize = 0,
925         .nattrs = 12,
926         .attrs = {
927                 { NL80211_ATTR_MLO_LINK_ID, "link_id", DT_U8, 0, NULL },
928                 { NL80211_ATTR_MAC, "mac", DT_LLADDR, 0, NULL },
929                 { NL80211_ATTR_WIPHY_FREQ, "wiphy_freq", DT_U32, 0, NULL },
930                 { NL80211_ATTR_CHANNEL_WIDTH, "channel_width", DT_U32, 0, NULL },
931                 { NL80211_ATTR_CENTER_FREQ1, "center_freq1", DT_U32, 0, NULL },
932                 { NL80211_ATTR_CENTER_FREQ2, "center_freq2", DT_U32, 0, NULL },
933                 { NL80211_ATTR_WIPHY_TX_POWER_LEVEL, "wiphy_tx_power_level", DT_U32, 0, NULL },
934                 { NL80211_ATTR_WIPHY_CHANNEL_TYPE, "wiphy_channel_type", DT_U32, 0, NULL },
935                 { NL80211_ATTR_MLO_LINK_DISABLED, "mlo_link_disabled", DT_FLAG, 0, NULL },
936                 { NL80211_ATTR_MLO_TTLM_DLINK, "mlo_ttlm_dlink", DT_STRING, DF_BINARY, NULL },
937                 { NL80211_ATTR_MLO_TTLM_ULINK, "mlo_ttlm_ulink", DT_STRING, DF_BINARY, NULL },
938                 { NL80211_ATTR_PUNCT_BITMAP, "punct_bitmap", DT_U32, 0, NULL },
939         }
940 };
941 
942 static const uc_nl_nested_spec_t nl80211_msg = {
943         .headsize = 0,
944         .nattrs = 137,
945         .attrs = {
946                 { NL80211_ATTR_4ADDR, "4addr", DT_U8, 0, NULL },
947                 { NL80211_ATTR_AIRTIME_WEIGHT, "airtime_weight", DT_U16, 0, NULL },
948                 { NL80211_ATTR_AKM_SUITES, "akm_suites", DT_U32, 0, NULL },
949                 { NL80211_ATTR_AUTH_TYPE, "auth_type", DT_U32, 0, NULL },
950                 { NL80211_ATTR_BANDS, "bands", DT_U32, 0, NULL },
951                 { NL80211_ATTR_BEACON_HEAD, "beacon_head", DT_STRING, DF_BINARY, NULL },
952                 { NL80211_ATTR_BEACON_INTERVAL, "beacon_interval", DT_U32, 0, NULL },
953                 { NL80211_ATTR_BEACON_TAIL, "beacon_tail", DT_STRING, DF_BINARY, NULL },
954                 { NL80211_ATTR_BSS, "bss", DT_NESTED, 0, &nl80211_bss_nla },
955                 { NL80211_ATTR_BSS_BASIC_RATES, "bss_basic_rates", DT_U32, DF_ARRAY, NULL },
956                 { NL80211_ATTR_CENTER_FREQ1, "center_freq1", DT_U32, 0, NULL },
957                 { NL80211_ATTR_CENTER_FREQ2, "center_freq2", DT_U32, 0, NULL },
958                 { NL80211_ATTR_CHANNEL_WIDTH, "channel_width", DT_U32, 0, NULL },
959                 { NL80211_ATTR_CH_SWITCH_BLOCK_TX, "ch_switch_block_tx", DT_FLAG, 0, NULL },
960                 { NL80211_ATTR_CH_SWITCH_COUNT, "ch_switch_count", DT_U32, 0, NULL },
961                 { NL80211_ATTR_CIPHER_SUITES, "cipher_suites", DT_U32, DF_ARRAY, NULL },
962                 { NL80211_ATTR_CIPHER_SUITES_PAIRWISE, "cipher_suites_pairwise", DT_U32, 0, NULL },
963                 { NL80211_ATTR_CIPHER_SUITE_GROUP, "cipher_suite_group", DT_U32, 0, NULL },
964                 { NL80211_ATTR_COALESCE_RULE, "coalesce_rule", DT_NESTED, 0, &nl80211_coalesce_rule_nla },
965                 { NL80211_ATTR_COOKIE, "cookie", DT_U64, 0, NULL },
966                 { NL80211_ATTR_CQM, "cqm", DT_NESTED, 0, &nl80211_cqm_nla },
967                 { NL80211_ATTR_DFS_CAC_TIME, "dfs_cac_time", DT_U32, 0, NULL },
968                 { NL80211_ATTR_DFS_REGION, "dfs_region", DT_U8, 0, NULL },
969                 { NL80211_ATTR_DTIM_PERIOD, "dtim_period", DT_U32, 0, NULL },
970                 { NL80211_ATTR_DURATION, "duration", DT_U32, 0, NULL },
971                 { NL80211_ATTR_EPCS, "epcs", DT_FLAG, 0, NULL },
972                 { NL80211_ATTR_EXT_FEATURES, "extended_features", DT_U8, DF_ARRAY, NULL },
973                 { NL80211_ATTR_FEATURE_FLAGS, "feature_flags", DT_U32, 0, NULL },
974                 { NL80211_ATTR_FRAME, "frame", DT_STRING, DF_BINARY, NULL },
975                 { NL80211_ATTR_FRAME_MATCH, "frame_match", DT_STRING, DF_BINARY, NULL },
976                 { NL80211_ATTR_FRAME_TYPE, "frame_type", DT_U16, 0, NULL },
977                 { NL80211_ATTR_FREQ_FIXED, "freq_fixed", DT_FLAG, 0, NULL },
978                 { NL80211_ATTR_FTM_RESPONDER, "ftm_responder", DT_NESTED, 0, &nl80211_ftm_responder_nla },
979                 { NL80211_ATTR_FTM_RESPONDER_STATS, "ftm_responder_stats", DT_NESTED, 0, &nl80211_ftm_responder_stats_nla },
980                 { NL80211_ATTR_HIDDEN_SSID, "hidden_ssid", DT_U32, 0, NULL },
981                 { NL80211_ATTR_HT_CAPABILITY_MASK, "ht_capability_mask", DT_HT_CAP, 0, NULL },
982                 { NL80211_ATTR_IE, "ie", DT_IE, 0, NULL },
983                 { NL80211_ATTR_IFINDEX, "dev", DT_NETDEV, 0, NULL },
984                 { NL80211_ATTR_IFNAME, "ifname", DT_STRING, 0, NULL },
985                 { NL80211_ATTR_IFTYPE, "iftype", DT_U32, 0, NULL },
986                 { NL80211_ATTR_INACTIVITY_TIMEOUT, "inactivity_timeout", DT_U16, 0, NULL },
987                 { NL80211_ATTR_INTERFACE_COMBINATIONS, "interface_combinations", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_ifcomb_nla },
988                 { NL80211_ATTR_KEYS, "keys", DT_NESTED, DF_AUTOIDX, &nl80211_keys_nla },
989                 { NL80211_ATTR_KEY_SEQ, "key_seq", DT_STRING, DF_BINARY, NULL },
990                 { NL80211_ATTR_KEY_TYPE, "key_type", DT_U32, 0, NULL },
991                 { NL80211_ATTR_LOCAL_MESH_POWER_MODE, "local_mesh_power_mode", DT_U32, 0, NULL },
992                 { NL80211_ATTR_MAC, "mac", DT_LLADDR, 0, NULL },
993                 { NL80211_ATTR_MAC_MASK, "mac_mask", DT_LLADDR, 0, NULL },
994                 { NL80211_ATTR_MCAST_RATE, "mcast_rate", DT_U32, 0, NULL },
995                 { NL80211_ATTR_MEASUREMENT_DURATION, "measurement_duration", DT_U16, 0, NULL },
996                 { NL80211_ATTR_MESH_ID, "mesh_id", DT_STRING, 0, NULL },
997                 { NL80211_ATTR_MESH_PARAMS, "mesh_params", DT_NESTED, 0, &nl80211_mesh_params_nla },
998                 { NL80211_ATTR_MESH_SETUP, "mesh_setup", DT_NESTED, 0, &nl80211_mesh_setup_nla },
999                 { NL80211_ATTR_MGMT_SUBTYPE, "mgmt_subtype", DT_U8, 0, NULL },
1000                 { NL80211_ATTR_MNTR_FLAGS, "mntr_flags", DT_NESTED, 0, &nl80211_mntr_flags_nla },
1001                 { NL80211_ATTR_MPATH_NEXT_HOP, "mpath_next_hop", DT_LLADDR, 0, NULL },
1002                 { NL80211_ATTR_MPATH_INFO, "mpath_info", DT_NESTED, 0, &nl80211_mpath_info_nla },
1003                 { NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR, "mu_mimo_follow_mac_addr", DT_LLADDR, 0, NULL },
1004                 { NL80211_ATTR_NAN_FUNC, "nan_func", DT_NESTED, 0, &nl80211_nan_func_nla },
1005                 { NL80211_ATTR_NAN_MASTER_PREF, "nan_master_pref", DT_U8, 0, NULL },
1006                 { NL80211_ATTR_NETNS_FD, "netns_fd", DT_U32, 0, NULL },
1007                 { NL80211_ATTR_NOACK_MAP, "noack_map", DT_U16, 0, NULL },
1008                 { NL80211_ATTR_NSS, "nss", DT_U8, 0, NULL },
1009                 { NL80211_ATTR_PEER_MEASUREMENTS, "peer_measurements", DT_NESTED, 0, &nl80211_peer_measurements_nla },
1010                 { NL80211_ATTR_PID, "pid", DT_U32, 0, NULL },
1011                 { NL80211_ATTR_PMK, "pmk", DT_STRING, DF_BINARY, NULL },
1012                 { NL80211_ATTR_PRIVACY, "privacy", DT_FLAG, 0, NULL },
1013                 { NL80211_ATTR_PUNCT_BITMAP, "punct_bitmap", DT_U32, 0, NULL },
1014                 { NL80211_ATTR_PROTOCOL_FEATURES, "protocol_features", DT_U32, 0, NULL },
1015                 { NL80211_ATTR_PS_STATE, "ps_state", DT_U32, 0, NULL },
1016                 { NL80211_ATTR_RADAR_EVENT, "radar_event", DT_U32, 0, NULL },
1017                 { NL80211_ATTR_REASON_CODE, "reason_code", DT_U16, 0, NULL },
1018                 { NL80211_ATTR_REG_ALPHA2, "reg_alpha2", DT_STRING, 0, NULL },
1019                 { NL80211_ATTR_REG_INITIATOR, "reg_initiator", DT_U32, 0, NULL },
1020                 { NL80211_ATTR_REG_RULES, "reg_rules", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_reg_rules_nla },
1021                 { NL80211_ATTR_REG_TYPE, "reg_type", DT_U8, 0, NULL },
1022                 { NL80211_ATTR_RX_FRAME_TYPES, "rx_frame_types", DT_NESTED, 0, &nl80211_frame_types_nla },
1023                 { NL80211_ATTR_RX_SIGNAL_DBM, "rx_signal_dbm", DT_U32, 0, NULL },
1024                 { NL80211_ATTR_SCAN_FLAGS, "scan_flags", DT_U32, 0, NULL },
1025                 { NL80211_ATTR_SCAN_FREQUENCIES, "scan_frequencies", DT_U32, DF_MULTIPLE|DF_AUTOIDX, NULL },
1026                 { NL80211_ATTR_SCAN_SSIDS, "scan_ssids", DT_STRING, DF_MULTIPLE|DF_AUTOIDX, NULL },
1027                 { NL80211_ATTR_SCHED_SCAN_DELAY, "sched_scan_delay", DT_U32, 0, NULL },
1028                 { NL80211_ATTR_SCHED_SCAN_INTERVAL, "sched_scan_interval", DT_U32, 0, NULL },
1029                 { NL80211_ATTR_SCHED_SCAN_MATCH, "sched_scan_match", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_sched_scan_match_nla },
1030                 { NL80211_ATTR_SCHED_SCAN_PLANS, "sched_scan_plans", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX|DF_OFFSET1, &nl80211_sched_scan_plan_nla },
1031                 { NL80211_ATTR_SMPS_MODE, "smps_mode", DT_U8, 0, NULL },
1032                 { NL80211_ATTR_SPLIT_WIPHY_DUMP, "split_wiphy_dump", DT_FLAG, 0, NULL },
1033                 { NL80211_ATTR_SSID, "ssid", DT_STRING, DF_BINARY, NULL },
1034                 { NL80211_ATTR_STATUS_CODE, "status_code", DT_U16, 0, NULL },
1035                 { NL80211_ATTR_STA_INFO, "sta_info", DT_NESTED, 0, &nl80211_sta_info_nla },
1036                 { NL80211_ATTR_STA_PLINK_ACTION, "sta_plink_action", DT_U8, 0, NULL },
1037                 { NL80211_ATTR_STA_TX_POWER, "sta_tx_power", DT_U16, 0, NULL },
1038                 { NL80211_ATTR_STA_TX_POWER_SETTING, "sta_tx_power_setting", DT_U8, 0, NULL },
1039                 { NL80211_ATTR_STA_VLAN, "sta_vlan", DT_U32, 0, NULL },
1040                 { NL80211_ATTR_SUPPORTED_COMMANDS, "supported_commands", DT_U32, DF_NO_SET|DF_MULTIPLE|DF_AUTOIDX, NULL },
1041                 { NL80211_ATTR_TESTDATA, "testdata", DT_NESTED, 0, &nl80211_testdata_nla },
1042                 { NL80211_ATTR_TID_CONFIG, "tid_config", DT_NESTED, DF_MULTIPLE, &nl80211_tid_config_nla },
1043                 { NL80211_ATTR_TIMEOUT, "timeout", DT_U32, 0, NULL },
1044                 { NL80211_ATTR_TXQ_LIMIT, "txq_limit", DT_U32, 0, NULL },
1045                 { NL80211_ATTR_TXQ_MEMORY_LIMIT, "txq_memory_limit", DT_U32, 0, NULL },
1046                 { NL80211_ATTR_TXQ_QUANTUM, "txq_quantum", DT_U32, 0, NULL },
1047                 { NL80211_ATTR_TX_FRAME_TYPES, "tx_frame_types", DT_NESTED, 0, &nl80211_frame_types_nla },
1048                 { NL80211_ATTR_USE_MFP, "use_mfp", DT_U32, 0, NULL },
1049                 { NL80211_ATTR_VENDOR_DATA, "vendor_data", DT_STRING, DF_BINARY, NULL },
1050                 { NL80211_ATTR_VENDOR_ID, "vendor_id", DT_U32, 0, NULL },
1051                 { NL80211_ATTR_VENDOR_SUBCMD, "vendor_subcmd", DT_U32, 0, NULL },
1052                 { NL80211_ATTR_WDEV, "wdev", DT_U64, 0, NULL },
1053                 { NL80211_ATTR_WIPHY, "wiphy", DT_U32, 0, NULL },
1054                 { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, "wiphy_antenna_avail_rx", DT_U32, 0, NULL },
1055                 { NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, "wiphy_antenna_avail_tx", DT_U32, 0, NULL },
1056                 { NL80211_ATTR_WIPHY_ANTENNA_RX, "wiphy_antenna_rx", DT_U32, 0, NULL },
1057                 { NL80211_ATTR_WIPHY_ANTENNA_TX, "wiphy_antenna_tx", DT_U32, 0, NULL },
1058                 { NL80211_ATTR_WIPHY_BANDS, "wiphy_bands", DT_NESTED, DF_NO_SET|DF_MULTIPLE|DF_TYPEIDX, &nl80211_wiphy_bands_nla },
1059                 { NL80211_ATTR_WIPHY_CHANNEL_TYPE, "wiphy_channel_type", DT_U32, 0, NULL },
1060                 { NL80211_ATTR_WIPHY_COVERAGE_CLASS, "wiphy_coverage_class", DT_U8, 0, NULL },
1061                 { NL80211_ATTR_WIPHY_DYN_ACK, "wiphy_dyn_ack", DT_FLAG, 0, NULL },
1062                 { NL80211_ATTR_WIPHY_FRAG_THRESHOLD, "wiphy_frag_threshold", DT_S32, 0, NULL },
1063                 { NL80211_ATTR_WIPHY_FREQ, "wiphy_freq", DT_U32, 0, NULL },
1064                 { NL80211_ATTR_WIPHY_NAME, "wiphy_name", DT_STRING, 0, NULL },
1065                 { NL80211_ATTR_WIPHY_RETRY_LONG, "wiphy_retry_long", DT_U8, 0, NULL },
1066                 { NL80211_ATTR_WIPHY_RETRY_SHORT, "wiphy_retry_short", DT_U8, 0, NULL },
1067                 { NL80211_ATTR_WIPHY_RTS_THRESHOLD, "wiphy_rts_threshold", DT_S32, 0, NULL },
1068                 { NL80211_ATTR_WIPHY_TX_POWER_LEVEL, "wiphy_tx_power_level", DT_U32, 0, NULL },
1069                 { NL80211_ATTR_WIPHY_TX_POWER_SETTING, "wiphy_tx_power_setting", DT_U32, 0, NULL },
1070                 { NL80211_ATTR_WOWLAN_TRIGGERS, "wowlan_triggers", DT_NESTED, 0, &nl80211_wowlan_triggers_nla },
1071                 { NL80211_ATTR_WPA_VERSIONS, "wpa_versions", DT_U32, 0, NULL },
1072                 { NL80211_ATTR_SUPPORTED_IFTYPES, "supported_iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla },
1073                 { NL80211_ATTR_SOFTWARE_IFTYPES, "software_iftypes", DT_NESTED, 0, &nl80211_ifcomb_limit_types_nla },
1074                 { NL80211_ATTR_MAX_AP_ASSOC_STA, "max_ap_assoc", DT_U16, 0, NULL },
1075                 { NL80211_ATTR_MLO_LINKS, "mlo_links", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_mlo_link_nla },
1076                 { NL80211_ATTR_MLD_ADDR, "mld_addr", DT_LLADDR, 0, NULL },
1077                 { NL80211_ATTR_EML_CAPABILITY, "eml_capability", DT_U16, 0, NULL },
1078                 { NL80211_ATTR_MLD_CAPA_AND_OPS, "mld_capa_and_ops", DT_U16, 0, NULL },
1079                 { NL80211_ATTR_MLO_LINK_ID, "mlo_link_id", DT_U8, 0, NULL },
1080                 { NL80211_ATTR_SURVEY_INFO, "survey_info", DT_NESTED, 0, &nl80211_survey_info_nla },
1081                 { NL80211_ATTR_WIPHY_RADIOS, "radios", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_wiphy_radio_nla },
1082                 { NL80211_ATTR_VIF_RADIO_MASK, "vif_radio_mask", DT_U32, 0, NULL },
1083         }
1084 };
1085 
1086 static const uc_nl_nested_spec_t hwsim_tx_info_struct = {
1087         .headsize = sizeof(struct hwsim_tx_rate),
1088         .nattrs = 2,
1089         .attrs = {
1090                 { NLA_UNSPEC, "idx", DT_S8, 0, MEMBER(hwsim_tx_rate, idx) },
1091                 { NLA_UNSPEC, "count", DT_U8, 0, MEMBER(hwsim_tx_rate, count) },
1092         }
1093 };
1094 
1095 static const uc_nl_nested_spec_t hwsim_tx_info_flags_struct = {
1096         .headsize = sizeof(struct hwsim_tx_rate_flag),
1097         .nattrs = 2,
1098         .attrs = {
1099                 { NLA_UNSPEC, "idx", DT_S8, 0, MEMBER(hwsim_tx_rate_flag, idx) },
1100                 { NLA_UNSPEC, "flags", DT_U16, 0, MEMBER(hwsim_tx_rate_flag, flags) },
1101         }
1102 };
1103 
1104 static const uc_nl_nested_spec_t hwsim_pmsr_support_nla = {
1105         .headsize = 0,
1106         .nattrs = 5,
1107         .attrs = {
1108                 { NL80211_PMSR_ATTR_MAX_PEERS, "max_peers", DT_U32, 0, NULL },
1109                 { NL80211_PMSR_ATTR_REPORT_AP_TSF, "report_ap_tsf", DT_FLAG, 0, NULL },
1110                 { NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR, "randomize_mac_addr", DT_FLAG, 0, NULL },
1111                 { NL80211_PMSR_ATTR_TYPE_CAPA, "type_capa", DT_U32, 0, NULL },
1112                 { NL80211_PMSR_ATTR_PEERS, "peers", DT_NESTED, DF_MULTIPLE|DF_AUTOIDX, &nl80211_peer_measurements_peers_nla },
1113         }
1114 };
1115 
1116 static const uc_nl_nested_spec_t hwsim_pmsr_request_nla = {
1117         .headsize = 0,
1118         .nattrs = 1,
1119         .attrs = {
1120                 { NL80211_ATTR_PEER_MEASUREMENTS, "peer_measurements", DT_NESTED, 0, &nl80211_peer_measurements_nla },
1121         }
1122 };
1123 
1124 static const uc_nl_nested_spec_t hwsim_msg = {
1125         .headsize = 0,
1126         .nattrs = 27,
1127         .attrs = {
1128                 { HWSIM_ATTR_ADDR_RECEIVER, "addr_receiver", DT_LLADDR, 0, NULL },
1129                 { HWSIM_ATTR_ADDR_TRANSMITTER, "addr_transmitter", DT_LLADDR, 0, NULL },
1130                 { HWSIM_ATTR_FRAME, "frame", DT_STRING, DF_BINARY, NULL },
1131                 { HWSIM_ATTR_FLAGS, "flags", DT_U32, 0, NULL },
1132                 { HWSIM_ATTR_RX_RATE, "rx_rate", DT_U32, 0, NULL },
1133                 { HWSIM_ATTR_SIGNAL, "signal", DT_U32, 0, NULL },
1134                 { HWSIM_ATTR_TX_INFO, "tx_info", DT_NESTED, DF_ARRAY, &hwsim_tx_info_struct },
1135                 { HWSIM_ATTR_COOKIE, "cookie", DT_U64, 0, NULL },
1136                 { HWSIM_ATTR_CHANNELS, "channels", DT_U32, 0, NULL },
1137                 { HWSIM_ATTR_RADIO_ID, "radio_id", DT_U32, 0, NULL },
1138                 { HWSIM_ATTR_REG_HINT_ALPHA2, "reg_hint_alpha2", DT_STRING, DF_BINARY, NULL },
1139                 { HWSIM_ATTR_REG_CUSTOM_REG, "reg_custom_reg", DT_U32, 0, NULL },
1140                 { HWSIM_ATTR_REG_STRICT_REG, "reg_strict_reg", DT_FLAG, 0, NULL },
1141                 { HWSIM_ATTR_SUPPORT_P2P_DEVICE, "support_p2p_device", DT_FLAG, 0, NULL },
1142                 { HWSIM_ATTR_USE_CHANCTX, "use_chanctx", DT_FLAG, 0, NULL },
1143                 { HWSIM_ATTR_DESTROY_RADIO_ON_CLOSE, "destroy_radio_on_close", DT_FLAG, 0, NULL },
1144                 { HWSIM_ATTR_RADIO_NAME, "radio_name", DT_STRING, DF_BINARY, NULL },
1145                 { HWSIM_ATTR_NO_VIF, "no_vif", DT_FLAG, 0, NULL },
1146                 { HWSIM_ATTR_FREQ, "freq", DT_U32, 0, NULL },
1147                 { HWSIM_ATTR_TX_INFO_FLAGS, "tx_info_flags", DT_NESTED, DF_ARRAY, &hwsim_tx_info_flags_struct },
1148                 { HWSIM_ATTR_PERM_ADDR, "perm_addr", DT_LLADDR, 0, NULL },
1149                 { HWSIM_ATTR_IFTYPE_SUPPORT, "iftype_support", DT_U32, 0, NULL },
1150                 { HWSIM_ATTR_CIPHER_SUPPORT, "cipher_support", DT_U32, DF_ARRAY, NULL },
1151                 { HWSIM_ATTR_MLO_SUPPORT, "mlo_support", DT_FLAG, 0, NULL },
1152                 { HWSIM_ATTR_PMSR_SUPPORT, "pmsr_support", DT_NESTED, 0, &hwsim_pmsr_support_nla },
1153                 { HWSIM_ATTR_PMSR_REQUEST, "pmsr_request", DT_NESTED, 0, &hwsim_pmsr_request_nla },
1154                 { HWSIM_ATTR_PMSR_RESULT, "pmsr_result", DT_NESTED, 0, &hwsim_pmsr_support_nla },
1155         }
1156 };
1157 
1158 
1159 static bool
1160 nla_check_len(struct nlattr *nla, size_t sz)
1161 {
1162         return (nla && nla_len(nla) >= (ssize_t)sz);
1163 }
1164 
1165 static bool
1166 nla_parse_error(const uc_nl_attr_spec_t *spec, uc_vm_t *vm, uc_value_t *v, const char *msg)
1167 {
1168         char *s;
1169 
1170         s = ucv_to_string(vm, v);
1171 
1172         set_error(NLE_INVAL, "%s `%s` has invalid value `%s`: %s",
1173                 spec->attr ? "attribute" : "field",
1174                 spec->key,
1175                 s,
1176                 msg);
1177 
1178         free(s);
1179 
1180         return false;
1181 }
1182 
1183 static void
1184 uc_nl_put_struct_member(char *base, const void *offset, size_t datalen, void *data)
1185 {
1186         memcpy(base + (uintptr_t)offset, data, datalen);
1187 }
1188 
1189 static void
1190 uc_nl_put_struct_member_u8(char *base, const void *offset, uint8_t u8)
1191 {
1192         base[(uintptr_t)offset] = u8;
1193 }
1194 
1195 static void
1196 uc_nl_put_struct_member_u32(char *base, const void *offset, uint32_t u32)
1197 {
1198         uc_nl_put_struct_member(base, offset, sizeof(u32), &u32);
1199 }
1200 
1201 static void *
1202 uc_nl_get_struct_member(char *base, const void *offset, size_t datalen, void *data)
1203 {
1204         memcpy(data, base + (uintptr_t)offset, datalen);
1205 
1206         return data;
1207 }
1208 
1209 static uint8_t
1210 uc_nl_get_struct_member_u8(char *base, const void *offset)
1211 {
1212         return (uint8_t)base[(uintptr_t)offset];
1213 }
1214 
1215 static uint32_t
1216 uc_nl_get_struct_member_u32(char *base, const void *offset)
1217 {
1218         uint32_t u32;
1219 
1220         uc_nl_get_struct_member(base, offset, sizeof(u32), &u32);
1221 
1222         return u32;
1223 }
1224 
1225 static bool
1226 uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx);
1227 
1228 static uc_value_t *
1229 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr *attr, struct nlattr *attr2, uc_vm_t *vm);
1230 
1231 static bool
1232 uc_nl_convert_attrs(struct nl_msg *msg, void *buf, size_t buflen, size_t headsize, const uc_nl_attr_spec_t *attrs, size_t nattrs, uc_vm_t *vm, uc_value_t *obj)
1233 {
1234         struct nlattr **tb, *nla, *nla2, *nla_nest;
1235         size_t i, type, maxattr = 0;
1236         uc_value_t *v, *arr;
1237         int rem;
1238 
1239         for (i = 0; i < nattrs; i++)
1240                 if (attrs[i].attr > maxattr)
1241                         maxattr = attrs[i].attr;
1242 
1243         tb = calloc(maxattr + 1, sizeof(struct nlattr *));
1244 
1245         if (!tb)
1246                 return false;
1247 
1248         nla_for_each_attr(nla, buf + headsize, buflen - headsize, rem) {
1249                 type = nla_type(nla);
1250 
1251                 if (type <= maxattr && !tb[type])
1252                         tb[type] = nla;
1253         }
1254 
1255         for (i = 0; i < nattrs; i++) {
1256                 if (attrs[i].attr != 0 && !tb[attrs[i].attr])
1257                         continue;
1258 
1259                 if (attrs[i].flags & DF_REPEATED) {
1260                         arr = ucv_array_new(vm);
1261 
1262                         nla = tb[attrs[i].attr];
1263                         rem = buflen - ((void *)nla - buf);
1264                         for (; nla_ok(nla, rem); nla = nla_next(nla, &rem)) {
1265                                 if (nla_type(nla) != (int)attrs[i].attr)
1266                                         break;
1267                                 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, nla, NULL, vm);
1268                                 if (!v)
1269                                         continue;
1270 
1271                                 ucv_array_push(arr, v);
1272                         }
1273                         if (!ucv_array_length(arr)) {
1274                                 ucv_put(arr);
1275                                 continue;
1276                         }
1277 
1278                         v = arr;
1279                 }
1280                 else if (attrs[i].flags & DF_MULTIPLE) {
1281                         arr = ucv_array_new(vm);
1282                         nla_nest = tb[attrs[i].attr];
1283 
1284                         nla_for_each_attr(nla, nla_data(nla_nest), nla_len(nla_nest), rem) {
1285                                 if (!(attrs[i].flags & (DF_AUTOIDX|DF_TYPEIDX)) &&
1286                                     attrs[i].auxdata && nla_type(nla) != (intptr_t)attrs[i].auxdata)
1287                                         continue;
1288 
1289                                 v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, nla, NULL, vm);
1290 
1291                                 if (!v)
1292                                         continue;
1293 
1294                                 if (attrs[i].flags & DF_TYPEIDX)
1295                                         ucv_array_set(arr, nla_type(nla) - !!(attrs[i].flags & DF_OFFSET1), v);
1296                                 else
1297                                         ucv_array_push(arr, v);
1298                         }
1299 
1300                         if (!ucv_array_length(arr)) {
1301                                 ucv_put(arr);
1302 
1303                                 continue;
1304                         }
1305 
1306                         v = arr;
1307                 }
1308                 else {
1309                         if (attrs[i].flags & DF_RELATED)
1310                                 nla2 = tb[(uintptr_t)attrs[i].auxdata];
1311                         else
1312                                 nla2 = NULL;
1313 
1314                         v = uc_nl_convert_attr(&attrs[i], msg, (char *)buf, tb[attrs[i].attr], nla2, vm);
1315 
1316                         if (!v)
1317                                 continue;
1318                 }
1319 
1320                 ucv_object_add(obj, attrs[i].key, v);
1321         }
1322 
1323         free(tb);
1324 
1325         return true;
1326 }
1327 
1328 static bool
1329 uc_nl_parse_attrs(struct nl_msg *msg, char *base, const uc_nl_attr_spec_t *attrs, size_t nattrs, uc_vm_t *vm, uc_value_t *obj)
1330 {
1331         struct nlattr *nla_nest = NULL;
1332         uc_value_t *v, *item;
1333         size_t i, j, idx;
1334         bool exists;
1335 
1336         for (i = 0; i < nattrs; i++) {
1337                 if (attrs[i].attr == NL80211_ATTR_NOT_IMPLEMENTED)
1338                         continue;
1339 
1340                 v = ucv_object_get(obj, attrs[i].key, &exists);
1341 
1342                 if (!exists)
1343                         continue;
1344 
1345                 if (attrs[i].flags & DF_MULTIPLE) {
1346                         nla_nest = nla_nest_start(msg, attrs[i].attr);
1347 
1348                         if (ucv_type(v) == UC_ARRAY) {
1349                                 for (j = 0; j < ucv_array_length(v); j++) {
1350                                         item = ucv_array_get(v, j);
1351 
1352                                         if (!item && (attrs[i].flags & DF_TYPEIDX))
1353                                                 continue;
1354 
1355                                         if (!attrs[i].auxdata || (attrs[i].flags & (DF_AUTOIDX|DF_TYPEIDX)))
1356                                                 idx = j + !!(attrs[i].flags & DF_OFFSET1);
1357                                         else
1358                                                 idx = (uintptr_t)attrs[i].auxdata;
1359 
1360                                         if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, item, idx))
1361                                                 return false;
1362                                 }
1363                         }
1364                         else {
1365                                 if (!attrs[i].auxdata || (attrs[i].flags & (DF_AUTOIDX|DF_TYPEIDX)))
1366                                         idx = !!(attrs[i].flags & DF_OFFSET1);
1367                                 else
1368                                         idx = (uintptr_t)attrs[i].auxdata;
1369 
1370                                 if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, idx))
1371                                         return false;
1372                         }
1373 
1374                         nla_nest_end(msg, nla_nest);
1375                 }
1376                 else if (!uc_nl_parse_attr(&attrs[i], msg, base, vm, v, 0)) {
1377                         return false;
1378                 }
1379         }
1380 
1381         return true;
1382 }
1383 
1384 static bool
1385 uc_nl_parse_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val)
1386 {
1387         const uc_nl_nested_spec_t *nest = spec->auxdata;
1388         struct nlattr *nested_nla;
1389 
1390         if (!nest)
1391                 return false;
1392 
1393         nested_nla = nla_reserve(msg, spec->attr, nest->headsize);
1394 
1395         if (!uc_nl_parse_attrs(msg, nla_data(nested_nla), nest->attrs, nest->nattrs, vm, val))
1396                 return false;
1397 
1398         nla_nest_end(msg, nested_nla);
1399 
1400         return true;
1401 }
1402 
1403 static uc_value_t *
1404 uc_nl_convert_rta_nested(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm)
1405 {
1406         const uc_nl_nested_spec_t *nest = spec->auxdata;
1407         uc_value_t *nested_obj;
1408         bool rv;
1409 
1410         if (!nest)
1411                 return NULL;
1412 
1413         if (!nla_check_len(attr, nest->headsize))
1414                 return NULL;
1415 
1416         nested_obj = ucv_object_new(vm);
1417 
1418         rv = uc_nl_convert_attrs(msg,
1419                 nla_data(attr), nla_len(attr), nest->headsize,
1420                 nest->attrs, nest->nattrs,
1421                 vm, nested_obj);
1422 
1423         if (!rv) {
1424                 ucv_put(nested_obj);
1425 
1426                 return NULL;
1427         }
1428 
1429         return nested_obj;
1430 }
1431 
1432 static uc_value_t *
1433 uc_nl_convert_rta_ht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm)
1434 {
1435         uc_value_t *mcs_obj, *mcs_idx;
1436         uint16_t max_rate = 0;
1437         uint8_t *mcs;
1438         size_t i;
1439 
1440         if (!nla_check_len(attr, 16))
1441                 return NULL;
1442 
1443         mcs = nla_data(attr);
1444         mcs_obj = ucv_object_new(vm);
1445 
1446         max_rate = (mcs[10] | ((mcs[11] & 0x3) << 8));
1447 
1448         if (max_rate)
1449                 ucv_object_add(mcs_obj, "rx_highest_data_rate", ucv_uint64_new(max_rate));
1450 
1451         mcs_idx = ucv_array_new(vm);
1452 
1453         for (i = 0; i <= 76; i++)
1454                 if (mcs[i / 8] & (1 << (i % 8)))
1455                         ucv_array_push(mcs_idx, ucv_uint64_new(i));
1456 
1457         ucv_object_add(mcs_obj, "rx_mcs_indexes", mcs_idx);
1458 
1459         ucv_object_add(mcs_obj, "tx_mcs_set_defined", ucv_boolean_new(mcs[12] & (1 << 0)));
1460         ucv_object_add(mcs_obj, "tx_rx_mcs_set_equal", ucv_boolean_new(!(mcs[12] & (1 << 1))));
1461         ucv_object_add(mcs_obj, "tx_max_spatial_streams", ucv_uint64_new(((mcs[12] >> 2) & 3) + 1));
1462         ucv_object_add(mcs_obj, "tx_unequal_modulation", ucv_boolean_new(mcs[12] & (1 << 4)));
1463 
1464         return mcs_obj;
1465 }
1466 
1467 static uc_value_t *
1468 uc_nl_convert_rta_ht_cap(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm)
1469 {
1470         uc_value_t *cap_obj, *mcs_obj, *rx_mask;
1471         struct ieee80211_ht_cap *cap;
1472         size_t i;
1473 
1474         if (!nla_check_len(attr, sizeof(*cap)))
1475                 return NULL;
1476 
1477         cap = nla_data(attr);
1478         cap_obj = ucv_object_new(vm);
1479 
1480         ucv_object_add(cap_obj, "cap_info", ucv_uint64_new(le16toh(cap->cap_info)));
1481         ucv_object_add(cap_obj, "ampdu_params_info", ucv_uint64_new(cap->ampdu_params_info));
1482         ucv_object_add(cap_obj, "extended_ht_cap_info", ucv_uint64_new(le16toh(cap->extended_ht_cap_info)));
1483         ucv_object_add(cap_obj, "tx_BF_cap_info", ucv_uint64_new(le32toh(cap->tx_BF_cap_info)));
1484         ucv_object_add(cap_obj, "antenna_selection_info", ucv_uint64_new(cap->antenna_selection_info));
1485 
1486         mcs_obj = ucv_object_new(vm);
1487         rx_mask = ucv_array_new_length(vm, sizeof(cap->mcs.rx_mask));
1488 
1489         for (i = 0; i < sizeof(cap->mcs.rx_mask); i++)
1490                 ucv_array_push(rx_mask, ucv_uint64_new(cap->mcs.rx_mask[i]));
1491 
1492         ucv_object_add(mcs_obj, "rx_mask", rx_mask);
1493         ucv_object_add(mcs_obj, "rx_highest", ucv_uint64_new(le16toh(cap->mcs.rx_highest)));
1494         ucv_object_add(mcs_obj, "tx_params", ucv_uint64_new(cap->mcs.tx_params));
1495 
1496         ucv_object_add(cap_obj, "mcs", mcs_obj);
1497 
1498         return cap_obj;
1499 }
1500 
1501 static uc_value_t *
1502 uc_nl_convert_rta_vht_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm)
1503 {
1504         uc_value_t *mcs_obj, *mcs_set, *mcs_entry, *mcs_idx;
1505         size_t i, j, max_idx;
1506         uint16_t u16;
1507         uint8_t *mcs;
1508 
1509         if (!nla_check_len(attr, 8))
1510                 return NULL;
1511 
1512         mcs = nla_data(attr);
1513         mcs_obj = ucv_object_new(vm);
1514 
1515         u16 = mcs[0] | (mcs[1] << 8);
1516         mcs_set = ucv_array_new(vm);
1517 
1518         for (i = 1; i <= 8; i++) {
1519                 switch ((u16 >> ((i - 1) * 2)) & 3) {
1520                 case 0: max_idx = 7; break;
1521                 case 1: max_idx = 8; break;
1522                 case 2: max_idx = 9; break;
1523                 default: continue;
1524                 }
1525 
1526                 mcs_idx = ucv_array_new_length(vm, max_idx + 1);
1527 
1528                 for (j = 0; j <= max_idx; j++)
1529                         ucv_array_push(mcs_idx, ucv_uint64_new(j));
1530 
1531                 mcs_entry = ucv_object_new(vm);
1532 
1533                 ucv_object_add(mcs_entry, "streams", ucv_uint64_new(i));
1534                 ucv_object_add(mcs_entry, "mcs_indexes", mcs_idx);
1535 
1536                 ucv_array_push(mcs_set, mcs_entry);
1537         }
1538 
1539         ucv_object_add(mcs_obj, "rx_mcs_set", mcs_set);
1540         ucv_object_add(mcs_obj, "rx_highest_data_rate", ucv_uint64_new((mcs[2] | (mcs[3] << 8)) & 0x1fff));
1541 
1542         u16 = mcs[4] | (mcs[5] << 8);
1543         mcs_set = ucv_array_new(vm);
1544 
1545         for (i = 1; i <= 8; i++) {
1546                 switch ((u16 >> ((i - 1) * 2)) & 3) {
1547                 case 0: max_idx = 7; break;
1548                 case 1: max_idx = 8; break;
1549                 case 2: max_idx = 9; break;
1550                 default: continue;
1551                 }
1552 
1553                 mcs_idx = ucv_array_new_length(vm, max_idx + 1);
1554 
1555                 for (j = 0; j <= max_idx; j++)
1556                         ucv_array_push(mcs_idx, ucv_uint64_new(j));
1557 
1558                 mcs_entry = ucv_object_new(vm);
1559 
1560                 ucv_object_add(mcs_entry, "streams", ucv_uint64_new(i));
1561                 ucv_object_add(mcs_entry, "mcs_indexes", mcs_idx);
1562 
1563                 ucv_array_push(mcs_set, mcs_entry);
1564         }
1565 
1566         ucv_object_add(mcs_obj, "tx_mcs_set", mcs_set);
1567         ucv_object_add(mcs_obj, "tx_highest_data_rate", ucv_uint64_new((mcs[6] | (mcs[7] << 8)) & 0x1fff));
1568 
1569         return mcs_obj;
1570 }
1571 
1572 static uc_value_t *
1573 uc_nl_convert_rta_he_mcs(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, struct nlattr *phy_attr, uc_vm_t *vm)
1574 {
1575         uint8_t bw_support_mask[] = { (1 << 1) | (1 << 2), (1 << 3), (1 << 4) };
1576         uc_value_t *mcs_set, *mcs_bw, *mcs_dir, *mcs_entry, *mcs_idx;
1577         uint16_t bw[] = { 80, 160, 8080 }, mcs[6];
1578         uint8_t phy_cap_0 = 0;
1579         uint16_t u16;
1580         size_t i, j, k, l, max_idx;
1581 
1582         if (!nla_check_len(attr, sizeof(mcs)))
1583                 return NULL;
1584 
1585         if (nla_check_len(phy_attr, sizeof(phy_cap_0)))
1586                 phy_cap_0 = *(uint8_t *)nla_data(phy_attr);
1587 
1588         memcpy(mcs, nla_data(attr), sizeof(mcs));
1589 
1590         mcs_set = ucv_array_new_length(vm, 3);
1591 
1592         for (i = 0; i < ARRAY_SIZE(bw); i++) {
1593                 if (!(phy_cap_0 & bw_support_mask[i]))
1594                         continue;
1595 
1596                 mcs_bw = ucv_object_new(vm);
1597 
1598                 for (j = 0; j < 2; j++) {
1599                         mcs_dir = ucv_array_new_length(vm, 8);
1600 
1601                         for (k = 0; k < 8; k++) {
1602                                 u16 = mcs[(i * 2) + j];
1603                                 u16 >>= k * 2;
1604                                 u16 &= 0x3;
1605 
1606                                 switch (u16) {
1607                                 case 0: max_idx = 7; break;
1608                                 case 1: max_idx = 9; break;
1609                                 case 2: max_idx = 11; break;
1610                                 case 3: continue;
1611                                 }
1612 
1613                                 mcs_idx = ucv_array_new_length(vm, max_idx + 1);
1614 
1615                                 for (l = 0; l <= max_idx; l++)
1616                                         ucv_array_push(mcs_idx, ucv_uint64_new(l));
1617 
1618                                 mcs_entry = ucv_object_new(vm);
1619 
1620                                 ucv_object_add(mcs_entry, "streams", ucv_uint64_new(k + 1));
1621                                 ucv_object_add(mcs_entry, "mcs_indexes", mcs_idx);
1622 
1623                                 ucv_array_push(mcs_dir, mcs_entry);
1624                         }
1625 
1626                         if (ucv_array_length(mcs_dir))
1627                                 ucv_object_add(mcs_bw, j ? "tx_mcs_set" : "rx_mcs_set", mcs_dir);
1628                         else
1629                                 ucv_put(mcs_dir);
1630                 }
1631 
1632                 if (ucv_object_length(mcs_bw)) {
1633                         ucv_object_add(mcs_bw, "bandwidth", ucv_uint64_new(bw[i]));
1634                         ucv_array_push(mcs_set, mcs_bw);
1635                 }
1636                 else {
1637                         ucv_put(mcs_bw);
1638                 }
1639         }
1640 
1641         return mcs_set;
1642 }
1643 
1644 static uc_value_t *
1645 uc_nl_convert_rta_ie(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, struct nlattr *attr, uc_vm_t *vm)
1646 {
1647         uc_value_t *ie_arr, *ie_obj;
1648         uint8_t *ie;
1649         size_t len;
1650 
1651         len = nla_len(attr);
1652         ie = nla_data(attr);
1653 
1654         if (len < 2)
1655                 return NULL;
1656 
1657         ie_arr = ucv_array_new(vm);
1658 
1659         while (len >= 2 && len - 2 >= ie[1]) {
1660                 ie_obj = ucv_object_new(vm);
1661 
1662                 ucv_object_add(ie_obj, "type", ucv_uint64_new(ie[0]));
1663                 ucv_object_add(ie_obj, "data", ucv_string_new_length((char *)&ie[2], ie[1]));
1664 
1665                 ucv_array_push(ie_arr, ie_obj);
1666 
1667                 len -= ie[1] + 2;
1668                 ie += ie[1] + 2;
1669         }
1670 
1671         return ie_arr;
1672 }
1673 
1674 
1675 static bool
1676 uc_nl_parse_numval(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, void *dst)
1677 {
1678         uint64_t u64;
1679         uint32_t u32;
1680         uint16_t u16;
1681         uint8_t u8;
1682 
1683         switch (spec->type) {
1684         case DT_U8:
1685                 if (!uc_nl_parse_u32(val, &u32) || u32 > 255)
1686                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-255");
1687 
1688                 u8 = (uint8_t)u32;
1689 
1690                 memcpy(dst, &u8, sizeof(u8));
1691                 break;
1692 
1693         case DT_U16:
1694                 if (!uc_nl_parse_u32(val, &u32) || u32 > 65535)
1695                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-65535");
1696 
1697                 u16 = (uint16_t)u32;
1698 
1699                 memcpy(dst, &u16, sizeof(u16));
1700                 break;
1701 
1702         case DT_S32:
1703         case DT_U32:
1704                 if (spec->type == DT_S32 && !uc_nl_parse_s32(val, &u32))
1705                         return nla_parse_error(spec, vm, val, "not an integer or out of range -2147483648-2147483647");
1706                 else if (spec->type == DT_U32 && !uc_nl_parse_u32(val, &u32))
1707                         return nla_parse_error(spec, vm, val, "not an integer or out of range 0-4294967295");
1708 
1709                 memcpy(dst, &u32, sizeof(u32));
1710                 break;
1711 
1712         case DT_U64:
1713                 if (!uc_nl_parse_u64(val, &u64))
1714                         return nla_parse_error(spec, vm, val, "not an integer or negative");
1715 
1716                 memcpy(dst, &u64, sizeof(u64));
1717                 break;
1718 
1719         default:
1720                 return false;
1721         }
1722 
1723         return true;
1724 }
1725 
1726 static const uint8_t dt_sizes[] = {
1727         [DT_U8] = sizeof(uint8_t),
1728         [DT_S8] = sizeof(int8_t),
1729         [DT_U16] = sizeof(uint16_t),
1730         [DT_U32] = sizeof(uint32_t),
1731         [DT_S32] = sizeof(int32_t),
1732         [DT_U64] = sizeof(uint64_t),
1733 };
1734 
1735 static bool
1736 uc_nl_parse_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, uc_vm_t *vm, uc_value_t *val, size_t idx)
1737 {
1738         char buf[sizeof(uint64_t)];
1739         struct in_addr in = { 0 };
1740         struct ether_addr *ea;
1741         struct nlattr *nla;
1742         uc_value_t *item;
1743         size_t attr, i;
1744         uint32_t u32;
1745         char *s;
1746 
1747         if (spec->flags & DF_MULTIPLE)
1748                 attr = idx;
1749         else
1750                 attr = spec->attr;
1751 
1752         switch (spec->type) {
1753         case DT_U8:
1754         case DT_U16:
1755         case DT_U32:
1756         case DT_S32:
1757         case DT_U64:
1758                 if (spec->flags & DF_ARRAY) {
1759                         assert(spec->attr != 0);
1760 
1761                         if (ucv_type(val) != UC_ARRAY)
1762                                 return nla_parse_error(spec, vm, val, "not an array");
1763 
1764                         nla = nla_reserve(msg, spec->attr, ucv_array_length(val) * dt_sizes[spec->type]);
1765                         s = nla_data(nla);
1766 
1767                         for (i = 0; i < ucv_array_length(val); i++) {
1768                                 item = ucv_array_get(val, i);
1769 
1770                                 if (!uc_nl_parse_numval(spec, msg, base, vm, item, buf))
1771                                         return false;
1772 
1773                                 memcpy(s, buf, dt_sizes[spec->type]);
1774 
1775                                 s += dt_sizes[spec->type];
1776                         }
1777                 }
1778                 else {
1779                         if (!uc_nl_parse_numval(spec, msg, base, vm, val, buf))
1780                                 return false;
1781 
1782                         if (spec->attr == 0)
1783                                 uc_nl_put_struct_member(base, spec->auxdata, dt_sizes[spec->type], buf);
1784                         else
1785                                 nla_put(msg, attr, dt_sizes[spec->type], buf);
1786                 }
1787 
1788                 break;
1789 
1790         case DT_BOOL:
1791                 u32 = (uint32_t)ucv_is_truish(val);
1792 
1793                 if (spec->attr == 0)
1794                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
1795                 else
1796                         nla_put_u8(msg, attr, u32);
1797 
1798                 break;
1799 
1800         case DT_FLAG:
1801                 u32 = (uint32_t)ucv_is_truish(val);
1802 
1803                 if (spec->attr == 0)
1804                         uc_nl_put_struct_member_u8(base, spec->auxdata, u32);
1805                 else if (u32 == 1)
1806                         nla_put_flag(msg, attr);
1807 
1808                 break;
1809 
1810         case DT_STRING:
1811                 assert(spec->attr != 0);
1812 
1813                 if (ucv_type(val) == UC_STRING) {
1814                         nla_put(msg, attr,
1815                                 ucv_string_length(val) + !(spec->flags & DF_BINARY),
1816                                         ucv_string_get(val));
1817                 }
1818                 else {
1819                         s = ucv_to_string(vm, val);
1820 
1821                         if (!s)
1822                                 return nla_parse_error(spec, vm, val, "out of memory");
1823 
1824                         nla_put(msg, attr, strlen(s) + !(spec->flags & DF_BINARY), s);
1825                         free(s);
1826                 }
1827 
1828                 break;
1829 
1830         case DT_NETDEV:
1831                 if (ucv_type(val) == UC_INTEGER) {
1832                         if (ucv_int64_get(val) < 0 ||
1833                             ucv_int64_get(val) > UINT32_MAX)
1834                                 return nla_parse_error(spec, vm, val, "interface index out of range 0-4294967295");
1835 
1836                         u32 = (uint32_t)ucv_int64_get(val);
1837                 }
1838                 else {
1839                         s = ucv_to_string(vm, val);
1840 
1841                         if (!s)
1842                                 return nla_parse_error(spec, vm, val, "out of memory");
1843 
1844                         u32 = if_nametoindex(s);
1845 
1846                         free(s);
1847                 }
1848 
1849                 if (spec->attr == 0)
1850                         uc_nl_put_struct_member_u32(base, spec->auxdata, u32);
1851                 else
1852                         nla_put_u32(msg, attr, u32);
1853 
1854                 break;
1855 
1856         case DT_LLADDR:
1857                 assert(spec->attr != 0);
1858 
1859                 s = ucv_to_string(vm, val);
1860 
1861                 if (!s)
1862                         return nla_parse_error(spec, vm, val, "out of memory");
1863 
1864                 ea = ether_aton(s);
1865 
1866                 free(s);
1867 
1868                 if (!ea)
1869                         return nla_parse_error(spec, vm, val, "invalid MAC address");
1870 
1871                 nla_put(msg, attr, sizeof(*ea), ea);
1872 
1873                 break;
1874 
1875         case DT_INADDR:
1876                 assert(spec->attr != 0);
1877 
1878                 if (!uc_nl_parse_ipaddr(vm, val, &in))
1879                         return nla_parse_error(spec, vm, val, "invalid IP address");
1880 
1881                 nla_put(msg, attr, sizeof(in), &in);
1882 
1883                 break;
1884 
1885         case DT_NESTED:
1886                 if (spec->flags & DF_ARRAY) {
1887                         const uc_nl_nested_spec_t *nested = spec->auxdata;
1888 
1889                         assert(nested != NULL);
1890                         assert(nested->headsize > 0);
1891 
1892                         if (ucv_type(val) != UC_ARRAY)
1893                                 return nla_parse_error(spec, vm, val, "not an array");
1894 
1895                         nla = nla_reserve(msg, spec->attr, ucv_array_length(val) * nested->headsize);
1896                         s = nla_data(nla);
1897 
1898                         for (i = 0; i < ucv_array_length(val); i++) {
1899                                 item = ucv_array_get(val, i);
1900 
1901                                 if (!uc_nl_parse_attrs(msg, s, nested->attrs, nested->nattrs, vm, item))
1902                                         return false;
1903 
1904                                 s += nested->headsize;
1905                         }
1906 
1907                         return true;
1908                 }
1909 
1910                 if (!uc_nl_parse_rta_nested(spec, msg, base, vm, val))
1911                         return false;
1912 
1913                 break;
1914 
1915         default:
1916                 assert(0);
1917         }
1918 
1919         return true;
1920 }
1921 
1922 static uc_value_t *
1923 uc_nl_convert_numval(const uc_nl_attr_spec_t *spec, char *base)
1924 {
1925         union { uint8_t *u8; uint16_t *u16; uint32_t *u32; uint64_t *u64; char *base; } t = { .base = base };
1926 
1927         switch (spec->type) {
1928         case DT_U8:
1929                 return ucv_uint64_new(t.u8[0]);
1930 
1931         case DT_S8:
1932                 return ucv_int64_new((int8_t)t.u8[0]);
1933 
1934         case DT_U16:
1935                 return ucv_uint64_new(t.u16[0]);
1936 
1937         case DT_U32:
1938                 return ucv_uint64_new(t.u32[0]);
1939 
1940         case DT_S32:
1941                 return ucv_int64_new((int32_t)t.u32[0]);
1942 
1943         case DT_U64:
1944                 return ucv_uint64_new(t.u64[0]);
1945 
1946         default:
1947                 return NULL;
1948         }
1949 }
1950 
1951 static uc_value_t *
1952 uc_nl_convert_attr(const uc_nl_attr_spec_t *spec, struct nl_msg *msg, char *base, struct nlattr *attr, struct nlattr *attr2, uc_vm_t *vm)
1953 {
1954         union { uint8_t u8; uint16_t u16; uint32_t u32; uint64_t u64; size_t sz; } t = { 0 };
1955         char buf[sizeof("FF:FF:FF:FF:FF:FF")];
1956         struct ether_addr *ea;
1957         uc_value_t *v;
1958         int i;
1959 
1960         switch (spec->type) {
1961         case DT_U8:
1962         case DT_S8:
1963         case DT_U16:
1964         case DT_U32:
1965         case DT_S32:
1966         case DT_U64:
1967                 if (spec->flags & DF_ARRAY) {
1968                         assert(spec->attr != 0);
1969                         assert((nla_len(attr) % dt_sizes[spec->type]) == 0);
1970 
1971                         v = ucv_array_new_length(vm, nla_len(attr) / dt_sizes[spec->type]);
1972 
1973                         for (i = 0; i < nla_len(attr); i += dt_sizes[spec->type])
1974                                 ucv_array_push(v, uc_nl_convert_numval(spec, nla_data(attr) + i));
1975 
1976                         return v;
1977                 }
1978                 else if (spec->attr == 0) {
1979                         return uc_nl_convert_numval(spec, base + (uintptr_t)spec->auxdata);
1980                 }
1981                 else if (nla_check_len(attr, dt_sizes[spec->type])) {
1982                         return uc_nl_convert_numval(spec, nla_data(attr));
1983                 }
1984 
1985                 return NULL;
1986 
1987         case DT_BOOL:
1988                 if (spec->attr == 0)
1989                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
1990                 else if (nla_check_len(attr, sizeof(t.u8)))
1991                         t.u8 = nla_get_u8(attr);
1992 
1993                 return ucv_boolean_new(t.u8 != 0);
1994 
1995         case DT_FLAG:
1996                 if (spec->attr == 0)
1997                         t.u8 = uc_nl_get_struct_member_u8(base, spec->auxdata);
1998                 else if (attr != NULL)
1999                         t.u8 = 1;
2000 
2001                 return ucv_boolean_new(t.u8 != 0);
2002 
2003         case DT_STRING:
2004                 assert(spec->attr != 0);
2005 
2006                 if (!nla_check_len(attr, 1))
2007                         return NULL;
2008 
2009                 t.sz = nla_len(attr);
2010 
2011                 if (!(spec->flags & DF_BINARY))
2012                         t.sz -= 1;
2013 
2014                 return ucv_string_new_length(nla_data(attr), t.sz);
2015 
2016         case DT_NETDEV:
2017                 if (spec->attr == 0)
2018                         t.u32 = uc_nl_get_struct_member_u32(base, spec->auxdata);
2019                 else if (nla_check_len(attr, sizeof(t.u32)))
2020                         t.u32 = nla_get_u32(attr);
2021 
2022                 if (if_indextoname(t.u32, buf))
2023                         return ucv_string_new(buf);
2024 
2025                 return NULL;
2026 
2027         case DT_LLADDR:
2028                 assert(spec->attr != 0);
2029 
2030                 if (!nla_check_len(attr, sizeof(*ea)))
2031                         return NULL;
2032 
2033                 ea = nla_data(attr);
2034 
2035                 snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
2036                         ea->ether_addr_octet[0], ea->ether_addr_octet[1],
2037                         ea->ether_addr_octet[2], ea->ether_addr_octet[3],
2038                         ea->ether_addr_octet[4], ea->ether_addr_octet[5]);
2039 
2040                 return ucv_string_new(buf);
2041 
2042         case DT_INADDR:
2043                 assert(spec->attr != 0);
2044 
2045                 if (!nla_check_len(attr, sizeof(struct in_addr)) ||
2046                     !inet_ntop(AF_INET, nla_data(attr), buf, sizeof(buf)))
2047                         return NULL;
2048 
2049                 return ucv_string_new(buf);
2050 
2051         case DT_NESTED:
2052                 if (spec->flags & DF_ARRAY) {
2053                         const uc_nl_nested_spec_t *nested = spec->auxdata;
2054 
2055                         assert(nested != NULL);
2056                         assert(nested->headsize > 0);
2057                         assert((nla_len(attr) % nested->headsize) == 0);
2058 
2059                         v = ucv_array_new_length(vm, nla_len(attr) / nested->headsize);
2060 
2061                         for (i = 0; i < nla_len(attr); i += nested->headsize) {
2062                                 uc_value_t *item = ucv_object_new(vm);
2063 
2064                                 ucv_array_push(v, item);
2065 
2066                                 bool rv = uc_nl_convert_attrs(msg,
2067                                         nla_data(attr) + i, nla_len(attr) - i, nested->headsize,
2068                                         nested->attrs, nested->nattrs, vm, item);
2069 
2070                                 if (!rv) {
2071                                         ucv_put(v);
2072 
2073                                         return NULL;
2074                                 }
2075                         }
2076 
2077                         return v;
2078                 }
2079 
2080                 return uc_nl_convert_rta_nested(spec, msg, attr, vm);
2081 
2082         case DT_HT_MCS:
2083                 return uc_nl_convert_rta_ht_mcs(spec, msg, attr, vm);
2084 
2085         case DT_HT_CAP:
2086                 return uc_nl_convert_rta_ht_cap(spec, msg, attr, vm);
2087 
2088         case DT_VHT_MCS:
2089                 return uc_nl_convert_rta_vht_mcs(spec, msg, attr, vm);
2090 
2091         case DT_HE_MCS:
2092                 return uc_nl_convert_rta_he_mcs(spec, msg, attr, attr2, vm);
2093 
2094         case DT_IE:
2095                 return uc_nl_convert_rta_ie(spec, msg, attr, vm);
2096 
2097         default:
2098                 assert(0);
2099         }
2100 
2101         return NULL;
2102 }
2103 
2104 
2105 static struct {
2106         struct nl_sock *sock;
2107         struct nl_sock *evsock;
2108         struct nl_cache *cache;
2109         struct uloop_fd evsock_fd;
2110         struct nl_cb *evsock_cb;
2111 } nl80211_conn;
2112 
2113 typedef enum {
2114         STATE_UNREPLIED,
2115         STATE_CONTINUE,
2116         STATE_REPLIED,
2117         STATE_ERROR
2118 } reply_state_t;
2119 
2120 typedef struct {
2121         reply_state_t state;
2122         uc_vm_t *vm;
2123         uc_value_t *res;
2124         bool merge_phy_info;
2125         bool single_phy_info;
2126         const uc_nl_nested_spec_t *spec;
2127 } request_state_t;
2128 
2129 
2130 /**
2131  * Get the last error information
2132  *
2133  * This function returns information about the last error that occurred
2134  * in nl80211 operations. It provides both error code and error message.
2135  *
2136  * @returns {Object|null} Object with 'code' and 'msg' properties containing
2137  *                       the error information, or null if no error occurred
2138  * @example
2139  * // Send a request that might fail
2140  * let result = request(const.SOME_COMMAND, 0, invalid_params);
2141  * if (!result) {
2142  *     let error = error();
2143  *     print('Error occurred:', error.code, error.msg);
2144  * }
2145  */
2146 static uc_value_t *
2147 uc_nl_error(uc_vm_t *vm, size_t nargs)
2148 {
2149         uc_stringbuf_t *buf;
2150         const char *s;
2151 
2152         if (last_error.code == 0)
2153                 return NULL;
2154 
2155         buf = ucv_stringbuf_new();
2156 
2157         if (last_error.code == NLE_FAILURE && last_error.msg) {
2158                 ucv_stringbuf_addstr(buf, last_error.msg, strlen(last_error.msg));
2159         }
2160         else {
2161                 s = nl_geterror(last_error.code);
2162 
2163                 ucv_stringbuf_addstr(buf, s, strlen(s));
2164 
2165                 if (last_error.msg)
2166                         ucv_stringbuf_printf(buf, ": %s", last_error.msg);
2167         }
2168 
2169         set_error(0, NULL);
2170 
2171         return ucv_stringbuf_finish(buf);
2172 }
2173 
2174 static int
2175 cb_done(struct nl_msg *msg, void *arg)
2176 {
2177         request_state_t *s = arg;
2178 
2179         s->state = STATE_REPLIED;
2180 
2181         return NL_STOP;
2182 }
2183 
2184 static void
2185 deep_merge_array(uc_value_t *dest, uc_value_t *src);
2186 
2187 static void
2188 deep_merge_object(uc_value_t *dest, uc_value_t *src);
2189 
2190 static void
2191 deep_merge_array(uc_value_t *dest, uc_value_t *src)
2192 {
2193         uc_value_t *e, *v;
2194         size_t i;
2195 
2196         if (ucv_type(dest) == UC_ARRAY && ucv_type(src) == UC_ARRAY) {
2197                 for (i = 0; i < ucv_array_length(src); i++) {
2198                         e = ucv_array_get(dest, i);
2199                         v = ucv_array_get(src, i);
2200 
2201                         if (!e)
2202                                 ucv_array_set(dest, i, ucv_get(v));
2203                         else if (ucv_type(v) == UC_ARRAY)
2204                                 deep_merge_array(e, v);
2205                         else if (ucv_type(v) == UC_OBJECT)
2206                                 deep_merge_object(e, v);
2207                 }
2208         }
2209 }
2210 
2211 static void
2212 deep_merge_object(uc_value_t *dest, uc_value_t *src)
2213 {
2214         uc_value_t *e;
2215         bool exists;
2216 
2217         if (ucv_type(dest) == UC_OBJECT && ucv_type(src) == UC_OBJECT) {
2218                 ucv_object_foreach(src, k, v) {
2219                         e = ucv_object_get(dest, k, &exists);
2220 
2221                         if (!exists)
2222                                 ucv_object_add(dest, k, ucv_get(v));
2223                         else if (ucv_type(v) == UC_ARRAY)
2224                                 deep_merge_array(e, v);
2225                         else if (ucv_type(v) == UC_OBJECT)
2226                                 deep_merge_object(e, v);
2227                 }
2228         }
2229 }
2230 
2231 static int
2232 cb_reply(struct nl_msg *msg, void *arg)
2233 {
2234         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2235         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2236         request_state_t *s = arg;
2237         uc_value_t *o, *idx;
2238         int64_t i;
2239         bool rv;
2240 
2241         o = ucv_object_new(s->vm);
2242 
2243         rv = uc_nl_convert_attrs(msg,
2244                 genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0),
2245                 0, s->spec->attrs, s->spec->nattrs, s->vm, o);
2246 
2247         if (rv) {
2248                 if (hdr->nlmsg_flags & NLM_F_MULTI) {
2249                         if (s->merge_phy_info && s->single_phy_info) {
2250                                 if (!s->res) {
2251                                         s->res = o;
2252                                 }
2253                                 else {
2254                                         deep_merge_object(s->res, o);
2255                                         ucv_put(o);
2256                                 }
2257                         }
2258                         else if (s->merge_phy_info) {
2259                                 idx = ucv_object_get(o, "wiphy", NULL);
2260                                 i = idx ? ucv_int64_get(idx) : -1;
2261 
2262                                 if (i >= 0) {
2263                                         if (!s->res)
2264                                                 s->res = ucv_array_new(s->vm);
2265 
2266                                         idx = ucv_array_get(s->res, i);
2267 
2268                                         if (idx) {
2269                                                 deep_merge_object(idx, o);
2270                                                 ucv_put(o);
2271                                         }
2272                                         else {
2273                                                 ucv_array_set(s->res, i, o);
2274                                         }
2275                                 }
2276                         }
2277                         else {
2278                                 if (!s->res)
2279                                         s->res = ucv_array_new(s->vm);
2280 
2281                                 ucv_array_push(s->res, o);
2282                         }
2283                 }
2284                 else {
2285                         s->res = o;
2286                 }
2287         }
2288         else {
2289                 ucv_put(o);
2290         }
2291 
2292         s->state = STATE_CONTINUE;
2293 
2294         return NL_SKIP;
2295 }
2296 
2297 static bool
2298 uc_nl_connect_sock(struct nl_sock **sk, bool nonblocking)
2299 {
2300         int err, fd;
2301 
2302         if (*sk)
2303                 return true;
2304 
2305         *sk = nl_socket_alloc();
2306 
2307         if (!*sk) {
2308                 set_error(NLE_NOMEM, NULL);
2309                 goto err;
2310         }
2311 
2312         err = genl_connect(*sk);
2313 
2314         if (err != 0) {
2315                 set_error(err, NULL);
2316                 goto err;
2317         }
2318 
2319         fd = nl_socket_get_fd(*sk);
2320 
2321         if (fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC) < 0) {
2322                 set_error(NLE_FAILURE, "unable to set FD_CLOEXEC flag on socket: %s", strerror(errno));
2323                 goto err;
2324         }
2325 
2326         if (nonblocking) {
2327                 err = nl_socket_set_nonblocking(*sk);
2328 
2329                 if (err != 0) {
2330                         set_error(err, NULL);
2331                         goto err;
2332                 }
2333         }
2334 
2335         return true;
2336 
2337 err:
2338         if (*sk) {
2339                 nl_socket_free(*sk);
2340                 *sk = NULL;
2341         }
2342 
2343         return false;
2344 }
2345 
2346 static int
2347 uc_nl_find_family_id(const char *name)
2348 {
2349         struct genl_family *fam;
2350 
2351         if (!nl80211_conn.cache && genl_ctrl_alloc_cache(nl80211_conn.sock, &nl80211_conn.cache))
2352                 return -NLE_NOMEM;
2353 
2354         fam = genl_ctrl_search_by_name(nl80211_conn.cache, name);
2355 
2356         if (!fam)
2357                 return -NLE_OBJ_NOTFOUND;
2358 
2359         return genl_family_get_id(fam);
2360 }
2361 
2362 static int
2363 cb_errno(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
2364 {
2365         int *ret = arg;
2366 
2367         if (err->error > 0) {
2368                 set_error(NLE_RANGE,
2369                         "Illegal error code %d in netlink reply", err->error);
2370 
2371                 *ret = -(NLE_MAX + 1);
2372         }
2373         else {
2374                 *ret = -nl_syserr2nlerr(err->error);
2375         }
2376 
2377         return NL_STOP;
2378 }
2379 
2380 static int
2381 cb_ack(struct nl_msg *msg, void *arg)
2382 {
2383         int *ret = arg;
2384 
2385         *ret = 0;
2386 
2387         return NL_STOP;
2388 }
2389 
2390 static int
2391 cb_subscribe(struct nl_msg *msg, void *arg)
2392 {
2393         struct nlattr *nla, *tb[CTRL_ATTR_MAX + 1], *grp[CTRL_ATTR_MCAST_GRP_MAX + 1];
2394         struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
2395         struct { int id; const char *group; } *ret = arg;
2396         int rem;
2397 
2398         nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), genlmsg_attrlen(gnlh, 0), NULL);
2399 
2400         if (!tb[CTRL_ATTR_MCAST_GROUPS])
2401                 return NL_SKIP;
2402 
2403         nla_for_each_nested(nla, tb[CTRL_ATTR_MCAST_GROUPS], rem) {
2404                 nla_parse(grp, CTRL_ATTR_MCAST_GRP_MAX, nla_data(nla), nla_len(nla), NULL);
2405 
2406                 if (!grp[CTRL_ATTR_MCAST_GRP_NAME] || !grp[CTRL_ATTR_MCAST_GRP_ID])
2407                         continue;
2408 
2409                 if (strncmp(nla_data(grp[CTRL_ATTR_MCAST_GRP_NAME]),
2410                             ret->group, nla_len(grp[CTRL_ATTR_MCAST_GRP_NAME])))
2411                         continue;
2412 
2413                 ret->id = nla_get_u32(grp[CTRL_ATTR_MCAST_GRP_ID]);
2414 
2415                 break;
2416         }
2417 
2418         return NL_SKIP;
2419 }
2420 
2421 static bool
2422 uc_nl_subscribe(struct nl_sock *sk, const char *family, const char *group)
2423 {
2424         struct { int id; const char *group; } grp = { -NLE_OBJ_NOTFOUND, group };
2425         struct nl_msg *msg;
2426         struct nl_cb *cb;
2427         int id, ret;
2428 
2429         if (!uc_nl_connect_sock(&nl80211_conn.sock, false))
2430                 return NULL;
2431 
2432         msg = nlmsg_alloc();
2433 
2434         if (!msg)
2435                 err_return(NLE_NOMEM, NULL);
2436 
2437         id = uc_nl_find_family_id("nlctrl");
2438 
2439         if (id < 0)
2440                 err_return(-id, NULL);
2441 
2442         genlmsg_put(msg, 0, 0, id, 0, 0, CTRL_CMD_GETFAMILY, 0);
2443         nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family);
2444 
2445         cb = nl_cb_alloc(NL_CB_DEFAULT);
2446 
2447         if (!cb) {
2448                 nlmsg_free(msg);
2449                 err_return(NLE_NOMEM, NULL);
2450         }
2451 
2452         nl_send_auto_complete(nl80211_conn.sock, msg);
2453 
2454         ret = 1;
2455 
2456         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_ack, &ret);
2457         nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &ret);
2458         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_subscribe, &grp);
2459 
2460         while (ret > 0)
2461                 nl_recvmsgs(nl80211_conn.sock, cb);
2462 
2463         nlmsg_free(msg);
2464         nl_cb_put(cb);
2465 
2466         if (ret < 0)
2467                 err_return(ret, NULL);
2468 
2469         if (grp.id < 0)
2470                 err_return(grp.id, NULL);
2471 
2472         ret = nl_socket_add_membership(sk, grp.id);
2473 
2474         if (ret != 0)
2475                 err_return(ret, NULL);
2476 
2477         return true;
2478 }
2479 
2480 
2481 struct waitfor_ctx {
2482         uint8_t cmd;
2483         uc_vm_t *vm;
2484         uc_value_t *res;
2485         uint32_t cmds[NL80211_CMDS_BITMAP_SIZE];
2486 };
2487 
2488 static uc_value_t *
2489 uc_nl_prepare_event(uc_vm_t *vm, struct nl_msg *msg)
2490 {
2491         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2492         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2493         uc_value_t *o = ucv_object_new(vm);
2494         const uc_nl_attr_spec_t *attrs;
2495         size_t nattrs;
2496 
2497         if (hdr->nlmsg_type == uc_nl_find_family_id("MAC80211_HWSIM")) {
2498                 attrs = hwsim_msg.attrs;
2499                 nattrs = hwsim_msg.nattrs;
2500         }
2501         else {
2502                 attrs = nl80211_msg.attrs;
2503                 nattrs = nl80211_msg.nattrs;
2504         }
2505 
2506         if (!uc_nl_convert_attrs(msg, genlmsg_attrdata(gnlh, 0),
2507                 genlmsg_attrlen(gnlh, 0), 0, attrs, nattrs, vm, o)) {
2508                 ucv_put(o);
2509                 return NULL;
2510         }
2511 
2512         return o;
2513 }
2514 
2515 static int
2516 cb_listener_event(struct nl_msg *msg, void *arg)
2517 {
2518         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2519         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2520         uc_vm_t *vm = listener_vm;
2521 
2522         if (!nl80211_conn.evsock_fd.registered || !vm)
2523                 return NL_SKIP;
2524 
2525         for (size_t i = 0; i < ucv_array_length(listener_registry); i += 2) {
2526                 uc_value_t *this = ucv_array_get(listener_registry, i);
2527                 uc_value_t *func = ucv_array_get(listener_registry, i + 1);
2528                 uc_nl_listener_t *l;
2529                 uc_value_t *o, *data;
2530 
2531                 l = ucv_resource_data(this, "nl80211.listener");
2532                 if (!l)
2533                         continue;
2534 
2535                 if (gnlh->cmd > NL80211_CMD_MAX ||
2536                         !(l->cmds[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32))))
2537                         continue;
2538 
2539                 if (!ucv_is_callable(func))
2540                         continue;
2541 
2542                 data = uc_nl_prepare_event(vm, msg);
2543                 if (!data)
2544                         return NL_SKIP;
2545 
2546                 o = ucv_object_new(vm);
2547                 ucv_object_add(o, "cmd", ucv_int64_new(gnlh->cmd));
2548                 ucv_object_add(o, "msg", data);
2549 
2550                 uc_vm_stack_push(vm, ucv_get(this));
2551                 uc_vm_stack_push(vm, ucv_get(func));
2552                 uc_vm_stack_push(vm, o);
2553 
2554                 if (uc_vm_call(vm, true, 1) != EXCEPTION_NONE) {
2555                         uloop_end();
2556                         return NL_STOP;
2557                 }
2558 
2559                 ucv_put(uc_vm_stack_pop(vm));
2560         }
2561 
2562         return NL_SKIP;
2563 }
2564 
2565 static int
2566 cb_evsock_msg(struct nl_msg *msg, void *arg)
2567 {
2568         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2569 
2570         if (hdr->nlmsg_seq == 0) {
2571                 cb_listener_event(msg, NULL);
2572                 return NL_SKIP;
2573         }
2574 
2575         return NL_OK;
2576 }
2577 
2578 static int
2579 cb_event(struct nl_msg *msg, void *arg)
2580 {
2581         struct nlmsghdr *hdr = nlmsg_hdr(msg);
2582         struct genlmsghdr *gnlh = nlmsg_data(hdr);
2583         struct waitfor_ctx *s = arg;
2584         uc_value_t *o;
2585 
2586         cb_listener_event(msg, arg);
2587 
2588         if (gnlh->cmd > NL80211_CMD_MAX ||
2589             !(s->cmds[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32))))
2590                 return NL_SKIP;
2591 
2592         o = uc_nl_prepare_event(s->vm, msg);
2593         if (o) {
2594                 ucv_put(s->res);
2595                 s->res = o;
2596                 s->cmd = gnlh->cmd;
2597         }
2598 
2599         return NL_SKIP;
2600 }
2601 
2602 static int
2603 cb_seq(struct nl_msg *msg, void *arg)
2604 {
2605         return NL_OK;
2606 }
2607 
2608 static bool
2609 uc_nl_fill_cmds(uint32_t *cmd_bits, uc_value_t *cmds)
2610 {
2611         if (ucv_type(cmds) == UC_ARRAY) {
2612                 for (size_t i = 0; i < ucv_array_length(cmds); i++) {
2613                         int64_t n = ucv_int64_get(ucv_array_get(cmds, i));
2614 
2615                         if (n >= HWSIM_CMD_OFFSET)
2616                                 n -= HWSIM_CMD_OFFSET;
2617 
2618                         if (errno || n < 0 || n > NL80211_CMD_MAX)
2619                                 return false;
2620 
2621                         cmd_bits[n / 32] |= (1 << (n % 32));
2622                 }
2623         }
2624         else if (ucv_type(cmds) == UC_INTEGER) {
2625                 int64_t n = ucv_int64_get(cmds);
2626 
2627                 if (n >= HWSIM_CMD_OFFSET)
2628                         n -= HWSIM_CMD_OFFSET;
2629 
2630                 if (errno || n < 0 || n > NL80211_CMD_MAX)
2631                         return false;
2632 
2633                 cmd_bits[n / 32] |= (1 << (n % 32));
2634         }
2635         else if (!cmds)
2636                 memset(cmd_bits, 0xff, NL80211_CMDS_BITMAP_SIZE * sizeof(*cmd_bits));
2637         else
2638                 return false;
2639 
2640         return true;
2641 }
2642 
2643 static bool
2644 uc_nl_evsock_init(void)
2645 {
2646         if (nl80211_conn.evsock)
2647                 return true;
2648 
2649         if (!uc_nl_connect_sock(&nl80211_conn.evsock, true))
2650                 return false;
2651 
2652         if (!uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "config") ||
2653             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "scan") ||
2654             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "regulatory") ||
2655             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "mlme") ||
2656             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "vendor") ||
2657             !uc_nl_subscribe(nl80211_conn.evsock, "nl80211", "nan")) {
2658                 nl_socket_free(nl80211_conn.evsock);
2659                 nl80211_conn.evsock = NULL;
2660                 return false;
2661         }
2662 
2663         return true;
2664 }
2665 
2666 /**
2667  * Wait for a specific nl80211 event
2668  *
2669  * This function waits for a specified nl80211 command to be received within
2670  * a given timeout period. It's useful for asynchronous event handling.
2671  *
2672  * @param {Array|number} cmds - Array of command IDs or single command ID to wait for
2673  * @param {number} timeout - Timeout in milliseconds (optional, default: -1 for infinite)
2674  * @returns {Object|null} Object with 'cmd' and 'msg' properties if event received,
2675  *                       null if timeout or error occurs
2676  * @example
2677  * // Wait for scan results with 5 second timeout
2678  * let event = waitfor([const.NL80211_CMD_NEW_SCAN_RESULTS], 5000);
2679  * if (event)
2680  *     print('Received scan results:', event.msg);
2681  */
2682 static uc_value_t *
2683 uc_nl_waitfor(uc_vm_t *vm, size_t nargs)
2684 {
2685         struct pollfd pfd = { .events = POLLIN };
2686         uc_value_t *cmds = uc_fn_arg(0);
2687         uc_value_t *timeout = uc_fn_arg(1);
2688         uc_value_t *rv = NULL;
2689         struct waitfor_ctx ctx = { .vm = vm };
2690         struct nl_cb *cb;
2691         int ms = -1, err;
2692 
2693         if (timeout) {
2694                 int64_t n = ucv_int64_get(timeout);
2695 
2696                 if (ucv_type(timeout) != UC_INTEGER || n < INT32_MIN || n > INT32_MAX)
2697                         err_return(NLE_INVAL, "Invalid timeout specified");
2698 
2699                 ms = (int)n;
2700         }
2701 
2702         if (!uc_nl_fill_cmds(ctx.cmds, cmds))
2703                 err_return(NLE_INVAL, "Invalid command ID specified");
2704 
2705         if (!uc_nl_evsock_init())
2706                 return NULL;
2707 
2708         cb = nl_cb_alloc(NL_CB_DEFAULT);
2709 
2710         if (!cb)
2711                 err_return(NLE_NOMEM, NULL);
2712 
2713         err = 0;
2714 
2715         nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, cb_seq, NULL);
2716         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_event, &ctx);
2717         nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &err);
2718 
2719         pfd.fd = nl_socket_get_fd(nl80211_conn.evsock);
2720 
2721         while (err == 0 && ctx.cmd == 0) {
2722                 struct timespec start, end;
2723 
2724                 if (ms > 0)
2725                         clock_gettime(CLOCK_MONOTONIC, &start);
2726 
2727                 if (poll(&pfd, 1, ms) != 1)
2728                         break;
2729 
2730                 nl_recvmsgs(nl80211_conn.evsock, cb);
2731 
2732                 if (ms > 0) {
2733                         clock_gettime(CLOCK_MONOTONIC, &end);
2734                         ms -= (end.tv_sec - start.tv_sec) * 1000 +
2735                               (end.tv_nsec - start.tv_nsec) / 1000000;
2736                         if (ms <= 0)
2737                                 break;
2738                 }
2739         }
2740 
2741         nl_cb_put(cb);
2742 
2743         if (ctx.cmd) {
2744                 rv = ucv_object_new(vm);
2745 
2746                 ucv_object_add(rv, "cmd", ucv_int64_new(ctx.cmd));
2747                 ucv_object_add(rv, "msg", ctx.res);
2748 
2749                 return rv;
2750         }
2751         else if (err) {
2752                 err_return(err, NULL);
2753         }
2754         else {
2755                 err_return(NLE_FAILURE, "No event received");
2756         }
2757 }
2758 
2759 static uc_value_t *
2760 uc_nl_request_common(struct nl_sock *sock, uc_vm_t *vm, size_t nargs)
2761 {
2762         request_state_t st = { .vm = vm };
2763         uc_value_t *cmd = uc_fn_arg(0);
2764         uc_value_t *flags = uc_fn_arg(1);
2765         uc_value_t *payload = uc_fn_arg(2);
2766         uint16_t flagval = 0;
2767         struct nl_msg *msg;
2768         struct nl_cb *cb;
2769         int ret, id, cid;
2770 
2771         if (ucv_type(cmd) != UC_INTEGER || ucv_int64_get(cmd) < 0 ||
2772             (flags != NULL && ucv_type(flags) != UC_INTEGER) ||
2773             (payload != NULL && ucv_type(payload) != UC_OBJECT))
2774                 err_return(NLE_INVAL, NULL);
2775 
2776         if (flags) {
2777                 if (ucv_int64_get(flags) < 0 || ucv_int64_get(flags) > 0xffff)
2778                         err_return(NLE_INVAL, NULL);
2779                 else
2780                         flagval = (uint16_t)ucv_int64_get(flags);
2781         }
2782 
2783         msg = nlmsg_alloc();
2784 
2785         if (!msg)
2786                 err_return(NLE_NOMEM, NULL);
2787 
2788         cid = ucv_int64_get(cmd);
2789 
2790         if (cid >= HWSIM_CMD_OFFSET) {
2791                 id = uc_nl_find_family_id("MAC80211_HWSIM");
2792                 cid -= HWSIM_CMD_OFFSET;
2793                 st.spec = &hwsim_msg;
2794         }
2795         else if (cid == NL80211_CMD_GET_WIPHY) {
2796                 id = uc_nl_find_family_id("nl80211");
2797                 st.spec = &nl80211_msg;
2798                 st.merge_phy_info = true;
2799 
2800                 if (ucv_object_get(payload, "wiphy", NULL) != NULL)
2801                         st.single_phy_info = true;
2802 
2803                 if (ucv_is_truish(ucv_object_get(payload, "split_wiphy_dump", NULL)))
2804                         flagval |= NLM_F_DUMP;
2805         }
2806         else {
2807                 id = uc_nl_find_family_id("nl80211");
2808                 st.spec = &nl80211_msg;
2809         }
2810 
2811         if (id < 0)
2812                 err_return(-id, NULL);
2813 
2814         genlmsg_put(msg, 0, 0, id, 0, flagval, cid, 0);
2815 
2816         if (!uc_nl_parse_attrs(msg, nlmsg_data(nlmsg_hdr(msg)), st.spec->attrs, st.spec->nattrs, vm, payload)) {
2817                 nlmsg_free(msg);
2818 
2819                 return NULL;
2820         }
2821 
2822         cb = nl_cb_alloc(NL_CB_DEFAULT);
2823 
2824         if (!cb) {
2825                 nlmsg_free(msg);
2826                 err_return(NLE_NOMEM, NULL);
2827         }
2828 
2829         ret = 1;
2830 
2831         nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_reply, &st);
2832         nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &st);
2833         nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, cb_done, &st);
2834         nl_cb_err(cb, NL_CB_CUSTOM, cb_errno, &ret);
2835 
2836         if (sock == nl80211_conn.evsock) {
2837                 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, cb_seq, NULL);
2838                 nl_cb_set(cb, NL_CB_MSG_IN, NL_CB_CUSTOM, cb_evsock_msg, NULL);
2839         }
2840 
2841         nl_send_auto_complete(sock, msg);
2842 
2843         while (ret > 0 && st.state < STATE_REPLIED)
2844                 nl_recvmsgs(sock, cb);
2845 
2846         nlmsg_free(msg);
2847         nl_cb_put(cb);
2848 
2849         if (ret < 0)
2850                 err_return(ret, NULL);
2851 
2852         switch (st.state) {
2853         case STATE_REPLIED:
2854                 return st.res;
2855 
2856         case STATE_UNREPLIED:
2857                 return ucv_boolean_new(true);
2858 
2859         default:
2860                 set_error(NLE_FAILURE, "Interrupted reply");
2861 
2862                 return ucv_boolean_new(false);
2863         }
2864 }
2865 
2866 /**
2867  * Send a nl80211 request
2868  *
2869  * This function sends a nl80211 netlink request to the kernel and processes
2870  * the response. It's the main interface for interacting with wireless devices.
2871  *
2872  * @param {number} cmd - The nl80211 command ID to execute
2873  * @param {number} flags - Netlink flags (optional, default: 0)
2874  * @param {Object} payload - Request payload object with attributes (optional)
2875  * @returns {Object|boolean} Response object from the kernel, or true for
2876  *                           successful acknowledgment without data
2877  * @example
2878  * // Get wireless device information
2879  * let response = request(const.NL80211_CMD_GET_WIPHY, 0, { wiphy: 0 });
2880  * print('Wireless device info:', response);
2881  *
2882  * // Set wireless interface mode
2883  * let result = request(const.NL80211_CMD_SET_INTERFACE, 0, {
2884  *     ifindex: 1,
2885  *     iftype: const.NL80211_IFTYPE_AP
2886  * });
2887  */
2888 static uc_value_t *
2889 uc_nl_request(uc_vm_t *vm, size_t nargs)
2890 {
2891         if (!uc_nl_connect_sock(&nl80211_conn.sock, false))
2892                 return NULL;
2893 
2894         return uc_nl_request_common(nl80211_conn.sock, vm, nargs);
2895 }
2896 
2897 
2898 /**
2899  * Represents a netlink listener resource.
2900  *
2901  * @class module:nl80211.listener
2902  * @hideconstructor
2903  *
2904  * @see {@link module:nl80211#listener|listener()}
2905  *
2906  * @example
2907  * const nlListener = listener((msg) => {
2908  *     print('Received netlink message:', msg, '\n');
2909  * }, [const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_DEL_INTERFACE]);
2910  *
2911  * nlListener.set_commands([const.NL80211_CMD_GET_WIPHY, const.NL80211_CMD_SET_INTERFACE]);
2912  *
2913  * nlListener.close();
2914  */
2915 
2916 static void
2917 uc_nl_listener_cb(struct uloop_fd *fd, unsigned int events)
2918 {
2919         nl_recvmsgs(nl80211_conn.evsock, nl80211_conn.evsock_cb);
2920 }
2921 
2922 /**
2923  * Create a listener for nl80211 events
2924  *
2925  * This function creates a listener that will receive nl80211 events matching
2926  * the specified command IDs. The listener runs asynchronously and calls the
2927  * provided callback function when events are received.
2928  *
2929  * @param {Function} callback - Function to call when an event is received
2930  * @param {Array|number} cmds - Array of command IDs or single command ID to listen for
2931  * @returns {Object} Listener resource object that can be used to manage the listener
2932  * @example
2933  * // Listen for interface changes
2934  * let listener = listener((msg) => {
2935  *     print('Interface event:', msg.cmd, msg.msg);
2936  * }, [const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_DEL_INTERFACE]);
2937  *
2938  * // Listen for scan results
2939  * let scanListener = listener((msg) => {
2940  *     print('Scan completed:', msg.msg);
2941  * }, const.NL80211_CMD_NEW_SCAN_RESULTS);
2942  */
2943 static uc_value_t *
2944 uc_nl_listener(uc_vm_t *vm, size_t nargs)
2945 {
2946         struct uloop_fd *fd = &nl80211_conn.evsock_fd;
2947         uc_nl_listener_t *l;
2948         uc_value_t *cb_func = uc_fn_arg(0);
2949         uc_value_t *cmds = uc_fn_arg(1);
2950         uc_value_t *rv;
2951         size_t i;
2952 
2953         if (!ucv_is_callable(cb_func)) {
2954                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid callback");
2955                 return NULL;
2956         }
2957 
2958         if (!uc_nl_evsock_init())
2959                 return NULL;
2960 
2961         if (!fd->registered) {
2962                 fd->fd = nl_socket_get_fd(nl80211_conn.evsock);
2963                 fd->cb = uc_nl_listener_cb;
2964                 uloop_fd_add(fd, ULOOP_READ);
2965         }
2966 
2967         if (!nl80211_conn.evsock_cb) {
2968                 struct nl_cb *cb = nl_cb_alloc(NL_CB_DEFAULT);
2969 
2970                 if (!cb)
2971                         err_return(NLE_NOMEM, NULL);
2972 
2973                 nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, cb_seq, NULL);
2974                 nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_listener_event, NULL);
2975                 nl80211_conn.evsock_cb = cb;
2976         }
2977 
2978         for (i = 0; i < ucv_array_length(listener_registry); i += 2) {
2979                 if (!ucv_array_get(listener_registry, i))
2980                         break;
2981         }
2982 
2983         ucv_array_set(listener_registry, i + 1, ucv_get(cb_func));
2984         l = xalloc(sizeof(*l));
2985         l->index = i;
2986         if (!uc_nl_fill_cmds(l->cmds, cmds)) {
2987                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
2988                 free(l);
2989                 return NULL;
2990         }
2991 
2992         rv = uc_resource_new(listener_type, l);
2993         ucv_array_set(listener_registry, i, ucv_get(rv));
2994         listener_vm = vm;
2995 
2996         return rv;
2997 }
2998 
2999 static void
3000 uc_nl_listener_free(void *arg)
3001 {
3002         uc_nl_listener_t *l = arg;
3003 
3004         ucv_array_set(listener_registry, l->index, NULL);
3005         ucv_array_set(listener_registry, l->index + 1, NULL);
3006         free(l);
3007 }
3008 
3009 /**
3010  * Set the commands this listener should listen for
3011  *
3012  * This method updates the command IDs that the listener will respond to.
3013  * It allows dynamic modification of which events the listener handles.
3014  *
3015  * @param {number[]|number} cmds - Array of command IDs or single command ID to listen for
3016  * @returns {void}
3017  * @example
3018  * // Create a listener initially for interface events
3019  * let listener = listener(callback, [const.NL80211_CMD_NEW_INTERFACE]);
3020  *
3021  * // Later, make it also listen for scan events
3022  * listener.set_commands([const.NL80211_CMD_NEW_INTERFACE, const.NL80211_CMD_NEW_SCAN_RESULTS]);
3023  */
3024 static uc_value_t *
3025 uc_nl_listener_set_commands(uc_vm_t *vm, size_t nargs)
3026 {
3027         uc_nl_listener_t *l = uc_fn_thisval("nl80211.listener");
3028         uc_value_t *cmds = uc_fn_arg(0);
3029 
3030         if (!l)
3031                 return NULL;
3032 
3033         memset(l->cmds, 0, sizeof(l->cmds));
3034         if (!uc_nl_fill_cmds(l->cmds, cmds))
3035                 uc_vm_raise_exception(vm, EXCEPTION_TYPE, "Invalid command ID");
3036 
3037         return NULL;
3038 }
3039 
3040 /**
3041  * Send a nl80211 request from the listener
3042  *
3043  * This method allows the listener to send its own nl80211 requests
3044  * using the same connection used for event listening.
3045  *
3046  * @param {number} cmd - The nl80211 command ID to execute
3047  * @param {number} flags - Netlink flags (optional, default: 0)
3048  * @param {Object} payload - Request payload object with attributes (optional)
3049  * @returns {Object|boolean} Response object from the kernel, or true for
3050  *                           successful acknowledgment without data
3051  * @example
3052  * // Listener sends its own request
3053  * let response = listener.request(const.NL80211_CMD_GET_STATION, 0, {
3054  *     ifindex: 1,
3055  *     mac: "00:11:22:33:44:55"
3056  * });
3057  */
3058 static uc_value_t *
3059 uc_nl_listener_request(uc_vm_t *vm, size_t nargs)
3060 {
3061         return uc_nl_request_common(nl80211_conn.evsock, vm, nargs);
3062 }
3063 
3064 /**
3065  * Close and remove the listener
3066  *
3067  * This method stops the listener from receiving further events and
3068  * cleans up all associated resources. The listener becomes invalid
3069  * after this method is called.
3070  *
3071  * @returns {void}
3072  * @example
3073  * // Create and use a listener
3074  * let listener = listener(callback, [const.NL80211_CMD_NEW_INTERFACE]);
3075  *
3076  * // Later, clean it up
3077  * listener.close();
3078  */
3079 static uc_value_t *
3080 uc_nl_listener_close(uc_vm_t *vm, size_t nargs)
3081 {
3082         uc_nl_listener_t **lptr = uc_fn_this("nl80211.listener");
3083         uc_nl_listener_t *l;
3084 
3085         if (!lptr)
3086                 return NULL;
3087 
3088         l = *lptr;
3089         if (!l)
3090                 return NULL;
3091 
3092         *lptr = NULL;
3093         uc_nl_listener_free(l);
3094 
3095         return NULL;
3096 }
3097 
3098 
3099 static void
3100 register_constants(uc_vm_t *vm, uc_value_t *scope)
3101 {
3102         uc_value_t *c = ucv_object_new(vm);
3103 
3104 #define ADD_CONST(x) ucv_object_add(c, #x, ucv_int64_new(x))
3105 
3106         /**
3107          * @typedef
3108          * @name Netlink message flags
3109          * @property {number} NLM_F_ACK - Request for acknowledgment
3110          * @property {number} NLM_F_ACK_TLVS - Request for acknowledgment with TLVs
3111          * @property {number} NLM_F_APPEND - Append to existing list
3112          * @property {number} NLM_F_ATOMIC - Atomic operation
3113          * @property {number} NLM_F_CAPPED - Request capped
3114          * @property {number} NLM_F_CREATE - Create if not exists
3115          * @property {number} NLM_F_DUMP - Dump request
3116          * @property {number} NLM_F_DUMP_FILTERED - Dump filtered request
3117          * @property {number} NLM_F_DUMP_INTR - Dump interrupted
3118          * @property {number} NLM_F_ECHO - Echo request
3119          * @property {number} NLM_F_EXCL - Exclusive creation
3120          * @property {number} NLM_F_MATCH - Match request
3121          * @property {number} NLM_F_MULTI - Multi-part message
3122          * @property {number} NLM_F_NONREC - Non-recursive operation
3123          * @property {number} NLM_F_REPLACE - Replace existing
3124          * @property {number} NLM_F_REQUEST - Request message
3125          * @property {number} NLM_F_ROOT - Root operation
3126          */
3127         ADD_CONST(NLM_F_ACK);
3128         ADD_CONST(NLM_F_ACK_TLVS);
3129         ADD_CONST(NLM_F_APPEND);
3130         ADD_CONST(NLM_F_ATOMIC);
3131         ADD_CONST(NLM_F_CAPPED);
3132         ADD_CONST(NLM_F_CREATE);
3133         ADD_CONST(NLM_F_DUMP);
3134         ADD_CONST(NLM_F_DUMP_FILTERED);
3135         ADD_CONST(NLM_F_DUMP_INTR);
3136         ADD_CONST(NLM_F_ECHO);
3137         ADD_CONST(NLM_F_EXCL);
3138         ADD_CONST(NLM_F_MATCH);
3139         ADD_CONST(NLM_F_MULTI);
3140         ADD_CONST(NLM_F_NONREC);
3141         ADD_CONST(NLM_F_REPLACE);
3142         ADD_CONST(NLM_F_REQUEST);
3143         ADD_CONST(NLM_F_ROOT);
3144 
3145         /**
3146          * @typedef
3147          * @name nl80211 commands
3148          * @property {number} NL80211_CMD_GET_WIPHY - Get wireless PHY attributes
3149          * @property {number} NL80211_CMD_SET_WIPHY - Set wireless PHY attributes
3150          * @property {number} NL80211_CMD_NEW_WIPHY - Create new wireless PHY
3151          * @property {number} NL80211_CMD_DEL_WIPHY - Delete wireless PHY
3152          * @property {number} NL80211_CMD_GET_INTERFACE - Get interface information
3153          * @property {number} NL80211_CMD_SET_INTERFACE - Set interface attributes
3154          * @property {number} NL80211_CMD_NEW_INTERFACE - Create new interface
3155          * @property {number} NL80211_CMD_DEL_INTERFACE - Delete interface
3156          * @property {number} NL80211_CMD_GET_KEY - Get key
3157          * @property {number} NL80211_CMD_SET_KEY - Set key
3158          * @property {number} NL80211_CMD_NEW_KEY - Add new key
3159          * @property {number} NL80211_CMD_DEL_KEY - Delete key
3160          * @property {number} NL80211_CMD_GET_BEACON - Get beacon
3161          * @property {number} NL80211_CMD_SET_BEACON - Set beacon
3162          * @property {number} NL80211_CMD_NEW_BEACON - Set beacon (alias)
3163          * @property {number} NL80211_CMD_STOP_AP - Stop AP operation
3164          * @property {number} NL80211_CMD_DEL_BEACON - Delete beacon
3165          * @property {number} NL80211_CMD_GET_STATION - Get station information
3166          * @property {number} NL80211_CMD_SET_STATION - Set station attributes
3167          * @property {number} NL80211_CMD_NEW_STATION - Add new station
3168          * @property {number} NL80211_CMD_DEL_STATION - Delete station
3169          * @property {number} NL80211_CMD_GET_MPATH - Get mesh path
3170          * @property {number} NL80211_CMD_SET_MPATH - Set mesh path
3171          * @property {number} NL80211_CMD_NEW_MPATH - Add new mesh path
3172          * @property {number} NL80211_CMD_DEL_MPATH - Delete mesh path
3173          * @property {number} NL80211_CMD_SET_BSS - Set BSS attributes
3174          * @property {number} NL80211_CMD_SET_REG - Set regulatory domain
3175          * @property {number} NL80211_CMD_REQ_SET_REG - Request regulatory domain change
3176          * @property {number} NL80211_CMD_GET_MESH_CONFIG - Get mesh configuration
3177          * @property {number} NL80211_CMD_SET_MESH_CONFIG - Set mesh configuration
3178          * @property {number} NL80211_CMD_GET_REG - Get regulatory domain
3179          * @property {number} NL80211_CMD_GET_SCAN - Get scan results
3180          * @property {number} NL80211_CMD_TRIGGER_SCAN - Trigger scan
3181          * @property {number} NL80211_CMD_NEW_SCAN_RESULTS - New scan results available
3182          * @property {number} NL80211_CMD_SCAN_ABORTED - Scan aborted
3183          * @property {number} NL80211_CMD_REG_CHANGE - Regulatory domain change
3184          * @property {number} NL80211_CMD_AUTHENTICATE - Authenticate
3185          * @property {number} NL80211_CMD_ASSOCIATE - Associate
3186          * @property {number} NL80211_CMD_DEAUTHENTICATE - Deauthenticate
3187          * @property {number} NL80211_CMD_DISASSOCIATE - Disassociate
3188          * @property {number} NL80211_CMD_MICHAEL_MIC_FAILURE - Michael MIC failure
3189          * @property {number} NL80211_CMD_REG_BEACON_HINT - Beacon regulatory hint
3190          * @property {number} NL80211_CMD_JOIN_IBSS - Join IBSS
3191          * @property {number} NL80211_CMD_LEAVE_IBSS - Leave IBSS
3192          * @property {number} NL80211_CMD_TESTMODE - Test mode
3193          * @property {number} NL80211_CMD_CONNECT - Connect
3194          * @property {number} NL80211_CMD_ROAM - Roam
3195          * @property {number} NL80211_CMD_DISCONNECT - Disconnect
3196          * @property {number} NL80211_CMD_SET_WIPHY_NETNS - Set wireless PHY network namespace
3197          * @property {number} NL80211_CMD_GET_SURVEY - Get survey data
3198          * @property {number} NL80211_CMD_NEW_SURVEY_RESULTS - New survey results
3199          * @property {number} NL80211_CMD_SET_PMKSA - Set PMKSA
3200          * @property {number} NL80211_CMD_DEL_PMKSA - Delete PMKSA
3201          * @property {number} NL80211_CMD_FLUSH_PMKSA - Flush PMKSA
3202          * @property {number} NL80211_CMD_REMAIN_ON_CHANNEL - Remain on channel
3203          * @property {number} NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL - Cancel remain on channel
3204          * @property {number} NL80211_CMD_SET_TX_BITRATE_MASK - Set TX bitrate mask
3205          * @property {number} NL80211_CMD_REGISTER_FRAME - Register frame
3206          * @property {number} NL80211_CMD_REGISTER_ACTION - Register action frame
3207          * @property {number} NL80211_CMD_FRAME - Frame
3208          * @property {number} NL80211_CMD_ACTION - Action frame
3209          * @property {number} NL80211_CMD_FRAME_TX_STATUS - Frame TX status
3210          * @property {number} NL80211_CMD_ACTION_TX_STATUS - Action TX status
3211          * @property {number} NL80211_CMD_SET_POWER_SAVE - Set power save
3212          * @property {number} NL80211_CMD_GET_POWER_SAVE - Get power save
3213          * @property {number} NL80211_CMD_SET_CQM - Set CQM
3214          * @property {number} NL80211_CMD_NOTIFY_CQM - Notify CQM
3215          * @property {number} NL80211_CMD_SET_CHANNEL - Set channel
3216          * @property {number} NL80211_CMD_SET_WDS_PEER - Set WDS peer
3217          * @property {number} NL80211_CMD_FRAME_WAIT_CANCEL - Cancel frame wait
3218          * @property {number} NL80211_CMD_JOIN_MESH - Join mesh
3219          * @property {number} NL80211_CMD_LEAVE_MESH - Leave mesh
3220          * @property {number} NL80211_CMD_UNPROT_DEAUTHENTICATE - Unprotected deauthenticate
3221          * @property {number} NL80211_CMD_UNPROT_DISASSOCIATE - Unprotected disassociate
3222          * @property {number} NL80211_CMD_NEW_PEER_CANDIDATE - New peer candidate
3223          * @property {number} NL80211_CMD_GET_WOWLAN - Get WoWLAN
3224          * @property {number} NL80211_CMD_SET_WOWLAN - Set WoWLAN
3225          * @property {number} NL80211_CMD_START_SCHED_SCAN - Start scheduled scan
3226          * @property {number} NL80211_CMD_STOP_SCHED_SCAN - Stop scheduled scan
3227          * @property {number} NL80211_CMD_SCHED_SCAN_RESULTS - Scheduled scan results
3228          * @property {number} NL80211_CMD_SCHED_SCAN_STOPPED - Scheduled scan stopped
3229          * @property {number} NL80211_CMD_SET_REKEY_OFFLOAD - Set rekey offload
3230          * @property {number} NL80211_CMD_PMKSA_CANDIDATE - PMKSA candidate
3231          * @property {number} NL80211_CMD_TDLS_OPER - TDLS operation
3232          * @property {number} NL80211_CMD_TDLS_MGMT - TDLS management
3233          * @property {number} NL80211_CMD_UNEXPECTED_FRAME - Unexpected frame
3234          * @property {number} NL80211_CMD_PROBE_CLIENT - Probe client
3235          * @property {number} NL80211_CMD_REGISTER_BEACONS - Register beacons
3236          * @property {number} NL80211_CMD_UNEXPECTED_4ADDR_FRAME - Unexpected 4-address frame
3237          * @property {number} NL80211_CMD_SET_NOACK_MAP - Set no-ack map
3238          * @property {number} NL80211_CMD_CH_SWITCH_NOTIFY - Channel switch notify
3239          * @property {number} NL80211_CMD_START_P2P_DEVICE - Start P2P device
3240          * @property {number} NL80211_CMD_STOP_P2P_DEVICE - Stop P2P device
3241          * @property {number} NL80211_CMD_CONN_FAILED - Connection failed
3242          * @property {number} NL80211_CMD_SET_MCAST_RATE - Set multicast rate
3243          * @property {number} NL80211_CMD_SET_MAC_ACL - Set MAC ACL
3244          * @property {number} NL80211_CMD_RADAR_DETECT - Radar detect
3245          * @property {number} NL80211_CMD_GET_PROTOCOL_FEATURES - Get protocol features
3246          * @property {number} NL80211_CMD_UPDATE_FT_IES - Update FT IEs
3247          * @property {number} NL80211_CMD_FT_EVENT - FT event
3248          * @property {number} NL80211_CMD_CRIT_PROTOCOL_START - Start critical protocol
3249          * @property {number} NL80211_CMD_CRIT_PROTOCOL_STOP - Stop critical protocol
3250          * @property {number} NL80211_CMD_GET_COALESCE - Get coalesce
3251          * @property {number} NL80211_CMD_SET_COALESCE - Set coalesce
3252          * @property {number} NL80211_CMD_CHANNEL_SWITCH - Channel switch
3253          * @property {number} NL80211_CMD_VENDOR - Vendor command
3254          * @property {number} NL80211_CMD_SET_QOS_MAP - Set QoS map
3255          * @property {number} NL80211_CMD_ADD_TX_TS - Add TX TS
3256          * @property {number} NL80211_CMD_DEL_TX_TS - Delete TX TS
3257          * @property {number} NL80211_CMD_GET_MPP - Get MPP
3258          * @property {number} NL80211_CMD_JOIN_OCB - Join OCB
3259          * @property {number} NL80211_CMD_LEAVE_OCB - Leave OCB
3260          * @property {number} NL80211_CMD_CH_SWITCH_STARTED_NOTIFY - Channel switch started notify
3261          * @property {number} NL80211_CMD_TDLS_CHANNEL_SWITCH - TDLS channel switch
3262          * @property {number} NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH - Cancel TDLS channel switch
3263          * @property {number} NL80211_CMD_ABORT_SCAN - Abort scan
3264          */
3265         ADD_CONST(NL80211_CMD_GET_WIPHY);
3266         ADD_CONST(NL80211_CMD_SET_WIPHY);
3267         ADD_CONST(NL80211_CMD_NEW_WIPHY);
3268         ADD_CONST(NL80211_CMD_DEL_WIPHY);
3269         ADD_CONST(NL80211_CMD_GET_INTERFACE);
3270         ADD_CONST(NL80211_CMD_SET_INTERFACE);
3271         ADD_CONST(NL80211_CMD_NEW_INTERFACE);
3272         ADD_CONST(NL80211_CMD_DEL_INTERFACE);
3273         ADD_CONST(NL80211_CMD_GET_KEY);
3274         ADD_CONST(NL80211_CMD_SET_KEY);
3275         ADD_CONST(NL80211_CMD_NEW_KEY);
3276         ADD_CONST(NL80211_CMD_DEL_KEY);
3277         ADD_CONST(NL80211_CMD_GET_BEACON);
3278         ADD_CONST(NL80211_CMD_SET_BEACON);
3279         ADD_CONST(NL80211_CMD_START_AP);
3280         ADD_CONST(NL80211_CMD_NEW_BEACON);
3281         ADD_CONST(NL80211_CMD_STOP_AP);
3282         ADD_CONST(NL80211_CMD_DEL_BEACON);
3283         ADD_CONST(NL80211_CMD_GET_STATION);
3284         ADD_CONST(NL80211_CMD_SET_STATION);
3285         ADD_CONST(NL80211_CMD_NEW_STATION);
3286         ADD_CONST(NL80211_CMD_DEL_STATION);
3287         ADD_CONST(NL80211_CMD_GET_MPATH);
3288         ADD_CONST(NL80211_CMD_SET_MPATH);
3289         ADD_CONST(NL80211_CMD_NEW_MPATH);
3290         ADD_CONST(NL80211_CMD_DEL_MPATH);
3291         ADD_CONST(NL80211_CMD_SET_BSS);
3292         ADD_CONST(NL80211_CMD_SET_REG);
3293         ADD_CONST(NL80211_CMD_REQ_SET_REG);
3294         ADD_CONST(NL80211_CMD_GET_MESH_CONFIG);
3295         ADD_CONST(NL80211_CMD_SET_MESH_CONFIG);
3296         ADD_CONST(NL80211_CMD_GET_REG);
3297         ADD_CONST(NL80211_CMD_GET_SCAN);
3298         ADD_CONST(NL80211_CMD_TRIGGER_SCAN);
3299         ADD_CONST(NL80211_CMD_NEW_SCAN_RESULTS);
3300         ADD_CONST(NL80211_CMD_SCAN_ABORTED);
3301         ADD_CONST(NL80211_CMD_REG_CHANGE);
3302         ADD_CONST(NL80211_CMD_AUTHENTICATE);
3303         ADD_CONST(NL80211_CMD_ASSOCIATE);
3304         ADD_CONST(NL80211_CMD_DEAUTHENTICATE);
3305         ADD_CONST(NL80211_CMD_DISASSOCIATE);
3306         ADD_CONST(NL80211_CMD_MICHAEL_MIC_FAILURE);
3307         ADD_CONST(NL80211_CMD_REG_BEACON_HINT);
3308         ADD_CONST(NL80211_CMD_JOIN_IBSS);
3309         ADD_CONST(NL80211_CMD_LEAVE_IBSS);
3310         ADD_CONST(NL80211_CMD_TESTMODE);
3311         ADD_CONST(NL80211_CMD_CONNECT);
3312         ADD_CONST(NL80211_CMD_ROAM);
3313         ADD_CONST(NL80211_CMD_DISCONNECT);
3314         ADD_CONST(NL80211_CMD_SET_WIPHY_NETNS);
3315         ADD_CONST(NL80211_CMD_GET_SURVEY);
3316         ADD_CONST(NL80211_CMD_NEW_SURVEY_RESULTS);
3317         ADD_CONST(NL80211_CMD_SET_PMKSA);
3318         ADD_CONST(NL80211_CMD_DEL_PMKSA);
3319         ADD_CONST(NL80211_CMD_FLUSH_PMKSA);
3320         ADD_CONST(NL80211_CMD_REMAIN_ON_CHANNEL);
3321         ADD_CONST(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
3322         ADD_CONST(NL80211_CMD_SET_TX_BITRATE_MASK);
3323         ADD_CONST(NL80211_CMD_REGISTER_FRAME);
3324         ADD_CONST(NL80211_CMD_REGISTER_ACTION);
3325         ADD_CONST(NL80211_CMD_FRAME);
3326         ADD_CONST(NL80211_CMD_ACTION);
3327         ADD_CONST(NL80211_CMD_FRAME_TX_STATUS);
3328         ADD_CONST(NL80211_CMD_ACTION_TX_STATUS);
3329         ADD_CONST(NL80211_CMD_SET_POWER_SAVE);
3330         ADD_CONST(NL80211_CMD_GET_POWER_SAVE);
3331         ADD_CONST(NL80211_CMD_SET_CQM);
3332         ADD_CONST(NL80211_CMD_NOTIFY_CQM);
3333         ADD_CONST(NL80211_CMD_SET_CHANNEL);
3334         ADD_CONST(NL80211_CMD_SET_WDS_PEER);
3335         ADD_CONST(NL80211_CMD_FRAME_WAIT_CANCEL);
3336         ADD_CONST(NL80211_CMD_JOIN_MESH);
3337         ADD_CONST(NL80211_CMD_LEAVE_MESH);
3338         ADD_CONST(NL80211_CMD_UNPROT_DEAUTHENTICATE);
3339         ADD_CONST(NL80211_CMD_UNPROT_DISASSOCIATE);
3340         ADD_CONST(NL80211_CMD_NEW_PEER_CANDIDATE);
3341         ADD_CONST(NL80211_CMD_GET_WOWLAN);
3342         ADD_CONST(NL80211_CMD_SET_WOWLAN);
3343         ADD_CONST(NL80211_CMD_START_SCHED_SCAN);
3344         ADD_CONST(NL80211_CMD_STOP_SCHED_SCAN);
3345         ADD_CONST(NL80211_CMD_SCHED_SCAN_RESULTS);
3346         ADD_CONST(NL80211_CMD_SCHED_SCAN_STOPPED);
3347         ADD_CONST(NL80211_CMD_SET_REKEY_OFFLOAD);
3348         ADD_CONST(NL80211_CMD_PMKSA_CANDIDATE);
3349         ADD_CONST(NL80211_CMD_TDLS_OPER);
3350         ADD_CONST(NL80211_CMD_TDLS_MGMT);
3351         ADD_CONST(NL80211_CMD_UNEXPECTED_FRAME);
3352         ADD_CONST(NL80211_CMD_PROBE_CLIENT);
3353         ADD_CONST(NL80211_CMD_REGISTER_BEACONS);
3354         ADD_CONST(NL80211_CMD_UNEXPECTED_4ADDR_FRAME);
3355         ADD_CONST(NL80211_CMD_SET_NOACK_MAP);
3356         ADD_CONST(NL80211_CMD_CH_SWITCH_NOTIFY);
3357         ADD_CONST(NL80211_CMD_START_P2P_DEVICE);
3358         ADD_CONST(NL80211_CMD_STOP_P2P_DEVICE);
3359         ADD_CONST(NL80211_CMD_CONN_FAILED);
3360         ADD_CONST(NL80211_CMD_SET_MCAST_RATE);
3361         ADD_CONST(NL80211_CMD_SET_MAC_ACL);
3362         ADD_CONST(NL80211_CMD_RADAR_DETECT);
3363         ADD_CONST(NL80211_CMD_GET_PROTOCOL_FEATURES);
3364         ADD_CONST(NL80211_CMD_UPDATE_FT_IES);
3365         ADD_CONST(NL80211_CMD_FT_EVENT);
3366         ADD_CONST(NL80211_CMD_CRIT_PROTOCOL_START);
3367         ADD_CONST(NL80211_CMD_CRIT_PROTOCOL_STOP);
3368         ADD_CONST(NL80211_CMD_GET_COALESCE);
3369         ADD_CONST(NL80211_CMD_SET_COALESCE);
3370         ADD_CONST(NL80211_CMD_CHANNEL_SWITCH);
3371         ADD_CONST(NL80211_CMD_VENDOR);
3372         ADD_CONST(NL80211_CMD_SET_QOS_MAP);
3373         ADD_CONST(NL80211_CMD_ADD_TX_TS);
3374         ADD_CONST(NL80211_CMD_DEL_TX_TS);
3375         ADD_CONST(NL80211_CMD_GET_MPP);
3376         ADD_CONST(NL80211_CMD_JOIN_OCB);
3377         ADD_CONST(NL80211_CMD_LEAVE_OCB);
3378         ADD_CONST(NL80211_CMD_CH_SWITCH_STARTED_NOTIFY);
3379         ADD_CONST(NL80211_CMD_TDLS_CHANNEL_SWITCH);
3380         ADD_CONST(NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
3381         ADD_CONST(NL80211_CMD_ABORT_SCAN);
3382 
3383         /**
3384          * @typedef
3385          * @name Scan flags
3386          * @description Constants for NL80211_ATTR_SCAN_FLAGS bitmask.
3387          * @property {number} NL80211_SCAN_FLAG_LOW_PRIORITY - Low priority scan
3388          * @property {number} NL80211_SCAN_FLAG_FLUSH - Flush scan results before returning
3389          * @property {number} NL80211_SCAN_FLAG_AP - Force AP mode scan
3390          * @property {number} NL80211_SCAN_FLAG_RANDOM_ADDR - Randomize source MAC address
3391          * @property {number} NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME - FILS max channel time
3392          * @property {number} NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP - Accept broadcast probe responses
3393          * @property {number} NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE - OCE high TX rate probe requests
3394          * @property {number} NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION - OCE probe request deferral suppression
3395          * @property {number} NL80211_SCAN_FLAG_LOW_SPAN - Low span scan
3396          * @property {number} NL80211_SCAN_FLAG_LOW_POWER - Low power scan
3397          * @property {number} NL80211_SCAN_FLAG_HIGH_ACCURACY - High accuracy scan
3398          * @property {number} NL80211_SCAN_FLAG_RANDOM_SN - Randomize sequence number
3399          * @property {number} NL80211_SCAN_FLAG_MIN_PREQ_CONTENT - Minimize probe request content
3400          * @property {number} NL80211_SCAN_FLAG_FREQ_KHZ - Report scan results with frequency in KHz
3401          * @property {number} NL80211_SCAN_FLAG_COLOCATED_6GHZ - Scan colocated 6GHz BSS
3402          */
3403         ADD_CONST(NL80211_SCAN_FLAG_LOW_PRIORITY);
3404         ADD_CONST(NL80211_SCAN_FLAG_FLUSH);
3405         ADD_CONST(NL80211_SCAN_FLAG_AP);
3406         ADD_CONST(NL80211_SCAN_FLAG_RANDOM_ADDR);
3407         ADD_CONST(NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME);
3408         ADD_CONST(NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP);
3409         ADD_CONST(NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE);
3410         ADD_CONST(NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION);
3411         ADD_CONST(NL80211_SCAN_FLAG_LOW_SPAN);
3412         ADD_CONST(NL80211_SCAN_FLAG_LOW_POWER);
3413         ADD_CONST(NL80211_SCAN_FLAG_HIGH_ACCURACY);
3414         ADD_CONST(NL80211_SCAN_FLAG_RANDOM_SN);
3415         ADD_CONST(NL80211_SCAN_FLAG_MIN_PREQ_CONTENT);
3416         ADD_CONST(NL80211_SCAN_FLAG_FREQ_KHZ);
3417         ADD_CONST(NL80211_SCAN_FLAG_COLOCATED_6GHZ);
3418 
3419         /**
3420          * @typedef
3421          * @name BSS status constants
3422          * @description Constants for BSS status values.
3423          * @property {number} NL80211_BSS_STATUS_AUTHENTICATED - Authenticated with BSS
3424          * @property {number} NL80211_BSS_STATUS_ASSOCIATED - Associated with BSS
3425          * @property {number} NL80211_BSS_STATUS_IBSS_JOINED - Joined IBSS
3426          */
3427         ADD_CONST(NL80211_BSS_STATUS_AUTHENTICATED);
3428         ADD_CONST(NL80211_BSS_STATUS_ASSOCIATED);
3429         ADD_CONST(NL80211_BSS_STATUS_IBSS_JOINED);
3430 
3431         /**
3432          * @typedef
3433          * @name BSS use-for and cannot-use-reasons constants
3434          * @description Constants for BSS use-for and cannot-use-reasons bitmasks.
3435          * @property {number} NL80211_BSS_USE_FOR_NORMAL - Use BSS for normal connection
3436          * @property {number} NL80211_BSS_USE_FOR_MLD_LINK - Use BSS as MLD link
3437          * @property {number} NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY - NSTR nonprimary link not usable
3438          * @property {number} NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH - 6GHz power mode mismatch
3439          */
3440         ADD_CONST(NL80211_BSS_USE_FOR_NORMAL);
3441         ADD_CONST(NL80211_BSS_USE_FOR_MLD_LINK);
3442         ADD_CONST(NL80211_BSS_CANNOT_USE_NSTR_NONPRIMARY);
3443         ADD_CONST(NL80211_BSS_CANNOT_USE_6GHZ_PWR_MISMATCH);
3444 
3445         /**
3446          * @typedef
3447          * @name HWSIM commands
3448          * @property {number} HWSIM_CMD_REGISTER - Register radio
3449          * @property {number} HWSIM_CMD_FRAME - Send frame
3450          * @property {number} HWSIM_CMD_TX_INFO_FRAME - Send TX info frame
3451          * @property {number} HWSIM_CMD_NEW_RADIO - Create new radio
3452          * @property {number} HWSIM_CMD_DEL_RADIO - Delete radio
3453          * @property {number} HWSIM_CMD_GET_RADIO - Get radio information
3454          * @property {number} HWSIM_CMD_ADD_MAC_ADDR - Add MAC address
3455          * @property {number} HWSIM_CMD_DEL_MAC_ADDR - Delete MAC address
3456          * @property {number} HWSIM_CMD_START_PMSR - Start peer measurement
3457          * @property {number} HWSIM_CMD_ABORT_PMSR - Abort peer measurement
3458          * @property {number} HWSIM_CMD_REPORT_PMSR - Report peer measurement
3459          */
3460         ADD_CONST(HWSIM_CMD_REGISTER),
3461         ADD_CONST(HWSIM_CMD_FRAME),
3462         ADD_CONST(HWSIM_CMD_TX_INFO_FRAME),
3463         ADD_CONST(HWSIM_CMD_NEW_RADIO),
3464         ADD_CONST(HWSIM_CMD_DEL_RADIO),
3465         ADD_CONST(HWSIM_CMD_GET_RADIO),
3466         ADD_CONST(HWSIM_CMD_ADD_MAC_ADDR),
3467         ADD_CONST(HWSIM_CMD_DEL_MAC_ADDR),
3468         ADD_CONST(HWSIM_CMD_START_PMSR),
3469         ADD_CONST(HWSIM_CMD_ABORT_PMSR),
3470         ADD_CONST(HWSIM_CMD_REPORT_PMSR),
3471 
3472         /**
3473          * @typedef
3474          * @name Interface types
3475          * @property {number} NL80211_IFTYPE_ADHOC - IBSS/ad-hoc interface
3476          * @property {number} NL80211_IFTYPE_STATION - Station interface
3477          * @property {number} NL80211_IFTYPE_AP - Access point interface
3478          * @property {number} NL80211_IFTYPE_AP_VLAN - AP VLAN interface
3479          * @property {number} NL80211_IFTYPE_WDS - WDS interface
3480          * @property {number} NL80211_IFTYPE_MONITOR - Monitor interface
3481          * @property {number} NL80211_IFTYPE_MESH_POINT - Mesh point interface
3482          * @property {number} NL80211_IFTYPE_P2P_CLIENT - P2P client interface
3483          * @property {number} NL80211_IFTYPE_P2P_GO - P2P group owner interface
3484          * @property {number} NL80211_IFTYPE_P2P_DEVICE - P2P device interface
3485          * @property {number} NL80211_IFTYPE_OCB - Outside context of BSS (OCB) interface
3486          */
3487         ADD_CONST(NL80211_IFTYPE_ADHOC);
3488         ADD_CONST(NL80211_IFTYPE_STATION);
3489         ADD_CONST(NL80211_IFTYPE_AP);
3490         ADD_CONST(NL80211_IFTYPE_AP_VLAN);
3491         ADD_CONST(NL80211_IFTYPE_WDS);
3492         ADD_CONST(NL80211_IFTYPE_MONITOR);
3493         ADD_CONST(NL80211_IFTYPE_MESH_POINT);
3494         ADD_CONST(NL80211_IFTYPE_P2P_CLIENT);
3495         ADD_CONST(NL80211_IFTYPE_P2P_GO);
3496         ADD_CONST(NL80211_IFTYPE_P2P_DEVICE);
3497         ADD_CONST(NL80211_IFTYPE_OCB);
3498 
3499         ucv_object_add(scope, "const", c);
3500 };
3501 
3502 static const uc_function_list_t global_fns[] = {
3503         { "error",              uc_nl_error },
3504         { "request",    uc_nl_request },
3505         { "waitfor",    uc_nl_waitfor },
3506         { "listener",   uc_nl_listener },
3507 };
3508 
3509 
3510 static const uc_function_list_t listener_fns[] = {
3511         { "set_commands",       uc_nl_listener_set_commands },
3512         { "request",            uc_nl_listener_request },
3513         { "close",                      uc_nl_listener_close },
3514 };
3515 
3516 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
3517 {
3518         uc_function_list_register(scope, global_fns);
3519 
3520         listener_type = uc_type_declare(vm, "nl80211.listener", listener_fns, uc_nl_listener_free);
3521         listener_registry = ucv_array_new(vm);
3522 
3523         uc_vm_registry_set(vm, "nl80211.registry", listener_registry);
3524 
3525         register_constants(vm, scope);
3526 }
3527 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt