return ret;
}
+
+static void
+hostapd_parse_vht_map_blobmsg(uint16_t map)
+{
+ char label[4];
+ int16_t val;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ snprintf(label, 4, "%dss", i + 1);
+
+ val = (map & (BIT(1) | BIT(0))) + 7;
+ blobmsg_add_u16(&b, label, val == 10 ? -1 : val);
+ map = map >> 2;
+ }
+}
+
+static void
+hostapd_parse_vht_capab_blobmsg(struct ieee80211_vht_capabilities *vhtc)
+{
+ void *supported_mcs;
+ void *map;
+ int i;
+
+ static const struct {
+ const char *name;
+ uint32_t flag;
+ } vht_capas[] = {
+ { "su_beamformee", VHT_CAP_SU_BEAMFORMEE_CAPABLE },
+ { "mu_beamformee", VHT_CAP_MU_BEAMFORMEE_CAPABLE },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(vht_capas); i++)
+ blobmsg_add_u8(&b, vht_capas[i].name,
+ !!(vhtc->vht_capabilities_info & vht_capas[i].flag));
+
+ supported_mcs = blobmsg_open_table(&b, "mcs_map");
+
+ /* RX map */
+ map = blobmsg_open_table(&b, "rx");
+ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.rx_map));
+ blobmsg_close_table(&b, map);
+
+ /* TX map */
+ map = blobmsg_open_table(&b, "tx");
+ hostapd_parse_vht_map_blobmsg(le_to_host16(vhtc->vht_supported_mcs_set.tx_map));
+ blobmsg_close_table(&b, map);
+
+ blobmsg_close_table(&b, supported_mcs);
+}
+
+static void
+hostapd_parse_capab_blobmsg(struct sta_info *sta)
+{
+ void *r, *v;
+
+ v = blobmsg_open_table(&b, "capabilities");
+
+ if (sta->vht_capabilities) {
+ r = blobmsg_open_table(&b, "vht");
+ hostapd_parse_vht_capab_blobmsg(sta->vht_capabilities);
+ blobmsg_close_table(&b, r);
+ }
+
+ /* ToDo: Add HT / HE capability parsing */
+
+ blobmsg_close_table(&b, v);
+}
+
static int
hostapd_bss_get_clients(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
blobmsg_add_u32(&b, "signal", sta_driver_data.signal);
}
+ hostapd_parse_capab_blobmsg(sta);
+
blobmsg_close_table(&b, c);
}
blobmsg_close_array(&b, list);
return 0;
}
+static int
+hostapd_bss_get_status(struct ubus_context *ctx, struct ubus_object *obj,
+ struct ubus_request_data *req, const char *method,
+ struct blob_attr *msg)
+{
+ struct hostapd_data *hapd = container_of(obj, struct hostapd_data, ubus.obj);
+ void *airtime_table, *dfs_table;
+ struct os_reltime now;
+ char phy_name[17];
+ char mac_buf[20];
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_string(&b, "status", hostapd_state_text(hapd->iface->state));
+ blobmsg_add_u32(&b, "freq", hapd->iface->freq);
+
+ snprintf(phy_name, 17, "%s", hapd->iface->phy);
+ blobmsg_add_string(&b, "phy", phy_name);
+
+ /* Airtime */
+ airtime_table = blobmsg_open_table(&b, "airtime");
+ blobmsg_add_u64(&b, "time", hapd->iface->last_channel_time);
+ blobmsg_add_u64(&b, "time_busy", hapd->iface->last_channel_time_busy);
+ blobmsg_add_u16(&b, "utilization", hapd->iface->channel_utilization);
+ blobmsg_close_table(&b, airtime_table);
+
+ /* DFS */
+ dfs_table = blobmsg_open_table(&b, "dfs");
+ blobmsg_add_u32(&b, "cac_seconds", hapd->iface->dfs_cac_ms / 1000);
+ blobmsg_add_u8(&b, "cac_active", !!(hapd->iface->cac_started));
+ os_reltime_age(&hapd->iface->dfs_cac_start, &now);
+ blobmsg_add_u32(&b, "cac_seconds_left",
+ hapd->iface->cac_started ? hapd->iface->dfs_cac_ms / 1000 - now.sec : 0);
+ blobmsg_close_table(&b, dfs_table);
+
+ ubus_send_reply(ctx, req, b.head);
+
+ return 0;
+}
+
enum {
NOTIFY_RESPONSE,
__NOTIFY_MAX
if (hostapd_add_iface(interfaces, buf))
return UBUS_STATUS_INVALID_ARGUMENT;
+ blob_buf_init(&b, 0);
+ blobmsg_add_u32(&b, "pid", getpid());
+ ubus_send_reply(ctx, req, b.head);
+
return UBUS_STATUS_OK;
}
struct blob_attr *tb[__CSA_MAX];
struct hostapd_data *hapd = get_hapd_from_object(obj);
struct csa_settings css;
+ int ret = UBUS_STATUS_OK;
+ int i;
blobmsg_parse(csa_policy, __CSA_MAX, tb, blob_data(msg), blob_len(msg));
SET_CSA_SETTING(CSA_VHT, freq_params.vht_enabled, bool);
SET_CSA_SETTING(CSA_BLOCK_TX, block_tx, bool);
+ for (i = 0; i < hapd->iface->num_bss; i++) {
+ struct hostapd_data *bss = hapd->iface->bss[i];
- if (hostapd_switch_channel(hapd, &css) != 0)
- return UBUS_STATUS_NOT_SUPPORTED;
- return UBUS_STATUS_OK;
+ if (hostapd_switch_channel(bss, &css) != 0)
+ ret = UBUS_STATUS_NOT_SUPPORTED;
+ }
+
+ return ret;
#undef SET_CSA_SETTING
}
#endif
if (tb[WNM_DISASSOC_ABRIDGED] && blobmsg_get_bool(tb[WNM_DISASSOC_ABRIDGED]))
req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
- if (wnm_send_bss_tm_req(hapd, sta, req_mode, duration, 0, NULL,
+ if (wnm_send_bss_tm_req(hapd, sta, req_mode, duration, duration, NULL,
NULL, nr, nr_len, NULL, 0))
return UBUS_STATUS_UNKNOWN_ERROR;
static const struct ubus_method bss_methods[] = {
UBUS_METHOD_NOARG("reload", hostapd_bss_reload),
UBUS_METHOD_NOARG("get_clients", hostapd_bss_get_clients),
+ UBUS_METHOD_NOARG("get_status", hostapd_bss_get_status),
UBUS_METHOD("del_client", hostapd_bss_del_client, del_policy),
UBUS_METHOD_NOARG("list_bans", hostapd_bss_list_bans),
#ifdef CONFIG_WPS
free(name);
}
+static void
+hostapd_ubus_vlan_action(struct hostapd_data *hapd, struct hostapd_vlan *vlan,
+ const char *action)
+{
+ struct vlan_description *desc = &vlan->vlan_desc;
+ void *c;
+ int i;
+
+ if (!hapd->ubus.obj.has_subscribers)
+ return;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_string(&b, "ifname", vlan->ifname);
+ blobmsg_add_string(&b, "bridge", vlan->bridge);
+ blobmsg_add_u32(&b, "vlan_id", vlan->vlan_id);
+
+ if (desc->notempty) {
+ blobmsg_add_u32(&b, "untagged", desc->untagged);
+ c = blobmsg_open_array(&b, "tagged");
+ for (i = 0; i < ARRAY_SIZE(desc->tagged) && desc->tagged[i]; i++)
+ blobmsg_add_u32(&b, "", desc->tagged[i]);
+ blobmsg_close_array(&b, c);
+ }
+
+ ubus_notify(ctx, &hapd->ubus.obj, action, b.head, -1);
+}
+
+void hostapd_ubus_add_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+ hostapd_ubus_vlan_action(hapd, vlan, "vlan_add");
+}
+
+void hostapd_ubus_remove_vlan(struct hostapd_data *hapd, struct hostapd_vlan *vlan)
+{
+ hostapd_ubus_vlan_action(hapd, vlan, "vlan_remove");
+}
+
static const struct ubus_method daemon_methods[] = {
UBUS_METHOD("config_add", hostapd_config_add, config_add_policy),
UBUS_METHOD("config_remove", hostapd_config_remove, config_remove_policy),