]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/wireless/nl80211.c
Merge branch 'thermal' into release
[linux-2.6-omap-h63xx.git] / net / wireless / nl80211.c
index 572793c8c7ab6b576dae03753b69503ee05f04cf..31b807af3235044557a72cffbf224d4926705280 100644 (file)
@@ -58,6 +58,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
                                      .len = BUS_ID_SIZE-1 },
+       [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+       [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
+       [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
 
        [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
        [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
@@ -84,7 +87,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
                                               .len = NL80211_MAX_SUPP_RATES },
        [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
        [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
-       [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED },
+       [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
        [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
                                .len = IEEE80211_MAX_MESH_ID_LEN },
        [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
@@ -95,6 +98,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
        [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
        [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
        [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
+       [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
+                                          .len = NL80211_MAX_SUPP_RATES },
+
+       [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED },
 
        [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
                                         .len = NL80211_HT_CAPABILITY_LEN },
@@ -157,6 +164,19 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                if (!nl_band)
                        goto nla_put_failure;
 
+               /* add HT info */
+               if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
+                       NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
+                               sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
+                               &dev->wiphy.bands[band]->ht_cap.mcs);
+                       NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
+                               dev->wiphy.bands[band]->ht_cap.cap);
+                       NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+                               dev->wiphy.bands[band]->ht_cap.ampdu_factor);
+                       NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+                               dev->wiphy.bands[band]->ht_cap.ampdu_density);
+               }
+
                /* add frequencies */
                nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
                if (!nl_freqs)
@@ -180,6 +200,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
                        if (chan->flags & IEEE80211_CHAN_RADAR)
                                NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
 
+                       NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+                                   DBM_TO_MBM(chan->max_power));
+
                        nla_nest_end(msg, nl_freq);
                }
 
@@ -269,20 +292,142 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
        return -ENOBUFS;
 }
 
+static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
+       [NL80211_TXQ_ATTR_QUEUE]                = { .type = NLA_U8 },
+       [NL80211_TXQ_ATTR_TXOP]                 = { .type = NLA_U16 },
+       [NL80211_TXQ_ATTR_CWMIN]                = { .type = NLA_U16 },
+       [NL80211_TXQ_ATTR_CWMAX]                = { .type = NLA_U16 },
+       [NL80211_TXQ_ATTR_AIFS]                 = { .type = NLA_U8 },
+};
+
+static int parse_txq_params(struct nlattr *tb[],
+                           struct ieee80211_txq_params *txq_params)
+{
+       if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
+           !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
+           !tb[NL80211_TXQ_ATTR_AIFS])
+               return -EINVAL;
+
+       txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
+       txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
+       txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
+       txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
+       txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
+
+       return 0;
+}
+
 static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev;
-       int result;
-
-       if (!info->attrs[NL80211_ATTR_WIPHY_NAME])
-               return -EINVAL;
+       int result = 0, rem_txq_params = 0;
+       struct nlattr *nl_txq_params;
 
        rdev = cfg80211_get_dev_from_info(info);
        if (IS_ERR(rdev))
                return PTR_ERR(rdev);
 
-       result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+       if (info->attrs[NL80211_ATTR_WIPHY_NAME]) {
+               result = cfg80211_dev_rename(
+                       rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
+               if (result)
+                       goto bad_res;
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
+               struct ieee80211_txq_params txq_params;
+               struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
+
+               if (!rdev->ops->set_txq_params) {
+                       result = -EOPNOTSUPP;
+                       goto bad_res;
+               }
+
+               nla_for_each_nested(nl_txq_params,
+                                   info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
+                                   rem_txq_params) {
+                       nla_parse(tb, NL80211_TXQ_ATTR_MAX,
+                                 nla_data(nl_txq_params),
+                                 nla_len(nl_txq_params),
+                                 txq_params_policy);
+                       result = parse_txq_params(tb, &txq_params);
+                       if (result)
+                               goto bad_res;
+
+                       result = rdev->ops->set_txq_params(&rdev->wiphy,
+                                                          &txq_params);
+                       if (result)
+                               goto bad_res;
+               }
+       }
+
+       if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
+               enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
+               struct ieee80211_channel *chan;
+               struct ieee80211_sta_ht_cap *ht_cap;
+               u32 freq, sec_freq;
+
+               if (!rdev->ops->set_channel) {
+                       result = -EOPNOTSUPP;
+                       goto bad_res;
+               }
+
+               result = -EINVAL;
+
+               if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+                       channel_type = nla_get_u32(info->attrs[
+                                          NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+                       if (channel_type != NL80211_CHAN_NO_HT &&
+                           channel_type != NL80211_CHAN_HT20 &&
+                           channel_type != NL80211_CHAN_HT40PLUS &&
+                           channel_type != NL80211_CHAN_HT40MINUS)
+                               goto bad_res;
+               }
+
+               freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
+               chan = ieee80211_get_channel(&rdev->wiphy, freq);
+
+               /* Primary channel not allowed */
+               if (!chan || chan->flags & IEEE80211_CHAN_DISABLED)
+                       goto bad_res;
+
+               if (channel_type == NL80211_CHAN_HT40MINUS)
+                       sec_freq = freq - 20;
+               else if (channel_type == NL80211_CHAN_HT40PLUS)
+                       sec_freq = freq + 20;
+               else
+                       sec_freq = 0;
+
+               ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap;
+
+               /* no HT capabilities */
+               if (channel_type != NL80211_CHAN_NO_HT &&
+                   !ht_cap->ht_supported)
+                       goto bad_res;
+
+               if (sec_freq) {
+                       struct ieee80211_channel *schan;
+
+                       /* no 40 MHz capabilities */
+                       if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
+                           (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT))
+                               goto bad_res;
 
+                       schan = ieee80211_get_channel(&rdev->wiphy, sec_freq);
+
+                       /* Secondary channel not allowed */
+                       if (!schan || schan->flags & IEEE80211_CHAN_DISABLED)
+                               goto bad_res;
+               }
+
+               result = rdev->ops->set_channel(&rdev->wiphy, chan,
+                                               channel_type);
+               if (result)
+                       goto bad_res;
+       }
+
+
+ bad_res:
        cfg80211_put_dev(rdev);
        return result;
 }
