*
* @vendor_commands: array of vendor commands supported by the hardware
* @n_vendor_commands: number of vendor commands
+ * @vendor_events: array of vendor events supported by the hardware
+ * @n_vendor_events: number of vendor events
*/
struct wiphy {
/* assign these fields before you register the wiphy */
const struct wiphy_coalesce_support *coalesce;
const struct wiphy_vendor_command *vendor_commands;
- int n_vendor_commands;
+ const struct nl80211_vendor_cmd_info *vendor_events;
+ int n_vendor_commands, n_vendor_events;
char priv[0] __aligned(NETDEV_ALIGN);
};
enum nl80211_attrs attr,
int approxlen);
+struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int vendor_event_idx,
+ int approxlen, gfp_t gfp);
+
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp);
+
/**
* cfg80211_vendor_cmd_alloc_reply_skb - allocate vendor command reply
* @wiphy: the wiphy
*/
int cfg80211_vendor_cmd_reply(struct sk_buff *skb);
+/**
+ * cfg80211_vendor_event_alloc - allocate vendor-specific event skb
+ * @wiphy: the wiphy
+ * @event_idx: index of the vendor event in the wiphy's vendor_events
+ * @approxlen: an upper bound of the length of the data that will
+ * be put into the skb
+ * @gfp: allocation flags
+ *
+ * This function allocates and pre-fills an skb for an event on the
+ * vendor-specific multicast group.
+ *
+ * When done filling the skb, call cfg80211_vendor_event() with the
+ * skb to send the event.
+ *
+ * Return: An allocated and pre-filled skb. %NULL if any errors happen.
+ */
+static inline struct sk_buff *
+cfg80211_vendor_event_alloc(struct wiphy *wiphy, int approxlen,
+ int event_idx, gfp_t gfp)
+{
+ return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_VENDOR,
+ NL80211_ATTR_VENDOR_DATA,
+ event_idx, approxlen, gfp);
+}
+
+/**
+ * cfg80211_vendor_event - send the event
+ * @skb: The skb, must have been allocated with cfg80211_vendor_event_alloc()
+ * @gfp: allocation flags
+ *
+ * This function sends the given @skb, which must have been allocated
+ * by cfg80211_vendor_event_alloc(), as an event. It always consumes it.
+ */
+static inline void cfg80211_vendor_event(struct sk_buff *skb, gfp_t gfp)
+{
+ __cfg80211_send_event_skb(skb, gfp);
+}
+
#ifdef CONFIG_NL80211_TESTMODE
/**
* DOC: Test mode
*
* Return: An allocated and pre-filled skb. %NULL if any errors happen.
*/
-struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
- int approxlen, gfp_t gfp);
+static inline struct sk_buff *
+cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, int approxlen, gfp_t gfp)
+{
+ return __cfg80211_alloc_event_skb(wiphy, NL80211_CMD_TESTMODE,
+ NL80211_ATTR_TESTDATA, -1,
+ approxlen, gfp);
+}
/**
* cfg80211_testmode_event - send the event
* by cfg80211_testmode_alloc_event_skb(), as an event. It always
* consumes it.
*/
-void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp);
+static inline void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
+{
+ __cfg80211_send_event_skb(skb, gfp);
+}
#define CFG80211_TESTMODE_CMD(cmd) .testmode_cmd = (cmd),
#define CFG80211_TESTMODE_DUMP(cmd) .testmode_dump = (cmd),
NL80211_MCGRP_SCAN,
NL80211_MCGRP_REGULATORY,
NL80211_MCGRP_MLME,
+ NL80211_MCGRP_VENDOR,
NL80211_MCGRP_TESTMODE /* keep last - ifdef! */
};
[NL80211_MCGRP_SCAN] = { .name = "scan", },
[NL80211_MCGRP_REGULATORY] = { .name = "regulatory", },
[NL80211_MCGRP_MLME] = { .name = "mlme", },
+ [NL80211_MCGRP_VENDOR] = { .name = "vendor", },
#ifdef CONFIG_NL80211_TESTMODE
[NL80211_MCGRP_TESTMODE] = { .name = "testmode", }
#endif
struct nlattr *nl_bands, *nl_band;
struct nlattr *nl_freqs, *nl_freq;
struct nlattr *nl_cmds;
- struct nlattr *nl_vendor_cmds;
enum ieee80211_band band;
struct ieee80211_channel *chan;
int i;
state->split_start++;
break;
case 11:
- nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
- if (!nl_vendor_cmds)
- goto nla_put_failure;
+ if (dev->wiphy.n_vendor_commands) {
+ const struct nl80211_vendor_cmd_info *info;
+ struct nlattr *nested;
+
+ nested = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!nested)
+ goto nla_put_failure;
+
+ for (i = 0; i < dev->wiphy.n_vendor_commands; i++) {
+ info = &dev->wiphy.vendor_commands[i].info;
+ if (nla_put(msg, i + 1, sizeof(*info), info))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nested);
+ }
+
+ if (dev->wiphy.n_vendor_events) {
+ const struct nl80211_vendor_cmd_info *info;
+ struct nlattr *nested;
- for (i = 0; i < dev->wiphy.n_vendor_commands; i++)
- if (nla_put(msg, i + 1,
- sizeof(struct nl80211_vendor_cmd_info),
- &dev->wiphy.vendor_commands[i].info))
+ nested = nla_nest_start(msg,
+ NL80211_ATTR_VENDOR_EVENTS);
+ if (!nested)
goto nla_put_failure;
- nla_nest_end(msg, nl_vendor_cmds);
+
+ for (i = 0; i < dev->wiphy.n_vendor_events; i++) {
+ info = &dev->wiphy.vendor_events[i];
+ if (nla_put(msg, i + 1, sizeof(*info), info))
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nested);
+ }
/* done */
state->split_start = 0;
__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 portid, u32 seq,
enum nl80211_commands cmd,
- enum nl80211_attrs attr, gfp_t gfp)
+ enum nl80211_attrs attr,
+ const struct nl80211_vendor_cmd_info *info,
+ gfp_t gfp)
{
struct sk_buff *skb;
void *hdr;
if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
goto nla_put_failure;
+
+ if (info) {
+ if (nla_put_u32(skb, NL80211_ATTR_VENDOR_ID,
+ info->vendor_id))
+ goto nla_put_failure;
+ if (nla_put_u32(skb, NL80211_ATTR_VENDOR_SUBCMD,
+ info->subcmd))
+ goto nla_put_failure;
+ }
+
data = nla_nest_start(skb, attr);
((void **)skb->cb)[0] = rdev;
return err;
}
-struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
- int approxlen, gfp_t gfp)
+struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+ enum nl80211_commands cmd,
+ enum nl80211_attrs attr,
+ int vendor_event_idx,
+ int approxlen, gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+ const struct nl80211_vendor_cmd_info *info;
+
+ switch (cmd) {
+ case NL80211_CMD_TESTMODE:
+ if (WARN_ON(vendor_event_idx != -1))
+ return NULL;
+ info = NULL;
+ break;
+ case NL80211_CMD_VENDOR:
+ if (WARN_ON(vendor_event_idx < 0 ||
+ vendor_event_idx >= wiphy->n_vendor_events))
+ return NULL;
+ info = &wiphy->vendor_events[vendor_event_idx];
+ break;
+ default:
+ WARN_ON(1);
+ return NULL;
+ }
return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
- NL80211_CMD_TESTMODE,
- NL80211_ATTR_TESTDATA, gfp);
+ cmd, attr, info, gfp);
}
-EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
+EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
-void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
+void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp)
{
struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
void *hdr = ((void **)skb->cb)[1];
struct nlattr *data = ((void **)skb->cb)[2];
+ enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE;
nla_nest_end(skb, data);
genlmsg_end(skb, hdr);
+
+ if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
+ mcgrp = NL80211_MCGRP_VENDOR;
+
genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0,
- NL80211_MCGRP_TESTMODE, gfp);
+ mcgrp, gfp);
}
-EXPORT_SYMBOL(cfg80211_testmode_event);
+EXPORT_SYMBOL(__cfg80211_send_event_skb);
#endif
static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
return __cfg80211_alloc_vendor_skb(rdev, approxlen,
rdev->cur_cmd_info->snd_portid,
rdev->cur_cmd_info->snd_seq,
- cmd, attr, GFP_KERNEL);
+ cmd, attr, NULL, GFP_KERNEL);
}
EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);