diff options
-rw-r--r-- | drivers/net/wireless/libertas/cfg.c | 110 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cfg.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmd.c | 65 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/cmdresp.c | 50 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/decl.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/dev.h | 3 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/host.h | 28 | ||||
-rw-r--r-- | drivers/net/wireless/libertas/main.c | 3 |
8 files changed, 268 insertions, 1 deletions
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 089f0722fa20..f36cc970ad1b 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c | |||
@@ -8,6 +8,7 @@ | |||
8 | 8 | ||
9 | #include <linux/slab.h> | 9 | #include <linux/slab.h> |
10 | #include <linux/if_arp.h> | 10 | #include <linux/if_arp.h> |
11 | #include <linux/ieee80211.h> | ||
11 | #include <net/cfg80211.h> | 12 | #include <net/cfg80211.h> |
12 | #include <asm/unaligned.h> | 13 | #include <asm/unaligned.h> |
13 | 14 | ||
@@ -2042,6 +2043,7 @@ int lbs_cfg_register(struct lbs_private *priv) | |||
2042 | */ | 2043 | */ |
2043 | wdev->wiphy->cipher_suites = cipher_suites; | 2044 | wdev->wiphy->cipher_suites = cipher_suites; |
2044 | wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); | 2045 | wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); |
2046 | wdev->wiphy->reg_notifier = lbs_reg_notifier; | ||
2045 | 2047 | ||
2046 | ret = wiphy_register(wdev->wiphy); | 2048 | ret = wiphy_register(wdev->wiphy); |
2047 | if (ret < 0) | 2049 | if (ret < 0) |
@@ -2061,6 +2063,114 @@ int lbs_cfg_register(struct lbs_private *priv) | |||
2061 | return ret; | 2063 | return ret; |
2062 | } | 2064 | } |
2063 | 2065 | ||
2066 | /** | ||
2067 | * @brief This function sets DOMAIN INFO to FW | ||
2068 | * @param priv pointer to struct lbs_private | ||
2069 | * @return 0; -1 | ||
2070 | */ | ||
2071 | static int lbs_11d_set_domain_info(struct lbs_private *priv) | ||
2072 | { | ||
2073 | int ret; | ||
2074 | |||
2075 | ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO, | ||
2076 | CMD_ACT_SET, | ||
2077 | CMD_OPTION_WAITFORRSP, 0, NULL); | ||
2078 | if (ret) | ||
2079 | lbs_deb_11d("fail to dnld domain info\n"); | ||
2080 | |||
2081 | return ret; | ||
2082 | } | ||
2083 | |||
2084 | static void lbs_send_domain_info_cmd_fw(struct wiphy *wiphy, | ||
2085 | struct regulatory_request *request) | ||
2086 | { | ||
2087 | u8 no_of_triplet = 0; | ||
2088 | u8 no_of_parsed_chan = 0; | ||
2089 | u8 first_channel = 0, next_chan = 0, max_pwr = 0; | ||
2090 | u8 i, flag = 0; | ||
2091 | enum ieee80211_band band; | ||
2092 | struct ieee80211_supported_band *sband; | ||
2093 | struct ieee80211_channel *ch; | ||
2094 | struct lbs_private *priv = wiphy_priv(wiphy); | ||
2095 | struct lbs_802_11d_domain_reg *domain_info = &priv->domain_reg; | ||
2096 | int ret = 0; | ||
2097 | |||
2098 | lbs_deb_enter(LBS_DEB_CFG80211); | ||
2099 | |||
2100 | /* Set country code */ | ||
2101 | domain_info->country_code[0] = request->alpha2[0]; | ||
2102 | domain_info->country_code[1] = request->alpha2[1]; | ||
2103 | domain_info->country_code[2] = ' '; | ||
2104 | |||
2105 | for (band = 0; band < IEEE80211_NUM_BANDS ; band++) { | ||
2106 | |||
2107 | if (!wiphy->bands[band]) | ||
2108 | continue; | ||
2109 | |||
2110 | sband = wiphy->bands[band]; | ||
2111 | |||
2112 | for (i = 0; i < sband->n_channels ; i++) { | ||
2113 | ch = &sband->channels[i]; | ||
2114 | if (ch->flags & IEEE80211_CHAN_DISABLED) | ||
2115 | continue; | ||
2116 | |||
2117 | if (!flag) { | ||
2118 | flag = 1; | ||
2119 | next_chan = first_channel = (u32) ch->hw_value; | ||
2120 | max_pwr = ch->max_power; | ||
2121 | no_of_parsed_chan = 1; | ||
2122 | continue; | ||
2123 | } | ||
2124 | |||
2125 | if (ch->hw_value == next_chan + 1 && | ||
2126 | ch->max_power == max_pwr) { | ||
2127 | next_chan++; | ||
2128 | no_of_parsed_chan++; | ||
2129 | } else { | ||
2130 | domain_info->triplet[no_of_triplet] | ||
2131 | .chans.first_channel = first_channel; | ||
2132 | domain_info->triplet[no_of_triplet] | ||
2133 | .chans.num_channels = no_of_parsed_chan; | ||
2134 | domain_info->triplet[no_of_triplet] | ||
2135 | .chans.max_power = max_pwr; | ||
2136 | no_of_triplet++; | ||
2137 | flag = 0; | ||
2138 | } | ||
2139 | } | ||
2140 | if (flag) { | ||
2141 | domain_info->triplet[no_of_triplet] | ||
2142 | .chans.first_channel = first_channel; | ||
2143 | domain_info->triplet[no_of_triplet] | ||
2144 | .chans.num_channels = no_of_parsed_chan; | ||
2145 | domain_info->triplet[no_of_triplet] | ||
2146 | .chans.max_power = max_pwr; | ||
2147 | no_of_triplet++; | ||
2148 | } | ||
2149 | } | ||
2150 | |||
2151 | domain_info->no_triplet = no_of_triplet; | ||
2152 | |||
2153 | /* Set domain info */ | ||
2154 | ret = lbs_11d_set_domain_info(priv); | ||
2155 | if (ret) | ||
2156 | lbs_pr_err("11D: error setting domain info in FW\n"); | ||
2157 | |||
2158 | lbs_deb_leave(LBS_DEB_CFG80211); | ||
2159 | } | ||
2160 | |||
2161 | int lbs_reg_notifier(struct wiphy *wiphy, | ||
2162 | struct regulatory_request *request) | ||
2163 | { | ||
2164 | lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain " | ||
2165 | "callback for domain %c%c\n", request->alpha2[0], | ||
2166 | request->alpha2[1]); | ||
2167 | |||
2168 | lbs_send_domain_info_cmd_fw(wiphy, request); | ||
2169 | |||
2170 | lbs_deb_leave(LBS_DEB_CFG80211); | ||
2171 | |||
2172 | return 0; | ||
2173 | } | ||
2064 | 2174 | ||
2065 | void lbs_scan_deinit(struct lbs_private *priv) | 2175 | void lbs_scan_deinit(struct lbs_private *priv) |
2066 | { | 2176 | { |
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h index eae3fd911abb..756fb98f9f05 100644 --- a/drivers/net/wireless/libertas/cfg.h +++ b/drivers/net/wireless/libertas/cfg.h | |||
@@ -3,11 +3,16 @@ | |||
3 | 3 | ||
4 | struct device; | 4 | struct device; |
5 | struct lbs_private; | 5 | struct lbs_private; |
6 | struct regulatory_request; | ||
7 | struct wiphy; | ||
6 | 8 | ||
7 | struct wireless_dev *lbs_cfg_alloc(struct device *dev); | 9 | struct wireless_dev *lbs_cfg_alloc(struct device *dev); |
8 | int lbs_cfg_register(struct lbs_private *priv); | 10 | int lbs_cfg_register(struct lbs_private *priv); |
9 | void lbs_cfg_free(struct lbs_private *priv); | 11 | void lbs_cfg_free(struct lbs_private *priv); |
10 | 12 | ||
13 | int lbs_reg_notifier(struct wiphy *wiphy, | ||
14 | struct regulatory_request *request); | ||
15 | |||
11 | /* All of those are TODOs: */ | 16 | /* All of those are TODOs: */ |
12 | #define lbs_cmd_802_11_rssi(priv, cmdptr) (0) | 17 | #define lbs_cmd_802_11_rssi(priv, cmdptr) (0) |
13 | #define lbs_ret_802_11_rssi(priv, resp) (0) | 18 | #define lbs_ret_802_11_rssi(priv, resp) (0) |
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index d8838461e596..6c8a9d952a01 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c | |||
@@ -899,6 +899,66 @@ void lbs_set_mac_control(struct lbs_private *priv) | |||
899 | } | 899 | } |
900 | 900 | ||
901 | /** | 901 | /** |
902 | * @brief This function implements command CMD_802_11D_DOMAIN_INFO | ||
903 | * @param priv pointer to struct lbs_private | ||
904 | * @param cmd pointer to cmd buffer | ||
905 | * @param cmdno cmd ID | ||
906 | * @param cmdOption cmd action | ||
907 | * @return 0 | ||
908 | */ | ||
909 | int lbs_cmd_802_11d_domain_info(struct lbs_private *priv, | ||
910 | struct cmd_ds_command *cmd, | ||
911 | u16 cmdoption) | ||
912 | { | ||
913 | struct cmd_ds_802_11d_domain_info *pdomaininfo = | ||
914 | &cmd->params.domaininfo; | ||
915 | struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain; | ||
916 | u8 nr_triplet = priv->domain_reg.no_triplet; | ||
917 | |||
918 | lbs_deb_enter(LBS_DEB_11D); | ||
919 | |||
920 | lbs_deb_11d("nr_triplet=%x\n", nr_triplet); | ||
921 | |||
922 | pdomaininfo->action = cpu_to_le16(cmdoption); | ||
923 | if (cmdoption == CMD_ACT_GET) { | ||
924 | cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + | ||
925 | sizeof(struct cmd_header)); | ||
926 | lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, | ||
927 | le16_to_cpu(cmd->size)); | ||
928 | goto done; | ||
929 | } | ||
930 | |||
931 | domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); | ||
932 | memcpy(domain->countrycode, priv->domain_reg.country_code, | ||
933 | sizeof(domain->countrycode)); | ||
934 | |||
935 | domain->header.len = cpu_to_le16(nr_triplet | ||
936 | * sizeof(struct ieee80211_country_ie_triplet) | ||
937 | + sizeof(domain->countrycode)); | ||
938 | |||
939 | if (nr_triplet) { | ||
940 | memcpy(domain->triplet, priv->domain_reg.triplet, | ||
941 | nr_triplet * | ||
942 | sizeof(struct ieee80211_country_ie_triplet)); | ||
943 | |||
944 | cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + | ||
945 | le16_to_cpu(domain->header.len) + | ||
946 | sizeof(struct mrvl_ie_header) + | ||
947 | sizeof(struct cmd_header)); | ||
948 | } else { | ||
949 | cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + | ||
950 | sizeof(struct cmd_header)); | ||
951 | } | ||
952 | |||
953 | lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, | ||
954 | le16_to_cpu(cmd->size)); | ||
955 | |||
956 | done: | ||
957 | lbs_deb_enter(LBS_DEB_11D); | ||
958 | return 0; | ||
959 | } | ||
960 | |||
961 | /** | ||
902 | * @brief This function prepare the command before send to firmware. | 962 | * @brief This function prepare the command before send to firmware. |
903 | * | 963 | * |
904 | * @param priv A pointer to struct lbs_private structure | 964 | * @param priv A pointer to struct lbs_private structure |
@@ -996,6 +1056,11 @@ int lbs_prepare_and_send_command(struct lbs_private *priv, | |||
996 | ret = 0; | 1056 | ret = 0; |
997 | goto done; | 1057 | goto done; |
998 | 1058 | ||
1059 | case CMD_802_11D_DOMAIN_INFO: | ||
1060 | cmdptr->command = cpu_to_le16(cmd_no); | ||
1061 | ret = lbs_cmd_802_11d_domain_info(priv, cmdptr, cmd_action); | ||
1062 | break; | ||
1063 | |||
999 | case CMD_802_11_TPC_CFG: | 1064 | case CMD_802_11_TPC_CFG: |
1000 | cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG); | 1065 | cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG); |
1001 | cmdptr->size = | 1066 | cmdptr->size = |
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c index 52b543c52a93..b4bc103d7970 100644 --- a/drivers/net/wireless/libertas/cmdresp.c +++ b/drivers/net/wireless/libertas/cmdresp.c | |||
@@ -97,6 +97,52 @@ static int lbs_ret_reg_access(struct lbs_private *priv, | |||
97 | return ret; | 97 | return ret; |
98 | } | 98 | } |
99 | 99 | ||
100 | /** | ||
101 | * @brief This function parses countryinfo from AP and download country info to FW | ||
102 | * @param priv pointer to struct lbs_private | ||
103 | * @param resp pointer to command response buffer | ||
104 | * @return 0; -1 | ||
105 | */ | ||
106 | int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp) | ||
107 | { | ||
108 | struct cmd_ds_802_11d_domain_info *domaininfo = | ||
109 | &resp->params.domaininforesp; | ||
110 | struct mrvl_ie_domain_param_set *domain = &domaininfo->domain; | ||
111 | u16 action = le16_to_cpu(domaininfo->action); | ||
112 | s16 ret = 0; | ||
113 | u8 nr_triplet = 0; | ||
114 | |||
115 | lbs_deb_enter(LBS_DEB_11D); | ||
116 | |||
117 | lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp, | ||
118 | (int)le16_to_cpu(resp->size)); | ||
119 | |||
120 | nr_triplet = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) / | ||
121 | sizeof(struct ieee80211_country_ie_triplet); | ||
122 | |||
123 | lbs_deb_11d("domain info resp: nr_triplet %d\n", nr_triplet); | ||
124 | |||
125 | if (nr_triplet > MRVDRV_MAX_TRIPLET_802_11D) { | ||
126 | lbs_deb_11d("invalid number of triplets returned!!\n"); | ||
127 | return -1; | ||
128 | } | ||
129 | |||
130 | switch (action) { | ||
131 | case CMD_ACT_SET: /*Proc set action */ | ||
132 | break; | ||
133 | |||
134 | case CMD_ACT_GET: | ||
135 | break; | ||
136 | default: | ||
137 | lbs_deb_11d("invalid action:%d\n", domaininfo->action); | ||
138 | ret = -1; | ||
139 | break; | ||
140 | } | ||
141 | |||
142 | lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret); | ||
143 | return ret; | ||
144 | } | ||
145 | |||
100 | static inline int handle_cmd_response(struct lbs_private *priv, | 146 | static inline int handle_cmd_response(struct lbs_private *priv, |
101 | struct cmd_header *cmd_response) | 147 | struct cmd_header *cmd_response) |
102 | { | 148 | { |
@@ -130,6 +176,10 @@ static inline int handle_cmd_response(struct lbs_private *priv, | |||
130 | ret = lbs_ret_802_11_rssi(priv, resp); | 176 | ret = lbs_ret_802_11_rssi(priv, resp); |
131 | break; | 177 | break; |
132 | 178 | ||
179 | case CMD_RET(CMD_802_11D_DOMAIN_INFO): | ||
180 | ret = lbs_ret_802_11d_domain_info(resp); | ||
181 | break; | ||
182 | |||
133 | case CMD_RET(CMD_802_11_TPC_CFG): | 183 | case CMD_RET(CMD_802_11_TPC_CFG): |
134 | spin_lock_irqsave(&priv->driver_lock, flags); | 184 | spin_lock_irqsave(&priv->driver_lock, flags); |
135 | memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg, | 185 | memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg, |
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h index 85c97d3ed88d..ba5438a7ba17 100644 --- a/drivers/net/wireless/libertas/decl.h +++ b/drivers/net/wireless/libertas/decl.h | |||
@@ -13,6 +13,7 @@ | |||
13 | struct lbs_private; | 13 | struct lbs_private; |
14 | struct sk_buff; | 14 | struct sk_buff; |
15 | struct net_device; | 15 | struct net_device; |
16 | struct cmd_ds_command; | ||
16 | 17 | ||
17 | 18 | ||
18 | /* ethtool.c */ | 19 | /* ethtool.c */ |
@@ -52,5 +53,9 @@ int lbs_exit_auto_deep_sleep(struct lbs_private *priv); | |||
52 | u32 lbs_fw_index_to_data_rate(u8 index); | 53 | u32 lbs_fw_index_to_data_rate(u8 index); |
53 | u8 lbs_data_rate_to_fw_index(u32 rate); | 54 | u8 lbs_data_rate_to_fw_index(u32 rate); |
54 | 55 | ||
56 | int lbs_cmd_802_11d_domain_info(struct lbs_private *priv, | ||
57 | struct cmd_ds_command *cmd, u16 cmdoption); | ||
58 | |||
59 | int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp); | ||
55 | 60 | ||
56 | #endif | 61 | #endif |
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h index be263acf19c4..4536d9c0ad87 100644 --- a/drivers/net/wireless/libertas/dev.h +++ b/drivers/net/wireless/libertas/dev.h | |||
@@ -60,6 +60,9 @@ struct lbs_private { | |||
60 | struct dentry *regs_dir; | 60 | struct dentry *regs_dir; |
61 | struct dentry *debugfs_regs_files[6]; | 61 | struct dentry *debugfs_regs_files[6]; |
62 | 62 | ||
63 | /** 11D and domain regulatory data */ | ||
64 | struct lbs_802_11d_domain_reg domain_reg; | ||
65 | |||
63 | /* Hardware debugging */ | 66 | /* Hardware debugging */ |
64 | u32 mac_offset; | 67 | u32 mac_offset; |
65 | u32 bbp_offset; | 68 | u32 bbp_offset; |
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h index 3809c0b49464..112fbf167dc8 100644 --- a/drivers/net/wireless/libertas/host.h +++ b/drivers/net/wireless/libertas/host.h | |||
@@ -389,6 +389,30 @@ struct lbs_offset_value { | |||
389 | u32 value; | 389 | u32 value; |
390 | } __attribute__ ((packed)); | 390 | } __attribute__ ((packed)); |
391 | 391 | ||
392 | #define MRVDRV_MAX_TRIPLET_802_11D 83 | ||
393 | |||
394 | #define COUNTRY_CODE_LEN 3 | ||
395 | |||
396 | struct mrvl_ie_domain_param_set { | ||
397 | struct mrvl_ie_header header; | ||
398 | |||
399 | u8 countrycode[COUNTRY_CODE_LEN]; | ||
400 | struct ieee80211_country_ie_triplet triplet[1]; | ||
401 | } __attribute__ ((packed)); | ||
402 | |||
403 | struct cmd_ds_802_11d_domain_info { | ||
404 | __le16 action; | ||
405 | struct mrvl_ie_domain_param_set domain; | ||
406 | } __attribute__ ((packed)); | ||
407 | |||
408 | struct lbs_802_11d_domain_reg { | ||
409 | /** Country code*/ | ||
410 | u8 country_code[COUNTRY_CODE_LEN]; | ||
411 | /** No. of triplet*/ | ||
412 | u8 no_triplet; | ||
413 | struct ieee80211_country_ie_triplet triplet[MRVDRV_MAX_TRIPLET_802_11D]; | ||
414 | } __attribute__ ((packed)); | ||
415 | |||
392 | /* | 416 | /* |
393 | * Define data structure for CMD_GET_HW_SPEC | 417 | * Define data structure for CMD_GET_HW_SPEC |
394 | * This structure defines the response for the GET_HW_SPEC command | 418 | * This structure defines the response for the GET_HW_SPEC command |
@@ -949,6 +973,9 @@ struct cmd_ds_command { | |||
949 | struct cmd_ds_bbp_reg_access bbpreg; | 973 | struct cmd_ds_bbp_reg_access bbpreg; |
950 | struct cmd_ds_rf_reg_access rfreg; | 974 | struct cmd_ds_rf_reg_access rfreg; |
951 | 975 | ||
976 | struct cmd_ds_802_11d_domain_info domaininfo; | ||
977 | struct cmd_ds_802_11d_domain_info domaininforesp; | ||
978 | |||
952 | struct cmd_ds_802_11_tpc_cfg tpccfg; | 979 | struct cmd_ds_802_11_tpc_cfg tpccfg; |
953 | struct cmd_ds_802_11_afc afc; | 980 | struct cmd_ds_802_11_afc afc; |
954 | struct cmd_ds_802_11_led_ctrl ledgpio; | 981 | struct cmd_ds_802_11_led_ctrl ledgpio; |
@@ -958,5 +985,4 @@ struct cmd_ds_command { | |||
958 | struct cmd_ds_802_11_beacon_control bcn_ctrl; | 985 | struct cmd_ds_802_11_beacon_control bcn_ctrl; |
959 | } params; | 986 | } params; |
960 | } __attribute__ ((packed)); | 987 | } __attribute__ ((packed)); |
961 | |||
962 | #endif | 988 | #endif |
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c index 58b031c52410..b519fc70f04f 100644 --- a/drivers/net/wireless/libertas/main.c +++ b/drivers/net/wireless/libertas/main.c | |||
@@ -652,6 +652,9 @@ static int lbs_setup_firmware(struct lbs_private *priv) | |||
652 | priv->txpower_max = maxlevel; | 652 | priv->txpower_max = maxlevel; |
653 | } | 653 | } |
654 | 654 | ||
655 | /* Send cmd to FW to enable 11D function */ | ||
656 | ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1); | ||
657 | |||
655 | lbs_set_mac_control(priv); | 658 | lbs_set_mac_control(priv); |
656 | done: | 659 | done: |
657 | lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); | 660 | lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret); |