@@ -945,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags)
        return 0;
 }
 
+static u16 nl80211_calculate_bitrate(struct rate_info *rate)
+{
+       int modulation, streams, bitrate;
+
+       if (!(rate->flags & RATE_INFO_FLAGS_MCS))
+               return rate->legacy;
+
+       /* the formula below does only work for MCS values smaller than 32 */
+       if (rate->mcs >= 32)
+               return 0;
+
+       modulation = rate->mcs & 7;
+       streams = (rate->mcs >> 3) + 1;
+
+       bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ?
+                       13500000 : 6500000;
+
+       if (modulation < 4)
+               bitrate *= (modulation + 1);
+       else if (modulation == 4)
+               bitrate *= (modulation + 2);
+       else
+               bitrate *= (modulation + 3);
+
+       bitrate *= streams;
+
+       if (rate->flags & RATE_INFO_FLAGS_SHORT_GI)
+               bitrate = (bitrate / 9) * 10;
+
+       /* do NOT round down here */
+       return (bitrate + 50000) / 100000;
+}
+
 static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
                                int flags, struct net_device *dev,
                                u8 *mac_addr, struct station_info *sinfo)
 {
        void *hdr;
-       struct nlattr *sinfoattr;
+       struct nlattr *sinfoattr, *txrate;
+       u16 bitrate;
 
        hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
        if (!hdr)
@@ -980,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
        if (sinfo->filled & STATION_INFO_PLINK_STATE)
                NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
                            sinfo->plink_state);
+       if (sinfo->filled & STATION_INFO_SIGNAL)
+               NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
+                          sinfo->signal);
+       if (sinfo->filled & STATION_INFO_TX_BITRATE) {
+               txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE);
+               if (!txrate)
+                       goto nla_put_failure;
+
+               /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */
+               bitrate = nl80211_calculate_bitrate(&sinfo->txrate);
+               if (bitrate > 0)
+                       NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
 
+               if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS)
+                       NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS,
+                                   sinfo->txrate.mcs);
+               if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
+                       NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
+               if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI)
+                       NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
+
+               nla_nest_end(msg, txrate);
+       }
        nla_nest_end(msg, sinfoattr);
 
        return genlmsg_end(msg, hdr);
@@ -1598,6 +1799,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
        if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
                params.use_short_slot_time =
                    nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
+       if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
+               params.basic_rates =
+                       nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+               params.basic_rates_len =
+                       nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
+       }
 
        err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
        if (err)
@@ -1680,11 +1887,199 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 #endif
        mutex_lock(&cfg80211_drv_mutex);
-       r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL);
+       r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY);
        mutex_unlock(&cfg80211_drv_mutex);
        return r;
 }
 
