nl80211: Initial support for vendor commands and events
authorJouni Malinen <j@w1.fi>
Wed, 1 Jan 2014 21:30:23 +0000 (23:30 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 1 Jan 2014 21:47:35 +0000 (23:47 +0200)
Print into the debug log the list of vendor commands and events that the
driver supports. In addition, add a generic handler for vendor events.
This can be extended for each vendor/subcmd.

Signed-hostap: Jouni Malinen <j@w1.fi>

src/drivers/driver_nl80211.c

index 5b82f60..5aa6925 100644 (file)
@@ -497,6 +497,11 @@ static const char * nl80211_command_to_string(enum nl80211_commands cmd)
        C2S(NL80211_CMD_FT_EVENT)
        C2S(NL80211_CMD_CRIT_PROTOCOL_START)
        C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
+       C2S(NL80211_CMD_GET_COALESCE)
+       C2S(NL80211_CMD_SET_COALESCE)
+       C2S(NL80211_CMD_CHANNEL_SWITCH)
+       C2S(NL80211_CMD_VENDOR)
+       C2S(NL80211_CMD_SET_QOS_MAP)
        default:
                return "NL80211_CMD_UNKNOWN";
        }
@@ -2671,6 +2676,48 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
 }
 
 
+static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+                                struct nlattr **tb)
+{
+       u32 vendor_id, subcmd, wiphy = 0;
+       int wiphy_idx;
+       u8 *data = NULL;
+       size_t len = 0;
+
+       if (!tb[NL80211_ATTR_VENDOR_ID] ||
+           !tb[NL80211_ATTR_VENDOR_SUBCMD])
+               return;
+
+       vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]);
+       subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]);
+
+       if (tb[NL80211_ATTR_WIPHY])
+               wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+       wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u",
+                  wiphy, vendor_id, subcmd);
+
+       if (tb[NL80211_ATTR_VENDOR_DATA]) {
+               data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]);
+               len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]);
+               wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len);
+       }
+
+       wiphy_idx = nl80211_get_wiphy_index(drv->first_bss);
+       if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)",
+                          wiphy, wiphy_idx);
+               return;
+       }
+
+       switch (vendor_id) {
+       default:
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+               break;
+       }
+}
+
+
 static void do_process_drv_event(struct i802_bss *bss, int cmd,
                                 struct nlattr **tb)
 {
@@ -2844,6 +2891,9 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
        case NL80211_CMD_STOP_AP:
                nl80211_stop_ap(drv, tb);
                break;
+       case NL80211_CMD_VENDOR:
+               nl80211_vendor_event(drv, tb);
+               break;
        default:
                wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
                        "(cmd=%d)", cmd);
@@ -3488,6 +3538,38 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
                }
        }
 
+       if (tb[NL80211_ATTR_VENDOR_DATA]) {
+               struct nlattr *nl;
+               int rem;
+
+               nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
+                       struct nl80211_vendor_cmd_info *vinfo;
+                       if (nla_len(nl) != sizeof(vinfo)) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+                               continue;
+                       }
+                       vinfo = nla_data(nl);
+                       wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+                                  vinfo->vendor_id, vinfo->subcmd);
+               }
+       }
+
+       if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
+               struct nlattr *nl;
+               int rem;
+
+               nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
+                       struct nl80211_vendor_cmd_info *vinfo;
+                       if (nla_len(nl) != sizeof(vinfo)) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+                               continue;
+                       }
+                       vinfo = nla_data(nl);
+                       wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
+                                  vinfo->vendor_id, vinfo->subcmd);
+               }
+       }
+
        return NL_SKIP;
 }
 
@@ -3716,6 +3798,16 @@ static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
                /* Continue without regulatory events */
        }
 
+       ret = nl_get_multicast_id(global, "nl80211", "vendor");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(global->nl_event, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+                          "membership for vendor events: %d (%s)",
+                          ret, strerror(-ret));
+               /* Continue without vendor events */
+       }
+
        nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
                  no_seq_check, NULL);
        nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,