diff options
author | David S. Miller <davem@davemloft.net> | 2009-02-03 15:41:58 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-02-03 15:41:58 -0500 |
commit | 1725d409caba16ea5fc694bd50e95e79e8ced11a (patch) | |
tree | 688fe26dd4ceda5364692f0ce307aadb6f04f331 /net/wireless | |
parent | b3ff29d2ccfe3af065a9b393699a8fbf2abd1b15 (diff) | |
parent | b8abde45d7d6ab9e8ceced9b5990eeb1149d0b97 (diff) |
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.c | 12 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 81 | ||||
-rw-r--r-- | net/wireless/reg.c | 285 | ||||
-rw-r--r-- | net/wireless/reg.h | 7 | ||||
-rw-r--r-- | net/wireless/sysfs.c | 30 | ||||
-rw-r--r-- | net/wireless/util.c | 2 |
6 files changed, 328 insertions, 89 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index b96fc0c3f1c4..125226476089 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -273,10 +273,16 @@ int wiphy_register(struct wiphy *wiphy) | |||
273 | 273 | ||
274 | sband->band = band; | 274 | sband->band = band; |
275 | 275 | ||
276 | if (!sband->n_channels || !sband->n_bitrates) { | 276 | if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) |
277 | WARN_ON(1); | 277 | return -EINVAL; |
278 | |||
279 | /* | ||
280 | * Since we use a u32 for rate bitmaps in | ||
281 | * ieee80211_get_response_rate, we cannot | ||
282 | * have more than 32 legacy rates. | ||
283 | */ | ||
284 | if (WARN_ON(sband->n_bitrates > 32)) | ||
278 | return -EINVAL; | 285 | return -EINVAL; |
279 | } | ||
280 | 286 | ||
281 | for (i = 0; i < sband->n_channels; i++) { | 287 | for (i = 0; i < sband->n_channels; i++) { |
282 | sband->channels[i].orig_flags = | 288 | sband->channels[i].orig_flags = |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 1e728fff474e..e69da8d20474 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -105,6 +105,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
105 | 105 | ||
106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, | 106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, |
107 | .len = NL80211_HT_CAPABILITY_LEN }, | 107 | .len = NL80211_HT_CAPABILITY_LEN }, |
108 | |||
109 | [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, | ||
110 | [NL80211_ATTR_IE] = { .type = NLA_BINARY, | ||
111 | .len = IEEE80211_MAX_DATA_LEN }, | ||
108 | }; | 112 | }; |
109 | 113 | ||
110 | /* message building helper */ | 114 | /* message building helper */ |
@@ -738,7 +742,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) | |||
738 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | 742 | if (info->attrs[NL80211_ATTR_KEY_IDX]) |
739 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 743 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
740 | 744 | ||
741 | if (key_idx > 3) | 745 | if (key_idx > 5) |
742 | return -EINVAL; | 746 | return -EINVAL; |
743 | 747 | ||
744 | if (info->attrs[NL80211_ATTR_MAC]) | 748 | if (info->attrs[NL80211_ATTR_MAC]) |
@@ -804,30 +808,41 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | |||
804 | int err; | 808 | int err; |
805 | struct net_device *dev; | 809 | struct net_device *dev; |
806 | u8 key_idx; | 810 | u8 key_idx; |
811 | int (*func)(struct wiphy *wiphy, struct net_device *netdev, | ||
812 | u8 key_index); | ||
807 | 813 | ||
808 | if (!info->attrs[NL80211_ATTR_KEY_IDX]) | 814 | if (!info->attrs[NL80211_ATTR_KEY_IDX]) |
809 | return -EINVAL; | 815 | return -EINVAL; |
810 | 816 | ||
811 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 817 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
812 | 818 | ||
813 | if (key_idx > 3) | 819 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) { |
820 | if (key_idx < 4 || key_idx > 5) | ||
821 | return -EINVAL; | ||
822 | } else if (key_idx > 3) | ||
814 | return -EINVAL; | 823 | return -EINVAL; |
815 | 824 | ||
816 | /* currently only support setting default key */ | 825 | /* currently only support setting default key */ |
817 | if (!info->attrs[NL80211_ATTR_KEY_DEFAULT]) | 826 | if (!info->attrs[NL80211_ATTR_KEY_DEFAULT] && |
827 | !info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]) | ||
818 | return -EINVAL; | 828 | return -EINVAL; |
819 | 829 | ||
820 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | 830 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); |
821 | if (err) | 831 | if (err) |
822 | return err; | 832 | return err; |
823 | 833 | ||
824 | if (!drv->ops->set_default_key) { | 834 | if (info->attrs[NL80211_ATTR_KEY_DEFAULT]) |
835 | func = drv->ops->set_default_key; | ||
836 | else | ||
837 | func = drv->ops->set_default_mgmt_key; | ||
838 | |||
839 | if (!func) { | ||
825 | err = -EOPNOTSUPP; | 840 | err = -EOPNOTSUPP; |
826 | goto out; | 841 | goto out; |
827 | } | 842 | } |
828 | 843 | ||
829 | rtnl_lock(); | 844 | rtnl_lock(); |
830 | err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx); | 845 | err = func(&drv->wiphy, dev, key_idx); |
831 | rtnl_unlock(); | 846 | rtnl_unlock(); |
832 | 847 | ||
833 | out: | 848 | out: |
@@ -863,7 +878,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
863 | if (info->attrs[NL80211_ATTR_MAC]) | 878 | if (info->attrs[NL80211_ATTR_MAC]) |
864 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | 879 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); |
865 | 880 | ||
866 | if (key_idx > 3) | 881 | if (key_idx > 5) |
867 | return -EINVAL; | 882 | return -EINVAL; |
868 | 883 | ||
869 | /* | 884 | /* |
@@ -894,6 +909,10 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | |||
894 | if (params.key_len != 13) | 909 | if (params.key_len != 13) |
895 | return -EINVAL; | 910 | return -EINVAL; |
896 | break; | 911 | break; |
912 | case WLAN_CIPHER_SUITE_AES_CMAC: | ||
913 | if (params.key_len != 16) | ||
914 | return -EINVAL; | ||
915 | break; | ||
897 | default: | 916 | default: |
898 | return -EINVAL; | 917 | return -EINVAL; |
899 | } | 918 | } |
@@ -928,7 +947,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | |||
928 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | 947 | if (info->attrs[NL80211_ATTR_KEY_IDX]) |
929 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | 948 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); |
930 | 949 | ||
931 | if (key_idx > 3) | 950 | if (key_idx > 5) |
932 | return -EINVAL; | 951 | return -EINVAL; |
933 | 952 | ||
934 | if (info->attrs[NL80211_ATTR_MAC]) | 953 | if (info->attrs[NL80211_ATTR_MAC]) |
@@ -1889,6 +1908,11 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1889 | mutex_lock(&cfg80211_drv_mutex); | 1908 | mutex_lock(&cfg80211_drv_mutex); |
1890 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); | 1909 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); |
1891 | mutex_unlock(&cfg80211_drv_mutex); | 1910 | mutex_unlock(&cfg80211_drv_mutex); |
1911 | /* This means the regulatory domain was already set, however | ||
1912 | * we don't want to confuse userspace with a "successful error" | ||
1913 | * message so lets just treat it as a success */ | ||
1914 | if (r == -EALREADY) | ||
1915 | r = 0; | ||
1892 | return r; | 1916 | return r; |
1893 | } | 1917 | } |
1894 | 1918 | ||
@@ -2134,6 +2158,43 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
2134 | return -EINVAL; | 2158 | return -EINVAL; |
2135 | } | 2159 | } |
2136 | 2160 | ||
2161 | static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, | ||
2162 | struct genl_info *info) | ||
2163 | { | ||
2164 | struct cfg80211_registered_device *drv; | ||
2165 | int err; | ||
2166 | struct net_device *dev; | ||
2167 | struct mgmt_extra_ie_params params; | ||
2168 | |||
2169 | memset(¶ms, 0, sizeof(params)); | ||
2170 | |||
2171 | if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) | ||
2172 | return -EINVAL; | ||
2173 | params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); | ||
2174 | if (params.subtype > 15) | ||
2175 | return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ | ||
2176 | |||
2177 | if (info->attrs[NL80211_ATTR_IE]) { | ||
2178 | params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); | ||
2179 | params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
2180 | } | ||
2181 | |||
2182 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
2183 | if (err) | ||
2184 | return err; | ||
2185 | |||
2186 | if (drv->ops->set_mgmt_extra_ie) { | ||
2187 | rtnl_lock(); | ||
2188 | err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); | ||
2189 | rtnl_unlock(); | ||
2190 | } else | ||
2191 | err = -EOPNOTSUPP; | ||
2192 | |||
2193 | cfg80211_put_dev(drv); | ||
2194 | dev_put(dev); | ||
2195 | return err; | ||
2196 | } | ||
2197 | |||
2137 | static struct genl_ops nl80211_ops[] = { | 2198 | static struct genl_ops nl80211_ops[] = { |
2138 | { | 2199 | { |
2139 | .cmd = NL80211_CMD_GET_WIPHY, | 2200 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -2295,6 +2356,12 @@ static struct genl_ops nl80211_ops[] = { | |||
2295 | .policy = nl80211_policy, | 2356 | .policy = nl80211_policy, |
2296 | .flags = GENL_ADMIN_PERM, | 2357 | .flags = GENL_ADMIN_PERM, |
2297 | }, | 2358 | }, |
2359 | { | ||
2360 | .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, | ||
2361 | .doit = nl80211_set_mgmt_extra_ie, | ||
2362 | .policy = nl80211_policy, | ||
2363 | .flags = GENL_ADMIN_PERM, | ||
2364 | }, | ||
2298 | }; | 2365 | }; |
2299 | 2366 | ||
2300 | /* multicast groups */ | 2367 | /* multicast groups */ |
diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 85c9034c59b2..f643d3981102 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c | |||
@@ -42,38 +42,6 @@ | |||
42 | #include "core.h" | 42 | #include "core.h" |
43 | #include "reg.h" | 43 | #include "reg.h" |
44 | 44 | ||
45 | /** | ||
46 | * struct regulatory_request - receipt of last regulatory request | ||
47 | * | ||
48 | * @wiphy: this is set if this request's initiator is | ||
49 | * %REGDOM_SET_BY_COUNTRY_IE or %REGDOM_SET_BY_DRIVER. This | ||
50 | * can be used by the wireless core to deal with conflicts | ||
51 | * and potentially inform users of which devices specifically | ||
52 | * cased the conflicts. | ||
53 | * @initiator: indicates who sent this request, could be any of | ||
54 | * of those set in reg_set_by, %REGDOM_SET_BY_* | ||
55 | * @alpha2: the ISO / IEC 3166 alpha2 country code of the requested | ||
56 | * regulatory domain. We have a few special codes: | ||
57 | * 00 - World regulatory domain | ||
58 | * 99 - built by driver but a specific alpha2 cannot be determined | ||
59 | * 98 - result of an intersection between two regulatory domains | ||
60 | * @intersect: indicates whether the wireless core should intersect | ||
61 | * the requested regulatory domain with the presently set regulatory | ||
62 | * domain. | ||
63 | * @country_ie_checksum: checksum of the last processed and accepted | ||
64 | * country IE | ||
65 | * @country_ie_env: lets us know if the AP is telling us we are outdoor, | ||
66 | * indoor, or if it doesn't matter | ||
67 | */ | ||
68 | struct regulatory_request { | ||
69 | struct wiphy *wiphy; | ||
70 | enum reg_set_by initiator; | ||
71 | char alpha2[2]; | ||
72 | bool intersect; | ||
73 | u32 country_ie_checksum; | ||
74 | enum environment_cap country_ie_env; | ||
75 | }; | ||
76 | |||
77 | /* Receipt of information from last regulatory request */ | 45 | /* Receipt of information from last regulatory request */ |
78 | static struct regulatory_request *last_request; | 46 | static struct regulatory_request *last_request; |
79 | 47 | ||
@@ -790,42 +758,35 @@ static u32 map_regdom_flags(u32 rd_flags) | |||
790 | return channel_flags; | 758 | return channel_flags; |
791 | } | 759 | } |
792 | 760 | ||
793 | /** | 761 | static int freq_reg_info_regd(struct wiphy *wiphy, |
794 | * freq_reg_info - get regulatory information for the given frequency | 762 | u32 center_freq, |
795 | * @center_freq: Frequency in KHz for which we want regulatory information for | 763 | u32 *bandwidth, |
796 | * @bandwidth: the bandwidth requirement you have in KHz, if you do not have one | 764 | const struct ieee80211_reg_rule **reg_rule, |
797 | * you can set this to 0. If this frequency is allowed we then set | 765 | const struct ieee80211_regdomain *custom_regd) |
798 | * this value to the maximum allowed bandwidth. | ||
799 | * @reg_rule: the regulatory rule which we have for this frequency | ||
800 | * | ||
801 | * Use this function to get the regulatory rule for a specific frequency on | ||
802 | * a given wireless device. If the device has a specific regulatory domain | ||
803 | * it wants to follow we respect that unless a country IE has been received | ||
804 | * and processed already. | ||
805 | * | ||
806 | * Returns 0 if it was able to find a valid regulatory rule which does | ||
807 | * apply to the given center_freq otherwise it returns non-zero. It will | ||
808 | * also return -ERANGE if we determine the given center_freq does not even have | ||
809 | * a regulatory rule for a frequency range in the center_freq's band. See | ||
810 | * freq_in_rule_band() for our current definition of a band -- this is purely | ||
811 | * subjective and right now its 802.11 specific. | ||
812 | */ | ||
813 | static int freq_reg_info(u32 center_freq, u32 *bandwidth, | ||
814 | const struct ieee80211_reg_rule **reg_rule) | ||
815 | { | 766 | { |
816 | int i; | 767 | int i; |
817 | bool band_rule_found = false; | 768 | bool band_rule_found = false; |
769 | const struct ieee80211_regdomain *regd; | ||
818 | u32 max_bandwidth = 0; | 770 | u32 max_bandwidth = 0; |
819 | 771 | ||
820 | if (!cfg80211_regdomain) | 772 | regd = custom_regd ? custom_regd : cfg80211_regdomain; |
773 | |||
774 | /* Follow the driver's regulatory domain, if present, unless a country | ||
775 | * IE has been processed or a user wants to help complaince further */ | ||
776 | if (last_request->initiator != REGDOM_SET_BY_COUNTRY_IE && | ||
777 | last_request->initiator != REGDOM_SET_BY_USER && | ||
778 | wiphy->regd) | ||
779 | regd = wiphy->regd; | ||
780 | |||
781 | if (!regd) | ||
821 | return -EINVAL; | 782 | return -EINVAL; |
822 | 783 | ||
823 | for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) { | 784 | for (i = 0; i < regd->n_reg_rules; i++) { |
824 | const struct ieee80211_reg_rule *rr; | 785 | const struct ieee80211_reg_rule *rr; |
825 | const struct ieee80211_freq_range *fr = NULL; | 786 | const struct ieee80211_freq_range *fr = NULL; |
826 | const struct ieee80211_power_rule *pr = NULL; | 787 | const struct ieee80211_power_rule *pr = NULL; |
827 | 788 | ||
828 | rr = &cfg80211_regdomain->reg_rules[i]; | 789 | rr = ®d->reg_rules[i]; |
829 | fr = &rr->freq_range; | 790 | fr = &rr->freq_range; |
830 | pr = &rr->power_rule; | 791 | pr = &rr->power_rule; |
831 | 792 | ||
@@ -849,6 +810,14 @@ static int freq_reg_info(u32 center_freq, u32 *bandwidth, | |||
849 | 810 | ||
850 | return !max_bandwidth; | 811 | return !max_bandwidth; |
851 | } | 812 | } |
813 | EXPORT_SYMBOL(freq_reg_info); | ||
814 | |||
815 | int freq_reg_info(struct wiphy *wiphy, u32 center_freq, u32 *bandwidth, | ||
816 | const struct ieee80211_reg_rule **reg_rule) | ||
817 | { | ||
818 | return freq_reg_info_regd(wiphy, center_freq, | ||
819 | bandwidth, reg_rule, NULL); | ||
820 | } | ||
852 | 821 | ||
853 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | 822 | static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, |
854 | unsigned int chan_idx) | 823 | unsigned int chan_idx) |
@@ -867,7 +836,7 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
867 | 836 | ||
868 | flags = chan->orig_flags; | 837 | flags = chan->orig_flags; |
869 | 838 | ||
870 | r = freq_reg_info(MHZ_TO_KHZ(chan->center_freq), | 839 | r = freq_reg_info(wiphy, MHZ_TO_KHZ(chan->center_freq), |
871 | &max_bandwidth, ®_rule); | 840 | &max_bandwidth, ®_rule); |
872 | 841 | ||
873 | if (r) { | 842 | if (r) { |
@@ -907,6 +876,22 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, | |||
907 | 876 | ||
908 | power_rule = ®_rule->power_rule; | 877 | power_rule = ®_rule->power_rule; |
909 | 878 | ||
879 | if (last_request->initiator == REGDOM_SET_BY_DRIVER && | ||
880 | last_request->wiphy && last_request->wiphy == wiphy && | ||
881 | last_request->wiphy->strict_regulatory) { | ||
882 | /* This gaurantees the driver's requested regulatory domain | ||
883 | * will always be used as a base for further regulatory | ||
884 | * settings */ | ||
885 | chan->flags = chan->orig_flags = | ||
886 | map_regdom_flags(reg_rule->flags); | ||
887 | chan->max_antenna_gain = chan->orig_mag = | ||
888 | (int) MBI_TO_DBI(power_rule->max_antenna_gain); | ||
889 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | ||
890 | chan->max_power = chan->orig_mpwr = | ||
891 | (int) MBM_TO_DBM(power_rule->max_eirp); | ||
892 | return; | ||
893 | } | ||
894 | |||
910 | chan->flags = flags | map_regdom_flags(reg_rule->flags); | 895 | chan->flags = flags | map_regdom_flags(reg_rule->flags); |
911 | chan->max_antenna_gain = min(chan->orig_mag, | 896 | chan->max_antenna_gain = min(chan->orig_mag, |
912 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); | 897 | (int) MBI_TO_DBI(power_rule->max_antenna_gain)); |
@@ -935,7 +920,12 @@ static bool ignore_reg_update(struct wiphy *wiphy, enum reg_set_by setby) | |||
935 | if (!last_request) | 920 | if (!last_request) |
936 | return true; | 921 | return true; |
937 | if (setby == REGDOM_SET_BY_CORE && | 922 | if (setby == REGDOM_SET_BY_CORE && |
938 | wiphy->fw_handles_regulatory) | 923 | wiphy->custom_regulatory) |
924 | return true; | ||
925 | /* wiphy->regd will be set once the device has its own | ||
926 | * desired regulatory domain set */ | ||
927 | if (wiphy->strict_regulatory && !wiphy->regd && | ||
928 | !is_world_regdom(last_request->alpha2)) | ||
939 | return true; | 929 | return true; |
940 | return false; | 930 | return false; |
941 | } | 931 | } |
@@ -945,20 +935,103 @@ static void update_all_wiphy_regulatory(enum reg_set_by setby) | |||
945 | struct cfg80211_registered_device *drv; | 935 | struct cfg80211_registered_device *drv; |
946 | 936 | ||
947 | list_for_each_entry(drv, &cfg80211_drv_list, list) | 937 | list_for_each_entry(drv, &cfg80211_drv_list, list) |
948 | if (!ignore_reg_update(&drv->wiphy, setby)) | 938 | wiphy_update_regulatory(&drv->wiphy, setby); |
949 | wiphy_update_regulatory(&drv->wiphy, setby); | ||
950 | } | 939 | } |
951 | 940 | ||
952 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) | 941 | void wiphy_update_regulatory(struct wiphy *wiphy, enum reg_set_by setby) |
953 | { | 942 | { |
954 | enum ieee80211_band band; | 943 | enum ieee80211_band band; |
944 | |||
945 | if (ignore_reg_update(wiphy, setby)) | ||
946 | return; | ||
955 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | 947 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { |
956 | if (wiphy->bands[band]) | 948 | if (wiphy->bands[band]) |
957 | handle_band(wiphy, band); | 949 | handle_band(wiphy, band); |
958 | if (wiphy->reg_notifier) | 950 | } |
959 | wiphy->reg_notifier(wiphy, setby); | 951 | if (wiphy->reg_notifier) |
952 | wiphy->reg_notifier(wiphy, last_request); | ||
953 | } | ||
954 | |||
955 | static void handle_channel_custom(struct wiphy *wiphy, | ||
956 | enum ieee80211_band band, | ||
957 | unsigned int chan_idx, | ||
958 | const struct ieee80211_regdomain *regd) | ||
959 | { | ||
960 | int r; | ||
961 | u32 max_bandwidth = 0; | ||
962 | const struct ieee80211_reg_rule *reg_rule = NULL; | ||
963 | const struct ieee80211_power_rule *power_rule = NULL; | ||
964 | struct ieee80211_supported_band *sband; | ||
965 | struct ieee80211_channel *chan; | ||
966 | |||
967 | sband = wiphy->bands[band]; | ||
968 | BUG_ON(chan_idx >= sband->n_channels); | ||
969 | chan = &sband->channels[chan_idx]; | ||
970 | |||
971 | r = freq_reg_info_regd(wiphy, MHZ_TO_KHZ(chan->center_freq), | ||
972 | &max_bandwidth, ®_rule, regd); | ||
973 | |||
974 | if (r) { | ||
975 | chan->flags = IEEE80211_CHAN_DISABLED; | ||
976 | return; | ||
977 | } | ||
978 | |||
979 | power_rule = ®_rule->power_rule; | ||
980 | |||
981 | chan->flags |= map_regdom_flags(reg_rule->flags); | ||
982 | chan->max_antenna_gain = (int) MBI_TO_DBI(power_rule->max_antenna_gain); | ||
983 | chan->max_bandwidth = KHZ_TO_MHZ(max_bandwidth); | ||
984 | chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); | ||
985 | } | ||
986 | |||
987 | static void handle_band_custom(struct wiphy *wiphy, enum ieee80211_band band, | ||
988 | const struct ieee80211_regdomain *regd) | ||
989 | { | ||
990 | unsigned int i; | ||
991 | struct ieee80211_supported_band *sband; | ||
992 | |||
993 | BUG_ON(!wiphy->bands[band]); | ||
994 | sband = wiphy->bands[band]; | ||
995 | |||
996 | for (i = 0; i < sband->n_channels; i++) | ||
997 | handle_channel_custom(wiphy, band, i, regd); | ||
998 | } | ||
999 | |||
1000 | /* Used by drivers prior to wiphy registration */ | ||
1001 | void wiphy_apply_custom_regulatory(struct wiphy *wiphy, | ||
1002 | const struct ieee80211_regdomain *regd) | ||
1003 | { | ||
1004 | enum ieee80211_band band; | ||
1005 | for (band = 0; band < IEEE80211_NUM_BANDS; band++) { | ||
1006 | if (wiphy->bands[band]) | ||
1007 | handle_band_custom(wiphy, band, regd); | ||
960 | } | 1008 | } |
961 | } | 1009 | } |
1010 | EXPORT_SYMBOL(wiphy_apply_custom_regulatory); | ||
1011 | |||
1012 | static int reg_copy_regd(const struct ieee80211_regdomain **dst_regd, | ||
1013 | const struct ieee80211_regdomain *src_regd) | ||
1014 | { | ||
1015 | struct ieee80211_regdomain *regd; | ||
1016 | int size_of_regd = 0; | ||
1017 | unsigned int i; | ||
1018 | |||
1019 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
1020 | ((src_regd->n_reg_rules + 1) * sizeof(struct ieee80211_reg_rule)); | ||
1021 | |||
1022 | regd = kzalloc(size_of_regd, GFP_KERNEL); | ||
1023 | if (!regd) | ||
1024 | return -ENOMEM; | ||
1025 | |||
1026 | memcpy(regd, src_regd, sizeof(struct ieee80211_regdomain)); | ||
1027 | |||
1028 | for (i = 0; i < src_regd->n_reg_rules; i++) | ||
1029 | memcpy(®d->reg_rules[i], &src_regd->reg_rules[i], | ||
1030 | sizeof(struct ieee80211_reg_rule)); | ||
1031 | |||
1032 | *dst_regd = regd; | ||
1033 | return 0; | ||
1034 | } | ||
962 | 1035 | ||
963 | /* Return value which can be used by ignore_request() to indicate | 1036 | /* Return value which can be used by ignore_request() to indicate |
964 | * it has been determined we should intersect two regulatory domains */ | 1037 | * it has been determined we should intersect two regulatory domains */ |
@@ -1007,9 +1080,14 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1007 | } | 1080 | } |
1008 | return REG_INTERSECT; | 1081 | return REG_INTERSECT; |
1009 | case REGDOM_SET_BY_DRIVER: | 1082 | case REGDOM_SET_BY_DRIVER: |
1010 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | 1083 | if (last_request->initiator == REGDOM_SET_BY_CORE) { |
1084 | if (is_old_static_regdom(cfg80211_regdomain)) | ||
1085 | return 0; | ||
1086 | if (!alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | ||
1087 | return 0; | ||
1011 | return -EALREADY; | 1088 | return -EALREADY; |
1012 | return 0; | 1089 | } |
1090 | return REG_INTERSECT; | ||
1013 | case REGDOM_SET_BY_USER: | 1091 | case REGDOM_SET_BY_USER: |
1014 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) | 1092 | if (last_request->initiator == REGDOM_SET_BY_COUNTRY_IE) |
1015 | return REG_INTERSECT; | 1093 | return REG_INTERSECT; |
@@ -1018,6 +1096,20 @@ static int ignore_request(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1018 | if (last_request->initiator == REGDOM_SET_BY_USER && | 1096 | if (last_request->initiator == REGDOM_SET_BY_USER && |
1019 | last_request->intersect) | 1097 | last_request->intersect) |
1020 | return -EOPNOTSUPP; | 1098 | return -EOPNOTSUPP; |
1099 | /* Process user requests only after previous user/driver/core | ||
1100 | * requests have been processed */ | ||
1101 | if (last_request->initiator == REGDOM_SET_BY_CORE || | ||
1102 | last_request->initiator == REGDOM_SET_BY_DRIVER || | ||
1103 | last_request->initiator == REGDOM_SET_BY_USER) { | ||
1104 | if (!alpha2_equal(last_request->alpha2, | ||
1105 | cfg80211_regdomain->alpha2)) | ||
1106 | return -EAGAIN; | ||
1107 | } | ||
1108 | |||
1109 | if (!is_old_static_regdom(cfg80211_regdomain) && | ||
1110 | alpha2_equal(cfg80211_regdomain->alpha2, alpha2)) | ||
1111 | return -EALREADY; | ||
1112 | |||
1021 | return 0; | 1113 | return 0; |
1022 | } | 1114 | } |
1023 | 1115 | ||
@@ -1036,11 +1128,28 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1036 | 1128 | ||
1037 | r = ignore_request(wiphy, set_by, alpha2); | 1129 | r = ignore_request(wiphy, set_by, alpha2); |
1038 | 1130 | ||
1039 | if (r == REG_INTERSECT) | 1131 | if (r == REG_INTERSECT) { |
1132 | if (set_by == REGDOM_SET_BY_DRIVER) { | ||
1133 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | ||
1134 | if (r) | ||
1135 | return r; | ||
1136 | } | ||
1040 | intersect = true; | 1137 | intersect = true; |
1041 | else if (r) | 1138 | } else if (r) { |
1139 | /* If the regulatory domain being requested by the | ||
1140 | * driver has already been set just copy it to the | ||
1141 | * wiphy */ | ||
1142 | if (r == -EALREADY && set_by == REGDOM_SET_BY_DRIVER) { | ||
1143 | r = reg_copy_regd(&wiphy->regd, cfg80211_regdomain); | ||
1144 | if (r) | ||
1145 | return r; | ||
1146 | r = -EALREADY; | ||
1147 | goto new_request; | ||
1148 | } | ||
1042 | return r; | 1149 | return r; |
1150 | } | ||
1043 | 1151 | ||
1152 | new_request: | ||
1044 | request = kzalloc(sizeof(struct regulatory_request), | 1153 | request = kzalloc(sizeof(struct regulatory_request), |
1045 | GFP_KERNEL); | 1154 | GFP_KERNEL); |
1046 | if (!request) | 1155 | if (!request) |
@@ -1056,6 +1165,11 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1056 | 1165 | ||
1057 | kfree(last_request); | 1166 | kfree(last_request); |
1058 | last_request = request; | 1167 | last_request = request; |
1168 | |||
1169 | /* When r == REG_INTERSECT we do need to call CRDA */ | ||
1170 | if (r < 0) | ||
1171 | return r; | ||
1172 | |||
1059 | /* | 1173 | /* |
1060 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled | 1174 | * Note: When CONFIG_WIRELESS_OLD_REGULATORY is enabled |
1061 | * AND if CRDA is NOT present nothing will happen, if someone | 1175 | * AND if CRDA is NOT present nothing will happen, if someone |
@@ -1071,10 +1185,15 @@ int __regulatory_hint(struct wiphy *wiphy, enum reg_set_by set_by, | |||
1071 | 1185 | ||
1072 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) | 1186 | void regulatory_hint(struct wiphy *wiphy, const char *alpha2) |
1073 | { | 1187 | { |
1188 | int r; | ||
1074 | BUG_ON(!alpha2); | 1189 | BUG_ON(!alpha2); |
1075 | 1190 | ||
1076 | mutex_lock(&cfg80211_drv_mutex); | 1191 | mutex_lock(&cfg80211_drv_mutex); |
1077 | __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, alpha2, 0, ENVIRON_ANY); | 1192 | r = __regulatory_hint(wiphy, REGDOM_SET_BY_DRIVER, |
1193 | alpha2, 0, ENVIRON_ANY); | ||
1194 | /* This is required so that the orig_* parameters are saved */ | ||
1195 | if (r == -EALREADY && wiphy->strict_regulatory) | ||
1196 | wiphy_update_regulatory(wiphy, REGDOM_SET_BY_DRIVER); | ||
1078 | mutex_unlock(&cfg80211_drv_mutex); | 1197 | mutex_unlock(&cfg80211_drv_mutex); |
1079 | } | 1198 | } |
1080 | EXPORT_SYMBOL(regulatory_hint); | 1199 | EXPORT_SYMBOL(regulatory_hint); |
@@ -1247,7 +1366,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) | |||
1247 | "domain intersected: \n"); | 1366 | "domain intersected: \n"); |
1248 | } else | 1367 | } else |
1249 | printk(KERN_INFO "cfg80211: Current regulatory " | 1368 | printk(KERN_INFO "cfg80211: Current regulatory " |
1250 | "intersected: \n"); | 1369 | "domain intersected: \n"); |
1251 | } else if (is_world_regdom(rd->alpha2)) | 1370 | } else if (is_world_regdom(rd->alpha2)) |
1252 | printk(KERN_INFO "cfg80211: World regulatory " | 1371 | printk(KERN_INFO "cfg80211: World regulatory " |
1253 | "domain updated:\n"); | 1372 | "domain updated:\n"); |
@@ -1349,6 +1468,23 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
1349 | } | 1468 | } |
1350 | 1469 | ||
1351 | if (!last_request->intersect) { | 1470 | if (!last_request->intersect) { |
1471 | int r; | ||
1472 | |||
1473 | if (last_request->initiator != REGDOM_SET_BY_DRIVER) { | ||
1474 | reset_regdomains(); | ||
1475 | cfg80211_regdomain = rd; | ||
1476 | return 0; | ||
1477 | } | ||
1478 | |||
1479 | /* For a driver hint, lets copy the regulatory domain the | ||
1480 | * driver wanted to the wiphy to deal with conflicts */ | ||
1481 | |||
1482 | BUG_ON(last_request->wiphy->regd); | ||
1483 | |||
1484 | r = reg_copy_regd(&last_request->wiphy->regd, rd); | ||
1485 | if (r) | ||
1486 | return r; | ||
1487 | |||
1352 | reset_regdomains(); | 1488 | reset_regdomains(); |
1353 | cfg80211_regdomain = rd; | 1489 | cfg80211_regdomain = rd; |
1354 | return 0; | 1490 | return 0; |
@@ -1362,8 +1498,14 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) | |||
1362 | if (!intersected_rd) | 1498 | if (!intersected_rd) |
1363 | return -EINVAL; | 1499 | return -EINVAL; |
1364 | 1500 | ||
1365 | /* We can trash what CRDA provided now */ | 1501 | /* We can trash what CRDA provided now. |
1366 | kfree(rd); | 1502 | * However if a driver requested this specific regulatory |
1503 | * domain we keep it for its private use */ | ||
1504 | if (last_request->initiator == REGDOM_SET_BY_DRIVER) | ||
1505 | last_request->wiphy->regd = rd; | ||
1506 | else | ||
1507 | kfree(rd); | ||
1508 | |||
1367 | rd = NULL; | 1509 | rd = NULL; |
1368 | 1510 | ||
1369 | reset_regdomains(); | 1511 | reset_regdomains(); |
@@ -1447,6 +1589,7 @@ int set_regdom(const struct ieee80211_regdomain *rd) | |||
1447 | /* Caller must hold cfg80211_drv_mutex */ | 1589 | /* Caller must hold cfg80211_drv_mutex */ |
1448 | void reg_device_remove(struct wiphy *wiphy) | 1590 | void reg_device_remove(struct wiphy *wiphy) |
1449 | { | 1591 | { |
1592 | kfree(wiphy->regd); | ||
1450 | if (!last_request || !last_request->wiphy) | 1593 | if (!last_request || !last_request->wiphy) |
1451 | return; | 1594 | return; |
1452 | if (last_request->wiphy != wiphy) | 1595 | if (last_request->wiphy != wiphy) |
diff --git a/net/wireless/reg.h b/net/wireless/reg.h index a76ea3ff7cd6..eb1dd5bc9b27 100644 --- a/net/wireless/reg.h +++ b/net/wireless/reg.h | |||
@@ -11,13 +11,6 @@ void regulatory_exit(void); | |||
11 | 11 | ||
12 | int set_regdom(const struct ieee80211_regdomain *rd); | 12 | int set_regdom(const struct ieee80211_regdomain *rd); |
13 | 13 | ||
14 | enum environment_cap { | ||
15 | ENVIRON_ANY, | ||
16 | ENVIRON_INDOOR, | ||
17 | ENVIRON_OUTDOOR, | ||
18 | }; | ||
19 | |||
20 | |||
21 | /** | 14 | /** |
22 | * __regulatory_hint - hint to the wireless core a regulatory domain | 15 | * __regulatory_hint - hint to the wireless core a regulatory domain |
23 | * @wiphy: if the hint comes from country information from an AP, this | 16 | * @wiphy: if the hint comes from country information from an AP, this |
diff --git a/net/wireless/sysfs.c b/net/wireless/sysfs.c index 79a382877641..26a72b0797a0 100644 --- a/net/wireless/sysfs.c +++ b/net/wireless/sysfs.c | |||
@@ -55,6 +55,34 @@ static int wiphy_uevent(struct device *dev, struct kobj_uevent_env *env) | |||
55 | } | 55 | } |
56 | #endif | 56 | #endif |
57 | 57 | ||
58 | static int wiphy_suspend(struct device *dev, pm_message_t state) | ||
59 | { | ||
60 | struct cfg80211_registered_device *rdev = dev_to_rdev(dev); | ||
61 | int ret = 0; | ||
62 | |||
63 | if (rdev->ops->suspend) { | ||
64 | rtnl_lock(); | ||
65 | ret = rdev->ops->suspend(&rdev->wiphy); | ||
66 | rtnl_unlock(); | ||
67 | } | ||
68 | |||
69 | return ret; | ||
70 | } | ||
71 | |||
72 | static int wiphy_resume(struct device *dev) | ||
73 | { | ||
74 | struct cfg80211_registered_device *rdev = dev_to_rdev(dev); | ||
75 | int ret = 0; | ||
76 | |||
77 | if (rdev->ops->resume) { | ||
78 | rtnl_lock(); | ||
79 | ret = rdev->ops->resume(&rdev->wiphy); | ||
80 | rtnl_unlock(); | ||
81 | } | ||
82 | |||
83 | return ret; | ||
84 | } | ||
85 | |||
58 | struct class ieee80211_class = { | 86 | struct class ieee80211_class = { |
59 | .name = "ieee80211", | 87 | .name = "ieee80211", |
60 | .owner = THIS_MODULE, | 88 | .owner = THIS_MODULE, |
@@ -63,6 +91,8 @@ struct class ieee80211_class = { | |||
63 | #ifdef CONFIG_HOTPLUG | 91 | #ifdef CONFIG_HOTPLUG |
64 | .dev_uevent = wiphy_uevent, | 92 | .dev_uevent = wiphy_uevent, |
65 | #endif | 93 | #endif |
94 | .suspend = wiphy_suspend, | ||
95 | .resume = wiphy_resume, | ||
66 | }; | 96 | }; |
67 | 97 | ||
68 | int wiphy_sysfs_init(void) | 98 | int wiphy_sysfs_init(void) |
diff --git a/net/wireless/util.c b/net/wireless/util.c index e76cc28b0345..487cdd9bcffc 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -9,7 +9,7 @@ | |||
9 | 9 | ||
10 | struct ieee80211_rate * | 10 | struct ieee80211_rate * |
11 | ieee80211_get_response_rate(struct ieee80211_supported_band *sband, | 11 | ieee80211_get_response_rate(struct ieee80211_supported_band *sband, |
12 | u64 basic_rates, int bitrate) | 12 | u32 basic_rates, int bitrate) |
13 | { | 13 | { |
14 | struct ieee80211_rate *result = &sband->bitrates[0]; | 14 | struct ieee80211_rate *result = &sband->bitrates[0]; |
15 | int i; | 15 | int i; |