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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt