Merge branch 'upstream/android-3.10' into linaro-fixes/android-3.10

This commit is contained in:
John Stultz
2014-06-18 09:32:43 -07:00
11 changed files with 832 additions and 128 deletions

View File

@@ -12,4 +12,4 @@ targets += $(DTB_LIST)
dtbs: $(addprefix $(obj)/, $(DTB_LIST))
clean-files := *.dtb
clean-files := dts/*.dtb *.dtb

View File

@@ -417,6 +417,9 @@ static void cpufreq_interactive_timer(unsigned long data)
}
} else {
new_freq = choose_freq(pcpu, loadadjfreq);
if (new_freq > tunables->hispeed_freq &&
pcpu->target_freq < tunables->hispeed_freq)
new_freq = tunables->hispeed_freq;
}
if (pcpu->target_freq >= tunables->hispeed_freq &&

View File

@@ -20,6 +20,8 @@
#include <linux/kobject.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
#include <linux/sort.h>
#include <linux/err.h>
#include <asm/cputime.h>
static spinlock_t cpufreq_stats_lock;
@@ -38,6 +40,20 @@ struct cpufreq_stats {
#endif
};
struct all_cpufreq_stats {
unsigned int state_num;
cputime64_t *time_in_state;
unsigned int *freq_table;
};
struct all_freq_table {
unsigned int *freq_table;
unsigned int table_size;
};
static struct all_freq_table *all_freq_table;
static DEFINE_PER_CPU(struct all_cpufreq_stats *, all_cpufreq_stats);
static DEFINE_PER_CPU(struct cpufreq_stats *, cpufreq_stats_table);
struct cpufreq_stats_attribute {
@@ -48,14 +64,24 @@ struct cpufreq_stats_attribute {
static int cpufreq_stats_update(unsigned int cpu)
{
struct cpufreq_stats *stat;
struct all_cpufreq_stats *all_stat;
unsigned long long cur_time;
cur_time = get_jiffies_64();
spin_lock(&cpufreq_stats_lock);
stat = per_cpu(cpufreq_stats_table, cpu);
if (stat->time_in_state)
all_stat = per_cpu(all_cpufreq_stats, cpu);
if (!stat) {
spin_unlock(&cpufreq_stats_lock);
return 0;
}
if (stat->time_in_state) {
stat->time_in_state[stat->last_index] +=
cur_time - stat->last_time;
if (all_stat)
all_stat->time_in_state[stat->last_index] +=
cur_time - stat->last_time;
}
stat->last_time = cur_time;
spin_unlock(&cpufreq_stats_lock);
return 0;
@@ -86,6 +112,62 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
return len;
}
static int get_index_all_cpufreq_stat(struct all_cpufreq_stats *all_stat,
unsigned int freq)
{
int i;
if (!all_stat)
return -1;
for (i = 0; i < all_stat->state_num; i++) {
if (all_stat->freq_table[i] == freq)
return i;
}
return -1;
}
static ssize_t show_all_time_in_state(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
ssize_t len = 0;
unsigned int i, cpu, freq, index;
struct all_cpufreq_stats *all_stat;
struct cpufreq_policy *policy;
len += scnprintf(buf + len, PAGE_SIZE - len, "freq\t\t");
for_each_possible_cpu(cpu) {
len += scnprintf(buf + len, PAGE_SIZE - len, "cpu%d\t\t", cpu);
if (cpu_online(cpu))
cpufreq_stats_update(cpu);
}
if (!all_freq_table)
goto out;
for (i = 0; i < all_freq_table->table_size; i++) {
freq = all_freq_table->freq_table[i];
len += scnprintf(buf + len, PAGE_SIZE - len, "\n%u\t\t", freq);
for_each_possible_cpu(cpu) {
policy = cpufreq_cpu_get(cpu);
if (policy == NULL)
continue;
all_stat = per_cpu(all_cpufreq_stats, policy->cpu);
index = get_index_all_cpufreq_stat(all_stat, freq);
if (index != -1) {
len += scnprintf(buf + len, PAGE_SIZE - len,
"%llu\t\t", (unsigned long long)
cputime64_to_clock_t(all_stat->time_in_state[index]));
} else {
len += scnprintf(buf + len, PAGE_SIZE - len,
"N/A\t\t");
}
cpufreq_cpu_put(policy);
}
}
out:
len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
return len;
}
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
{
@@ -149,6 +231,9 @@ static struct attribute_group stats_attr_group = {
.name = "stats"
};
static struct kobj_attribute _attr_all_time_in_state = __ATTR(all_time_in_state,
0444, show_all_time_in_state, NULL);
static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
{
int index;
@@ -195,6 +280,29 @@ put_ref:
cpufreq_cpu_put(policy);
}
static void cpufreq_allstats_free(void)
{
int i;
struct all_cpufreq_stats *all_stat;
sysfs_remove_file(cpufreq_global_kobject,
&_attr_all_time_in_state.attr);
for (i = 0; i < total_cpus; i++) {
all_stat = per_cpu(all_cpufreq_stats, i);
if (!all_stat)
continue;
kfree(all_stat->time_in_state);
kfree(all_stat);
per_cpu(all_cpufreq_stats, i) = NULL;
}
if (all_freq_table) {
kfree(all_freq_table->freq_table);
kfree(all_freq_table);
all_freq_table = NULL;
}
}
static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table)
{
@@ -281,6 +389,106 @@ static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy)
stat->cpu = policy->cpu;
}
static int compare_for_sort(const void *lhs_ptr, const void *rhs_ptr)
{
unsigned int lhs = *(const unsigned int *)(lhs_ptr);
unsigned int rhs = *(const unsigned int *)(rhs_ptr);
if (lhs < rhs)
return -1;
if (lhs > rhs)
return 1;
return 0;
}
static bool check_all_freq_table(unsigned int freq)
{
int i;
for (i = 0; i < all_freq_table->table_size; i++) {
if (freq == all_freq_table->freq_table[i])
return true;
}
return false;
}
static void create_all_freq_table(void)
{
all_freq_table = kzalloc(sizeof(struct all_freq_table),
GFP_KERNEL);
if (!all_freq_table)
pr_warn("could not allocate memory for all_freq_table\n");
return;
}
static void add_all_freq_table(unsigned int freq)
{
unsigned int size;
size = sizeof(unsigned int) * (all_freq_table->table_size + 1);
all_freq_table->freq_table = krealloc(all_freq_table->freq_table,
size, GFP_KERNEL);
if (IS_ERR(all_freq_table->freq_table)) {
pr_warn("Could not reallocate memory for freq_table\n");
all_freq_table->freq_table = NULL;
return;
}
all_freq_table->freq_table[all_freq_table->table_size++] = freq;
}
static void cpufreq_allstats_create(unsigned int cpu)
{
int i , j = 0;
unsigned int alloc_size, count = 0;
struct cpufreq_frequency_table *table = cpufreq_frequency_get_table(cpu);
struct all_cpufreq_stats *all_stat;
bool sort_needed = false;
if (!table)
return;
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
count++;
}
all_stat = kzalloc(sizeof(struct all_cpufreq_stats),
GFP_KERNEL);
if (!all_stat) {
pr_warn("Cannot allocate memory for cpufreq stats\n");
return;
}
/*Allocate memory for freq table per cpu as well as clockticks per freq*/
alloc_size = count * sizeof(int) + count * sizeof(cputime64_t);
all_stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
if (!all_stat->time_in_state) {
pr_warn("Cannot allocate memory for cpufreq time_in_state\n");
kfree(all_stat);
all_stat = NULL;
return;
}
all_stat->freq_table = (unsigned int *)
(all_stat->time_in_state + count);
spin_lock(&cpufreq_stats_lock);
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
unsigned int freq = table[i].frequency;
if (freq == CPUFREQ_ENTRY_INVALID)
continue;
all_stat->freq_table[j++] = freq;
if (all_freq_table && !check_all_freq_table(freq)) {
add_all_freq_table(freq);
sort_needed = true;
}
}
if (sort_needed)
sort(all_freq_table->freq_table, all_freq_table->table_size,
sizeof(unsigned int), &compare_for_sort, NULL);
all_stat->state_num = j;
per_cpu(all_cpufreq_stats, cpu) = all_stat;
spin_unlock(&cpufreq_stats_lock);
}
static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
unsigned long val, void *data)
{
@@ -299,6 +507,10 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
table = cpufreq_frequency_get_table(cpu);
if (!table)
return 0;
if (!per_cpu(all_cpufreq_stats, cpu))
cpufreq_allstats_create(cpu);
ret = cpufreq_stats_create_table(policy, table);
if (ret)
return ret;
@@ -355,6 +567,9 @@ static int cpufreq_stats_create_table_cpu(unsigned int cpu)
if (!table)
goto out;
if (!per_cpu(all_cpufreq_stats, cpu))
cpufreq_allstats_create(cpu);
ret = cpufreq_stats_create_table(policy, table);
out:
@@ -430,6 +645,12 @@ static int __init cpufreq_stats_init(void)
return ret;
}
create_all_freq_table();
ret = sysfs_create_file(cpufreq_global_kobject,
&_attr_all_time_in_state.attr);
if (ret)
pr_warn("Error creating sysfs file for cpufreq stats\n");
return 0;
}
static void __exit cpufreq_stats_exit(void)
@@ -445,6 +666,7 @@ static void __exit cpufreq_stats_exit(void)
cpufreq_stats_free_table(cpu);
cpufreq_stats_free_sysfs(cpu);
}
cpufreq_allstats_free();
}
MODULE_AUTHOR("Zou Nan hai <nanhai.zou@intel.com>");

