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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt