char *buf, size_t buflen)
{
int ret;
- char *pos;
+ char *pos, *temp = NULL;
u8 *data = NULL;
unsigned int vendor_id, subcmd;
+ enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
struct wpabuf *reply;
size_t data_len = 0;
- /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+ /**
+ * cmd: <vendor id> <subcommand id> [<hex formatted data>]
+ * [nested=<0|1>]
+ */
vendor_id = strtoul(cmd, &pos, 16);
if (!isblank((unsigned char) *pos))
return -EINVAL;
if (*pos != '\0') {
if (!isblank((unsigned char) *pos++))
return -EINVAL;
- data_len = os_strlen(pos);
+
+ temp = os_strchr(pos, ' ');
+ data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
}
if (data_len) {
}
}
+ pos = os_strstr(cmd, "nested=");
+ if (pos)
+ nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED :
+ NESTED_ATTR_NOT_USED;
+
reply = wpabuf_alloc((buflen - 1) / 2);
if (!reply) {
os_free(data);
}
ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
- reply);
+ nested_attr_flag, reply);
if (ret == 0)
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
char cmd[256];
int res;
- if (argc < 2 || argc > 3) {
+ if (argc < 2 || argc > 4) {
printf("Invalid vendor command\n"
- "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
+ "usage: <vendor id> <command id> [<hex formatted command argument>] [nested=<0|1>]\n");
return -1;
}
- res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
- argc == 3 ? argv[2] : "");
+ res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s%s%s", argv[0],
+ argv[1], argc >= 3 ? argv[2] : "",
+ argc == 4 ? " " : "", argc == 4 ? argv[3] : "");
if (os_snprintf_error(sizeof(cmd), res)) {
printf("Too long VENDOR command.\n");
return -1;
static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
int vendor_id, int subcmd,
const u8 *data, size_t data_len,
+ enum nested_attr nested_attr_flag,
struct wpabuf *buf)
{
if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
return -1;
return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
- data_len, buf);
+ data_len, nested_attr_flag, buf);
}
static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
const u8 *pmkid;
};
+/* enum nested_attr - Used to specify if subcommand uses nested attributes */
+enum nested_attr {
+ NESTED_ATTR_NOT_USED = 0,
+ NESTED_ATTR_USED = 1,
+ NESTED_ATTR_UNSPECIFIED = 2,
+};
+
/**
* struct wpa_driver_ops - Driver interface API definition
*
* @priv: Private driver interface data
* @vendor_id: Vendor id
* @subcmd: Vendor command id
+ * @nested_attr_flag: Specifies if vendor subcommand uses nested
+ * attributes or not
* @data: Vendor command parameters (%NULL if no parameters)
* @data_len: Data length
* @buf: Return buffer (%NULL to ignore reply)
*
* This function handles vendor specific commands that are passed to
* the driver/device. The command is identified by vendor id and
- * command id. Parameters can be passed as argument to the command
- * in the data buffer. Reply (if any) will be filled in the supplied
- * return buffer.
+ * command id. The nested_attr_flag specifies whether the subcommand
+ * uses nested attributes or not. Parameters can be passed
+ * as argument to the command in the data buffer. Reply (if any) will be
+ * filled in the supplied return buffer.
*
* The exact driver behavior is driver interface and vendor specific. As
* an example, this will be converted to a vendor specific cfg80211
*/
int (*vendor_cmd)(void *priv, unsigned int vendor_id,
unsigned int subcmd, const u8 *data, size_t data_len,
+ enum nested_attr nested_attr_flag,
struct wpabuf *buf);
/**
}
+static bool is_cmd_with_nested_attrs(unsigned int vendor_id,
+ unsigned int subcmd)
+{
+ if (vendor_id != OUI_QCA)
+ return true;
+
+ switch (subcmd) {
+ case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+ case QCA_NL80211_VENDOR_SUBCMD_STATS_EXT:
+ case QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI:
+ case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY:
+ case QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_STATUS:
+ case QCA_NL80211_VENDOR_SUBCMD_NAN:
+ return false;
+ default:
+ return true;
+ }
+}
+
+
static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
unsigned int subcmd, const u8 *data,
- size_t data_len, struct wpabuf *buf)
+ size_t data_len, enum nested_attr nested_attr,
+ struct wpabuf *buf)
{
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
- int ret;
+ int ret, nla_flag;
#ifdef CONFIG_TESTING_OPTIONS
if (vendor_id == 0xffffffff) {
}
#endif /* CONFIG_TESTING_OPTIONS */
+ if (nested_attr == NESTED_ATTR_USED)
+ nla_flag = NLA_F_NESTED;
+ else if (nested_attr == NESTED_ATTR_UNSPECIFIED &&
+ is_cmd_with_nested_attrs(vendor_id, subcmd))
+ nla_flag = NLA_F_NESTED;
+ else
+ nla_flag = 0;
+
if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
(data &&
- nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data)))
+ nla_put(msg, nla_flag | NL80211_ATTR_VENDOR_DATA,
+ data_len, data)))
goto fail;
ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf,
char *buf, size_t buflen)
{
int ret;
- char *pos;
+ char *pos, *temp = NULL;
u8 *data = NULL;
unsigned int vendor_id, subcmd;
+ enum nested_attr nested_attr_flag = NESTED_ATTR_UNSPECIFIED;
struct wpabuf *reply;
size_t data_len = 0;
- /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+ /**
+ * cmd: <vendor id> <subcommand id> [<hex formatted data>]
+ * [nested=<0|1>]
+ */
vendor_id = strtoul(cmd, &pos, 16);
if (!isblank((unsigned char) *pos))
return -EINVAL;
if (*pos != '\0') {
if (!isblank((unsigned char) *pos++))
return -EINVAL;
- data_len = os_strlen(pos);
+
+ temp = os_strchr(pos, ' ');
+ data_len = temp ? (size_t) (temp - pos) : os_strlen(pos);
}
if (data_len) {
}
}
+ pos = os_strstr(cmd, "nested=");
+ if (pos)
+ nested_attr_flag = atoi(pos + 7) ? NESTED_ATTR_USED :
+ NESTED_ATTR_NOT_USED;
+
reply = wpabuf_alloc((buflen - 1) / 2);
if (!reply) {
os_free(data);
}
ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
- reply);
+ nested_attr_flag, reply);
if (ret == 0)
ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
int vendor_id, int subcmd, const u8 *data,
- size_t data_len, struct wpabuf *buf)
+ size_t data_len,
+ enum nested_attr nested_attr_flag,
+ struct wpabuf *buf)
{
if (!wpa_s->driver->vendor_cmd)
return -1;
return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
- data, data_len, buf);
+ data, data_len, nested_attr_flag, buf);
}
static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,