+static int nl80211_get_mesh_params(struct sk_buff *skb,
+       struct genl_info *info)
+{
+       struct cfg80211_registered_device *drv;
+       struct mesh_config cur_params;
+       int err;
+       struct net_device *dev;
+       void *hdr;
+       struct nlattr *pinfoattr;
+       struct sk_buff *msg;
+
+       /* Look up our device */
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               return err;
+
+       if (!drv->ops->get_mesh_params) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       /* Get the mesh params */
+       rtnl_lock();
+       err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params);
+       rtnl_unlock();
+       if (err)
+               goto out;
+
+       /* Draw up a netlink message to send back */
+       msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+       if (!msg) {
+               err = -ENOBUFS;
+               goto out;
+       }
+       hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
+                            NL80211_CMD_GET_MESH_PARAMS);
+       if (!hdr)
+               goto nla_put_failure;
+       pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS);
+       if (!pinfoattr)
+               goto nla_put_failure;
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
+                       cur_params.dot11MeshRetryTimeout);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
+                       cur_params.dot11MeshConfirmTimeout);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
+                       cur_params.dot11MeshHoldingTimeout);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+                       cur_params.dot11MeshMaxPeerLinks);
+       NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
+                       cur_params.dot11MeshMaxRetries);
+       NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
+                       cur_params.dot11MeshTTL);
+       NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+                       cur_params.auto_open_plinks);
+       NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+                       cur_params.dot11MeshHWMPmaxPREQretries);
+       NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
+                       cur_params.path_refresh_time);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+                       cur_params.min_discovery_timeout);
+       NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+                       cur_params.dot11MeshHWMPactivePathTimeout);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+                       cur_params.dot11MeshHWMPpreqMinInterval);
+       NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+                       cur_params.dot11MeshHWMPnetDiameterTraversalTime);
+       nla_nest_end(msg, pinfoattr);
+       genlmsg_end(msg, hdr);
+       err = genlmsg_unicast(msg, info->snd_pid);
+       goto out;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       err = -EMSGSIZE;
+out:
+       /* Cleanup */
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+       return err;
+}
+
+#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
+do {\
+       if (table[attr_num]) {\
+               cfg.param = nla_fn(table[attr_num]); \
+               mask |= (1 << (attr_num - 1)); \
+       } \
+} while (0);\
+
+static struct nla_policy
+nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
+       [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
+       [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
+       [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
+
+       [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
+       [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
+       [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
+       [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
+       [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
+};
+
+static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
+{
+       int err;
+       u32 mask;
+       struct cfg80211_registered_device *drv;
+       struct net_device *dev;
+       struct mesh_config cfg;
+       struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
+       struct nlattr *parent_attr;
+
+       parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS];
+       if (!parent_attr)
+               return -EINVAL;
+       if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
+                       parent_attr, nl80211_meshconf_params_policy))
+               return -EINVAL;
+
+       err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+       if (err)
+               return err;
+
+       if (!drv->ops->set_mesh_params) {
+               err = -EOPNOTSUPP;
+               goto out;
+       }
+
+       /* This makes sure that there aren't more than 32 mesh config
+        * parameters (otherwise our bitfield scheme would not work.) */
+       BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
+
+       /* Fill in the params struct */
+       mask = 0;
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
+                       mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
+                       mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
+                       mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
+                       mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
+                       mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
+                       mask, NL80211_MESHCONF_TTL, nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
+                       mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
+                       mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+                       nla_get_u8);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
+                       mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
+                       mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+                       nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
+                       mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+                       nla_get_u32);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
+                       mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+                       nla_get_u16);
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
+                       dot11MeshHWMPnetDiameterTraversalTime,
+                       mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+                       nla_get_u16);
+
+       /* Apply changes */
+       rtnl_lock();
+       err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask);
+       rtnl_unlock();
+
+ out:
+       /* cleanup */
+       cfg80211_put_dev(drv);
+       dev_put(dev);
+       return err;
+}
+
+#undef FILL_IN_MESH_PARAM_IF_SET
+
 static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
@@ -1743,12 +2138,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
        mutex_lock(&cfg80211_drv_mutex);
        r = set_regdom(rd);
        mutex_unlock(&cfg80211_drv_mutex);
-       if (r)
-               goto bad_reg;
-
        return r;
 
-bad_reg:
+ bad_reg:
        kfree(rd);
        return -EINVAL;
 }
@@ -1902,6 +2294,18 @@ static struct genl_ops nl80211_ops[] = {
                .policy = nl80211_policy,
                .flags = GENL_ADMIN_PERM,
        },
+       {
+               .cmd = NL80211_CMD_GET_MESH_PARAMS,
+               .doit = nl80211_get_mesh_params,
+               .policy = nl80211_policy,
+               /* can be retrieved by unprivileged users */
+       },
+       {
+               .cmd = NL80211_CMD_SET_MESH_PARAMS,
+               .doit = nl80211_set_mesh_params,
+               .policy = nl80211_policy,
+               .flags = GENL_ADMIN_PERM,
+       },
 };
 
 /* multicast groups */