View File

@@ -105,7 +105,10 @@ static ssize_t power_supply_show_property(struct device *dev,
else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
return sprintf(buf, "%s\n", value.strval);
return sprintf(buf, "%d\n", value.intval);
if (off == POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT)
return sprintf(buf, "%lld\n", value.int64val);
else
return sprintf(buf, "%d\n", value.intval);
}
static ssize_t power_supply_store_property(struct device *dev,
@@ -193,6 +196,8 @@ static struct device_attribute power_supply_attrs[] = {
POWER_SUPPLY_ATTR(usb_hc),
POWER_SUPPLY_ATTR(usb_otg),
POWER_SUPPLY_ATTR(charge_enabled),
/* Local extensions of type int64_t */
POWER_SUPPLY_ATTR(charge_counter_ext),
/* Properties of type `const char *' */
POWER_SUPPLY_ATTR(model_name),
POWER_SUPPLY_ATTR(manufacturer),

View File

@@ -15,6 +15,7 @@
#include <linux/workqueue.h>
#include <linux/leds.h>
#include <linux/types.h>
struct device;
@@ -144,6 +145,8 @@ enum power_supply_property {
POWER_SUPPLY_PROP_USB_HC,
POWER_SUPPLY_PROP_USB_OTG,
POWER_SUPPLY_PROP_CHARGE_ENABLED,
/* Local extensions of type int64_t */
POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
@@ -164,6 +167,7 @@ enum power_supply_type {
union power_supply_propval {
int intval;
const char *strval;
int64_t int64val;
};
struct power_supply {

View File

@@ -2254,22 +2254,28 @@ struct cfg80211_ops {
* enum wiphy_flags - wiphy capability flags
*
* @WIPHY_FLAG_CUSTOM_REGULATORY: tells us the driver for this device
* has its own custom regulatory domain and cannot identify the
* ISO / IEC 3166 alpha2 it belongs to. When this is enabled
* we will disregard the first regulatory hint (when the
* initiator is %REGDOM_SET_BY_CORE).
* @WIPHY_FLAG_STRICT_REGULATORY: tells us the driver for this device will
* ignore regulatory domain settings until it gets its own regulatory
* domain via its regulatory_hint() unless the regulatory hint is
* from a country IE. After its gets its own regulatory domain it will
* only allow further regulatory domain settings to further enhance
* compliance. For example if channel 13 and 14 are disabled by this
* regulatory domain no user regulatory domain can enable these channels
* at a later time. This can be used for devices which do not have
* calibration information guaranteed for frequencies or settings
* outside of its regulatory domain. If used in combination with
* WIPHY_FLAG_CUSTOM_REGULATORY the inspected country IE power settings
* will be followed.
* has its own custom regulatory domain and cannot identify the
* ISO / IEC 3166 alpha2 it belongs to. When this is enabled
* we will disregard the first regulatory hint (when the
* initiator is %REGDOM_SET_BY_CORE). wiphys can set the custom
* regulatory domain using wiphy_apply_custom_regulatory()
* prior to wiphy registration.
* @WIPHY_FLAG_STRICT_REGULATORY: tells us that the wiphy for this device
* has regulatory domain that it wishes to be considered as the
* superset for regulatory rules. After this device gets its regulatory
* domain programmed further regulatory hints shall only be considered
* for this device to enhance regulatory compliance, forcing the
* device to only possibly use subsets of the original regulatory
* rules. For example if channel 13 and 14 are disabled by this
* device's regulatory domain no user specified regulatory hint which
* has these channels enabled would enable them for this wiphy,
* the device's original regulatory domain will be trusted as the
* base. You can program the superset of regulatory rules for this
* wiphy with regulatory_hint() for cards programmed with an
* ISO3166-alpha2 country code. wiphys that use regulatory_hint()
* will have their wiphy->regd programmed once the regulatory
* domain is set, and all other regulatory hints will be ignored
* until their own regulatory domain gets programmed.
* @WIPHY_FLAG_DISABLE_BEACON_HINTS: enable this if your driver needs to ensure
* that passive scan flags and beaconing flags may not be lifted by
* cfg80211 due to regulatory beacon hints. For more information on beacon
@@ -2466,6 +2472,34 @@ struct wiphy_wowlan_support {
const struct wiphy_wowlan_tcp_support *tcp;
};
/**
* enum wiphy_vendor_command_flags - validation flags for vendor commands
* @WIPHY_VENDOR_CMD_NEED_WDEV: vendor command requires wdev
* @WIPHY_VENDOR_CMD_NEED_NETDEV: vendor command requires netdev
* @WIPHY_VENDOR_CMD_NEED_RUNNING: interface/wdev must be up & running
* (must be combined with %_WDEV or %_NETDEV)
*/
enum wiphy_vendor_command_flags {
WIPHY_VENDOR_CMD_NEED_WDEV = BIT(0),
WIPHY_VENDOR_CMD_NEED_NETDEV = BIT(1),
WIPHY_VENDOR_CMD_NEED_RUNNING = BIT(2),
};
/**
* struct wiphy_vendor_command - vendor command definition
* @info: vendor command identifying information, as used in nl80211
* @flags: flags, see &enum wiphy_vendor_command_flags
* @doit: callback for the operation, note that wdev is %NULL if the
* flags didn't ask for a wdev and non-%NULL otherwise; the data
* pointer may be %NULL if userspace provided no data at all
*/
struct wiphy_vendor_command {
struct nl80211_vendor_cmd_info info;
u32 flags;
int (*doit)(struct wiphy *wiphy, struct wireless_dev *wdev,
void *data, int data_len);
};
/**
* struct wiphy - wireless hardware description
* @reg_notifier: the driver's regulatory notification callback,
@@ -2573,6 +2607,12 @@ struct wiphy_wowlan_support {
* 802.11-2012 8.4.2.29 for the defined fields.
* @extended_capabilities_mask: mask of the valid values
* @extended_capabilities_len: length of the extended capabilities
* @country_ie_pref: country IE processing preferences specified
* by enum nl80211_country_ie_pref
* @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 */
@@ -2642,6 +2682,8 @@ struct wiphy {
const u8 *extended_capabilities, *extended_capabilities_mask;
u8 extended_capabilities_len;
u8 country_ie_pref;
/* If multiple wiphys are registered and you're handed e.g.
* a regular netdev with assigned ieee80211_ptr, you won't
* know whether it points to a wiphy your driver has registered
@@ -2681,6 +2723,10 @@ struct wiphy {
const struct iw_handler_def *wext;
#endif
const struct wiphy_vendor_command *vendor_commands;
const struct nl80211_vendor_cmd_info *vendor_events;
int n_vendor_commands, n_vendor_events;
char priv[0] __aligned(NETDEV_ALIGN);
};
@@ -3591,6 +3637,121 @@ void wiphy_rfkill_start_polling(struct wiphy *wiphy);
*/
void wiphy_rfkill_stop_polling(struct wiphy *wiphy);
/**
* DOC: Vendor commands
*
* Occasionally, there are special protocol or firmware features that
* can't be implemented very openly. For this and similar cases, the
* vendor command functionality allows implementing the features with
* (typically closed-source) userspace and firmware, using nl80211 as
* the configuration mechanism.
*
* A driver supporting vendor commands must register them as an array
* in struct wiphy, with handlers for each one, each command has an
* OUI and sub command ID to identify it.
*
* Note that this feature should not be (ab)used to implement protocol
* features that could openly be shared across drivers. In particular,
* it must never be required to use vendor commands to implement any
* "normal" functionality that higher-level userspace like connection
* managers etc. need.
*/
struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
enum nl80211_commands cmd,
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
* @approxlen: an upper bound of the length of the data that will
* be put into the skb
*
* This function allocates and pre-fills an skb for a reply to
* a vendor command. Since it is intended for a reply, calling
* it outside of a vendor command's doit() operation is invalid.
*
* The returned skb is pre-filled with some identifying data in
* a way that any data that is put into the skb (with skb_put(),
* nla_put() or similar) will end up being within the
* %NL80211_ATTR_VENDOR_DATA attribute, so all that needs to be done
* with the skb is adding data for the corresponding userspace tool
* which can then read that data out of the testdata attribute. You
* must not modify the skb in any other way.
*
* When done, call cfg80211_vendor_cmd_reply() with the skb and return
* its error code as the result of the doit() operation.
*
* Return: An allocated and pre-filled skb. %NULL if any errors happen.
*/
static inline struct sk_buff *
cfg80211_vendor_cmd_alloc_reply_skb(struct wiphy *wiphy, int approxlen)
{
return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE,
NL80211_ATTR_TESTDATA, approxlen);
}
/**
* cfg80211_vendor_cmd_reply - send the reply skb
* @skb: The skb, must have been allocated with
* cfg80211_vendor_cmd_alloc_reply_skb()
*
* Since calling this function will usually be the last thing
* before returning from the vendor command doit() you should
* return the error code. Note that this function consumes the
* skb regardless of the return value.
*
* Return: An error code or 0 on success.
*/
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
@@ -3626,8 +3787,12 @@ void wiphy_rfkill_stop_polling(struct wiphy *wiphy);
*
* Return: An allocated and pre-filled skb. %NULL if any errors happen.
*/
struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
int approxlen);
static inline struct sk_buff *
cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy, int approxlen)
{
return __cfg80211_alloc_reply_skb(wiphy, NL80211_CMD_TESTMODE,
NL80211_ATTR_TESTDATA, approxlen);
}
/**
* cfg80211_testmode_reply - send the reply skb
@@ -3641,7 +3806,10 @@ struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
*
* Return: An error code or 0 on success.
*/
int cfg80211_testmode_reply(struct sk_buff *skb);
static inline int cfg80211_testmode_reply(struct sk_buff *skb)
{
return cfg80211_vendor_cmd_reply(skb);
}
/**
* cfg80211_testmode_alloc_event_skb - allocate testmode event
@@ -3664,8 +3832,13 @@ int cfg80211_testmode_reply(struct sk_buff *skb);
*
* 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
@@ -3677,7 +3850,10 @@ struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
* 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),

View File

@@ -646,6 +646,28 @@
* @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
* return back to normal.
*
* @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
* @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
*
* @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
* the new channel information (Channel Switch Announcement - CSA)
* in the beacon for some time (as defined in the
* %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
* new channel. Userspace provides the new channel information (using
* %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
* width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
* other station that transmission must be blocked until the channel
* switch is complete.
*
* @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
* by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
* %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
* %NL80211_ATTR_VENDOR_DATA.
* For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
* used in the wiphy data as a nested attribute containing descriptions
* (&struct nl80211_vendor_cmd_info) of the supported vendor commands.
* This may also be sent as an event with the same attributes.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -808,6 +830,13 @@ enum nl80211_commands {
NL80211_CMD_CRIT_PROTOCOL_START,
NL80211_CMD_CRIT_PROTOCOL_STOP,
NL80211_CMD_GET_COALESCE,
NL80211_CMD_SET_COALESCE,
NL80211_CMD_CHANNEL_SWITCH,
NL80211_CMD_VENDOR,
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1429,6 +1458,48 @@ enum nl80211_commands {
* @NL80211_ATTR_MAX_CRIT_PROT_DURATION: duration in milliseconds in which
* the connection should have increased reliability (u16).
*
* @NL80211_ATTR_PEER_AID: Association ID for the peer TDLS station (u16).
* This is similar to @NL80211_ATTR_STA_AID but with a difference of being
* allowed to be used with the first @NL80211_CMD_SET_STATION command to
* update a TDLS peer STA entry.
*
* @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
*
* @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
* until the channel switch event.
* @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
* must be blocked on the current channel (before the channel switch
* operation).
* @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
* for the time while performing a channel switch.
* @NL80211_ATTR_CSA_C_OFF_BEACON: Offset of the channel switch counter
* field in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
* @NL80211_ATTR_CSA_C_OFF_PRESP: Offset of the channel switch counter
* field in the probe response (%NL80211_ATTR_PROBE_RESP).
*
* @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
* As specified in the &enum nl80211_rxmgmt_flags.
*
* @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
*
* @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
* supported operating classes.
*
* @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
* controls DFS operation in IBSS mode. If the flag is included in
* %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
* channels and reports radar events to userspace. Userspace is required
* to react to radar events, e.g. initiate a channel switch or leave the
* IBSS network.
*
* @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
* %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
* @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
* @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
* attribute is also used for vendor command feature advertisement
* @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
* info, containing a nested array of possible events
*
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -1727,6 +1798,35 @@ enum nl80211_attrs {
NL80211_ATTR_CRIT_PROT_ID,
NL80211_ATTR_MAX_CRIT_PROT_DURATION,
NL80211_ATTR_PEER_AID,
NL80211_ATTR_COALESCE_RULE,
NL80211_ATTR_CH_SWITCH_COUNT,
NL80211_ATTR_CH_SWITCH_BLOCK_TX,
NL80211_ATTR_CSA_IES,
NL80211_ATTR_CSA_C_OFF_BEACON,
NL80211_ATTR_CSA_C_OFF_PRESP,
NL80211_ATTR_RXMGMT_FLAGS,
NL80211_ATTR_STA_SUPPORTED_CHANNELS,
NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
NL80211_ATTR_HANDLE_DFS,
NL80211_ATTR_SUPPORT_5_MHZ,
NL80211_ATTR_SUPPORT_10_MHZ,
NL80211_ATTR_OPMODE_NOTIF,
NL80211_ATTR_VENDOR_ID,
NL80211_ATTR_VENDOR_SUBCMD,
NL80211_ATTR_VENDOR_DATA,
NL80211_ATTR_VENDOR_EVENTS,
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -2278,9 +2378,15 @@ enum nl80211_reg_rule_attr {
* enum nl80211_sched_scan_match_attr - scheduled scan match attributes
* @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
* @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
* only report BSS with matching SSID.
* only report BSS with matching SSID.
* @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
* BSS in scan results. Filtering is turned off if not specified.
* BSS in scan results. Filtering is turned off if not specified. Note that
* if this attribute is in a match set of its own, then it is treated as
* the default value for all matchsets with an SSID, rather than being a
* matchset of its own without an RSSI filter. This is due to problems with
* how this API was implemented in the past. Also, due to the same problem,
* the only way to create a matchset with only an RSSI filter (with this
* attribute) is if there's only a single matchset with the RSSI attribute.
* @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
* attribute number currently defined
* @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -3721,4 +3827,24 @@ enum nl80211_crit_proto_id {
/* maximum duration for critical protocol measures */
#define NL80211_CRIT_PROTO_MAX_DURATION 5000 /* msec */
/*
* If this flag is unset, the lower 24 bits are an OUI, if set
* a Linux nl80211 vendor ID is used (no such IDs are allocated
* yet, so that's not valid so far)
*/
#define NL80211_VENDOR_ID_IS_LINUX 0x80000000
/**
* struct nl80211_vendor_cmd_info - vendor command data
* @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
* value is a 24-bit OUI; if it is set then a separately allocated ID
* may be used, but no such IDs are allocated yet. New IDs should be
* added to this file when needed.
* @subcmd: sub-command ID for the command
*/
struct nl80211_vendor_cmd_info {
__u32 vendor_id;
__u32 subcmd;
};
#endif /* __LINUX_NL80211_H */

View File

@@ -2378,6 +2378,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
return -EINVAL;
break;
case PR_SET_TIMERSLACK_PID:
if (current->pid != (pid_t)arg3 &&
!capable(CAP_SYS_NICE))
return -EPERM;
rcu_read_lock();
tsk = find_task_by_pid_ns((pid_t)arg3, &init_pid_ns);
if (tsk == NULL) {

View File

@@ -1950,18 +1950,18 @@ static int qtaguid_ctrl_proc_show(struct seq_file *m, void *v)
"match_found_no_sk_in_ct=%llu "
"match_no_sk=%llu "
"match_no_sk_file=%llu\n",
atomic64_read(&qtu_events.sockets_tagged),
atomic64_read(&qtu_events.sockets_untagged),
atomic64_read(&qtu_events.counter_set_changes),
atomic64_read(&qtu_events.delete_cmds),
atomic64_read(&qtu_events.iface_events),
atomic64_read(&qtu_events.match_calls),
atomic64_read(&qtu_events.match_calls_prepost),
atomic64_read(&qtu_events.match_found_sk),
atomic64_read(&qtu_events.match_found_sk_in_ct),
atomic64_read(&qtu_events.match_found_no_sk_in_ct),
atomic64_read(&qtu_events.match_no_sk),
atomic64_read(&qtu_events.match_no_sk_file));
(u64)atomic64_read(&qtu_events.sockets_tagged),
(u64)atomic64_read(&qtu_events.sockets_untagged),
(u64)atomic64_read(&qtu_events.counter_set_changes),
(u64)atomic64_read(&qtu_events.delete_cmds),
(u64)atomic64_read(&qtu_events.iface_events),
(u64)atomic64_read(&qtu_events.match_calls),
(u64)atomic64_read(&qtu_events.match_calls_prepost),
(u64)atomic64_read(&qtu_events.match_found_sk),
(u64)atomic64_read(&qtu_events.match_found_sk_in_ct),
(u64)atomic64_read(&qtu_events.match_found_no_sk_in_ct),
(u64)atomic64_read(&qtu_events.match_no_sk),
(u64)atomic64_read(&qtu_events.match_no_sk_file));
/* Count the following as part of the last item_index */
prdebug_full_state(0, "proc ctrl");

View File

@@ -77,9 +77,7 @@ struct cfg80211_registered_device {
struct mutex sched_scan_mtx;
#ifdef CONFIG_NL80211_TESTMODE
struct genl_info *testmode_info;
#endif
struct genl_info *cur_cmd_info;
struct work_struct conn_work;
struct work_struct event_work;

View File

@@ -378,6 +378,12 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
[NL80211_ATTR_MDID] = { .type = NLA_U16 },
[NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_PEER_AID] = { .type = NLA_U16 },
[NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
[NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 },
[NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 },
[NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY },
};
/* policy for the key attributes */
@@ -471,10 +477,12 @@ static int nl80211_prepare_wdev_dump(struct sk_buff *skb,
goto out_unlock;
}
*rdev = wiphy_to_dev((*wdev)->wiphy);
cb->args[0] = (*rdev)->wiphy_idx;
/* 0 is the first index - add 1 to parse only once */
cb->args[0] = (*rdev)->wiphy_idx + 1;
cb->args[1] = (*wdev)->identifier;
} else {
struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0]);
/* subtract the 1 again here */
struct wiphy *wiphy = wiphy_idx_to_wiphy(cb->args[0] - 1);
struct wireless_dev *tmp;
if (!wiphy) {
@@ -1518,6 +1526,39 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev,
dev->wiphy.max_acl_mac_addrs))
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;
nested = nla_nest_start(msg,
NL80211_ATTR_VENDOR_EVENTS);
if (!nested)
goto nla_put_failure;
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);
}
/*
* Any information below this point is only available to
* applications that can deal with it being split. This
@@ -2633,8 +2674,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_NEW_KEY);
if (IS_ERR(hdr))
return PTR_ERR(hdr);
if (!hdr)
goto nla_put_failure;
cookie.msg = msg;
cookie.idx = key_idx;
@@ -6391,12 +6432,62 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
return err;
}
static struct sk_buff *
__cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 portid, u32 seq,
enum nl80211_commands cmd,
enum nl80211_attrs attr,
const struct nl80211_vendor_cmd_info *info,
gfp_t gfp)
{
struct sk_buff *skb;
void *hdr;
struct nlattr *data;
skb = nlmsg_new(approxlen + 100, gfp);
if (!skb)
return NULL;
hdr = nl80211hdr_put(skb, portid, seq, 0, cmd);
if (!hdr) {
kfree_skb(skb);
return NULL;
}
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;
((void **)skb->cb)[1] = hdr;
((void **)skb->cb)[2] = data;
return skb;
nla_put_failure:
kfree_skb(skb);
return NULL;
}
#ifdef CONFIG_NL80211_TESTMODE
static struct genl_multicast_group nl80211_testmode_mcgrp = {
.name = "testmode",
};
static struct genl_multicast_group nl80211_vendor_mcgrp = {
.name = "vendor",
};
static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
@@ -6407,11 +6498,11 @@ static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
err = -EOPNOTSUPP;
if (rdev->ops->testmode_cmd) {
rdev->testmode_info = info;
rdev->cur_cmd_info = info;
err = rdev_testmode_cmd(rdev,
nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
rdev->testmode_info = NULL;
rdev->cur_cmd_info = NULL;
}
return err;
@@ -6480,6 +6571,9 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
NL80211_CMD_TESTMODE);
struct nlattr *tmdata;
if (!hdr)
break;
if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
genlmsg_cancel(skb, hdr);
break;
@@ -6512,90 +6606,53 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
return err;
}
static struct sk_buff *
__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
int approxlen, u32 portid, u32 seq, gfp_t gfp)
{
struct sk_buff *skb;
void *hdr;
struct nlattr *data;
skb = nlmsg_new(approxlen + 100, gfp);
if (!skb)
return NULL;
hdr = nl80211hdr_put(skb, portid, seq, 0, NL80211_CMD_TESTMODE);
if (!hdr) {
kfree_skb(skb);
return NULL;
}
if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
goto nla_put_failure;
data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
((void **)skb->cb)[0] = rdev;
((void **)skb->cb)[1] = hdr;
((void **)skb->cb)[2] = data;
return skb;
nla_put_failure:
kfree_skb(skb);
return NULL;
}
struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
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)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
const struct nl80211_vendor_cmd_info *info;
if (WARN_ON(!rdev->testmode_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_testmode_alloc_skb(rdev, approxlen,
rdev->testmode_info->snd_portid,
rdev->testmode_info->snd_seq,
GFP_KERNEL);
}
return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
cmd, attr, info, gfp);
}
EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
int cfg80211_testmode_reply(struct sk_buff *skb)
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];
if (WARN_ON(!rdev->testmode_info)) {
kfree_skb(skb);
return -EINVAL;
}
nla_nest_end(skb, data);
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, rdev->testmode_info);
if (data->nla_type == NL80211_ATTR_VENDOR_DATA)
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), skb, 0,
nl80211_vendor_mcgrp.id, gfp);
else
genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), skb, 0,
nl80211_testmode_mcgrp.id, gfp);
}
EXPORT_SYMBOL(cfg80211_testmode_reply);
struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
int approxlen, gfp_t gfp)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
}
EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
{
void *hdr = ((void **)skb->cb)[1];
struct nlattr *data = ((void **)skb->cb)[2];
nla_nest_end(skb, data);
genlmsg_end(skb, hdr);
genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, 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)
@@ -6916,9 +6973,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_REMAIN_ON_CHANNEL);
if (IS_ERR(hdr)) {
err = PTR_ERR(hdr);
if (!hdr) {
err = -ENOBUFS;
goto free_msg;
}
@@ -7205,9 +7261,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_FRAME);
if (IS_ERR(hdr)) {
err = PTR_ERR(hdr);
if (!hdr) {
err = -ENOBUFS;
goto free_msg;
}
}
@@ -8068,9 +8123,8 @@ static int nl80211_probe_client(struct sk_buff *skb,
hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
NL80211_CMD_PROBE_CLIENT);
if (IS_ERR(hdr)) {
err = PTR_ERR(hdr);
if (!hdr) {
err = -ENOBUFS;
goto free_msg;
}
@@ -8287,6 +8341,108 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
return 0;
}
static int nl80211_vendor_cmd(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct wireless_dev *wdev =
__cfg80211_wdev_from_attrs(genl_info_net(info), info->attrs);
int i, err;
u32 vid, subcmd;
if (!rdev || !rdev->wiphy.vendor_commands)
return -EOPNOTSUPP;
if (IS_ERR(wdev)) {
err = PTR_ERR(wdev);
if (err != -EINVAL)
return err;
wdev = NULL;
} else if (wdev->wiphy != &rdev->wiphy) {
return -EINVAL;
}
if (!info->attrs[NL80211_ATTR_VENDOR_ID] ||
!info->attrs[NL80211_ATTR_VENDOR_SUBCMD])
return -EINVAL;
vid = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_ID]);
subcmd = nla_get_u32(info->attrs[NL80211_ATTR_VENDOR_SUBCMD]);
for (i = 0; i < rdev->wiphy.n_vendor_commands; i++) {
const struct wiphy_vendor_command *vcmd;
void *data = NULL;
int len = 0;
vcmd = &rdev->wiphy.vendor_commands[i];
if (vcmd->info.vendor_id != vid || vcmd->info.subcmd != subcmd)
continue;
if (vcmd->flags & (WIPHY_VENDOR_CMD_NEED_WDEV |
WIPHY_VENDOR_CMD_NEED_NETDEV)) {
if (!wdev)
return -EINVAL;
if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_NETDEV &&
!wdev->netdev)
return -EINVAL;
if (vcmd->flags & WIPHY_VENDOR_CMD_NEED_RUNNING) {
if (!wdev->netdev ||
!netif_running(wdev->netdev))
return -ENETDOWN;
}
} else {
wdev = NULL;
}
if (info->attrs[NL80211_ATTR_VENDOR_DATA]) {
data = nla_data(info->attrs[NL80211_ATTR_VENDOR_DATA]);
len = nla_len(info->attrs[NL80211_ATTR_VENDOR_DATA]);
}
rdev->cur_cmd_info = info;
err = rdev->wiphy.vendor_commands[i].doit(&rdev->wiphy, wdev,
data, len);
rdev->cur_cmd_info = NULL;
return err;
}
return -EOPNOTSUPP;
}
struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
enum nl80211_commands cmd,
enum nl80211_attrs attr,
int approxlen)
{
struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
if (WARN_ON(!rdev->cur_cmd_info))
return NULL;
return __cfg80211_alloc_vendor_skb(rdev, approxlen,
0,
0,
cmd, attr, NULL, GFP_KERNEL);
}
EXPORT_SYMBOL(__cfg80211_alloc_reply_skb);
int cfg80211_vendor_cmd_reply(struct sk_buff *skb)
{
struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
void *hdr = ((void **)skb->cb)[1];
struct nlattr *data = ((void **)skb->cb)[2];
if (WARN_ON(!rdev->cur_cmd_info)) {
kfree_skb(skb);
return -EINVAL;
}
nla_nest_end(skb, data);
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, rdev->cur_cmd_info);
}
EXPORT_SYMBOL(cfg80211_vendor_cmd_reply);
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -8991,7 +9147,14 @@ static struct genl_ops nl80211_ops[] = {
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
NL80211_FLAG_NEED_RTNL,
}
},
{
.cmd = NL80211_CMD_VENDOR,
.doit = nl80211_vendor_cmd,
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
.internal_flags = NL80211_FLAG_NEED_RTNL,
},
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -10811,6 +10974,10 @@ int nl80211_init(void)
goto err_out;
#endif
err = genl_register_mc_group(&nl80211_fam, &nl80211_vendor_mcgrp);
if (err)
goto err_out;
err = netlink_register_notifier(&nl80211_netlink_notifier);
if (err)
goto err_out;