return ret;
}
-/**
- * @brief This function sets DOMAIN INFO to FW
- * @param priv pointer to struct lbs_private
- * @return 0; -1
-*/
-static int lbs_11d_set_domain_info(struct lbs_private *priv)
-{
- int ret;
-
- ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO,
- CMD_ACT_SET,
- CMD_OPTION_WAITFORRSP, 0, NULL);
- if (ret)
- lbs_deb_11d("fail to dnld domain info\n");
-
- return ret;
-}
-
-static void lbs_send_domain_info_cmd_fw(struct wiphy *wiphy,
- struct regulatory_request *request)
-{
- u8 no_of_triplet = 0;
- u8 no_of_parsed_chan = 0;
- u8 first_channel = 0, next_chan = 0, max_pwr = 0;
- u8 i, flag = 0;
- enum ieee80211_band band;
- struct ieee80211_supported_band *sband;
- struct ieee80211_channel *ch;
- struct lbs_private *priv = wiphy_priv(wiphy);
- struct lbs_802_11d_domain_reg *domain_info = &priv->domain_reg;
- int ret = 0;
-
- lbs_deb_enter(LBS_DEB_CFG80211);
-
- /* Set country code */
- domain_info->country_code[0] = request->alpha2[0];
- domain_info->country_code[1] = request->alpha2[1];
- domain_info->country_code[2] = ' ';
-
- for (band = 0; band < IEEE80211_NUM_BANDS ; band++) {
-
- if (!wiphy->bands[band])
- continue;
-
- sband = wiphy->bands[band];
-
- for (i = 0; i < sband->n_channels ; i++) {
- ch = &sband->channels[i];
- if (ch->flags & IEEE80211_CHAN_DISABLED)
- continue;
-
- if (!flag) {
- flag = 1;
- next_chan = first_channel = (u32) ch->hw_value;
- max_pwr = ch->max_power;
- no_of_parsed_chan = 1;
- continue;
- }
-
- if (ch->hw_value == next_chan + 1 &&
- ch->max_power == max_pwr) {
- next_chan++;
- no_of_parsed_chan++;
- } else {
- domain_info->triplet[no_of_triplet]
- .chans.first_channel = first_channel;
- domain_info->triplet[no_of_triplet]
- .chans.num_channels = no_of_parsed_chan;
- domain_info->triplet[no_of_triplet]
- .chans.max_power = max_pwr;
- no_of_triplet++;
- flag = 0;
- }
- }
- if (flag) {
- domain_info->triplet[no_of_triplet]
- .chans.first_channel = first_channel;
- domain_info->triplet[no_of_triplet]
- .chans.num_channels = no_of_parsed_chan;
- domain_info->triplet[no_of_triplet]
- .chans.max_power = max_pwr;
- no_of_triplet++;
- }
- }
-
- domain_info->no_triplet = no_of_triplet;
-
- /* Set domain info */
- ret = lbs_11d_set_domain_info(priv);
- if (ret)
- lbs_pr_err("11D: error setting domain info in FW\n");
-
- lbs_deb_leave(LBS_DEB_CFG80211);
-}
-
int lbs_reg_notifier(struct wiphy *wiphy,
struct regulatory_request *request)
{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret;
+
lbs_deb_enter_args(LBS_DEB_CFG80211, "cfg80211 regulatory domain "
"callback for domain %c%c\n", request->alpha2[0],
request->alpha2[1]);
- lbs_send_domain_info_cmd_fw(wiphy, request);
+ ret = lbs_set_11d_domain_info(priv, request, wiphy->bands);
lbs_deb_leave(LBS_DEB_CFG80211);
-
- return 0;
+ return ret;
}
void lbs_scan_deinit(struct lbs_private *priv)
return ret;
}
+/**
+ * @brief Send regulatory and 802.11d domain information to the firmware
+ *
+ * @param priv pointer to struct lbs_private
+ * @param request cfg80211 regulatory request structure
+ * @param bands the device's supported bands and channels
+ *
+ * @return 0 on success, error code on failure
+*/
+int lbs_set_11d_domain_info(struct lbs_private *priv,
+ struct regulatory_request *request,
+ struct ieee80211_supported_band **bands)
+{
+ struct cmd_ds_802_11d_domain_info cmd;
+ struct mrvl_ie_domain_param_set *domain = &cmd.domain;
+ struct ieee80211_country_ie_triplet *t;
+ enum ieee80211_band band;
+ struct ieee80211_channel *ch;
+ u8 num_triplet = 0;
+ u8 num_parsed_chan = 0;
+ u8 first_channel = 0, next_chan = 0, max_pwr = 0;
+ u8 i, flag = 0;
+ size_t triplet_size;
+ int ret;
+
+ lbs_deb_enter(LBS_DEB_11D);
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.action = cpu_to_le16(CMD_ACT_SET);
+
+ lbs_deb_11d("Setting country code '%c%c'\n",
+ request->alpha2[0], request->alpha2[1]);
+
+ domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
+
+ /* Set country code */
+ domain->country_code[0] = request->alpha2[0];
+ domain->country_code[1] = request->alpha2[1];
+ domain->country_code[2] = ' ';
+
+ /* Now set up the channel triplets; firmware is somewhat picky here
+ * and doesn't validate channel numbers and spans; hence it would
+ * interpret a triplet of (36, 4, 20) as channels 36, 37, 38, 39. Since
+ * the last 3 aren't valid channels, the driver is responsible for
+ * splitting that up into 4 triplet pairs of (36, 1, 20) + (40, 1, 20)
+ * etc.
+ */
+ for (band = 0;
+ (band < IEEE80211_NUM_BANDS) && (num_triplet < MAX_11D_TRIPLETS);
+ band++) {
+
+ if (!bands[band])
+ continue;
+
+ for (i = 0;
+ (i < bands[band]->n_channels) && (num_triplet < MAX_11D_TRIPLETS);
+ i++) {
+ ch = &bands[band]->channels[i];
+ if (ch->flags & IEEE80211_CHAN_DISABLED)
+ continue;
+
+ if (!flag) {
+ flag = 1;
+ next_chan = first_channel = (u32) ch->hw_value;
+ max_pwr = ch->max_power;
+ num_parsed_chan = 1;
+ continue;
+ }
+
+ if ((ch->hw_value == next_chan + 1) &&
+ (ch->max_power == max_pwr)) {
+ /* Consolidate adjacent channels */
+ next_chan++;
+ num_parsed_chan++;
+ } else {
+ /* Add this triplet */
+ lbs_deb_11d("11D triplet (%d, %d, %d)\n",
+ first_channel, num_parsed_chan,
+ max_pwr);
+ t = &domain->triplet[num_triplet];
+ t->chans.first_channel = first_channel;
+ t->chans.num_channels = num_parsed_chan;
+ t->chans.max_power = max_pwr;
+ num_triplet++;
+ flag = 0;
+ }
+ }
+
+ if (flag) {
+ /* Add last triplet */
+ lbs_deb_11d("11D triplet (%d, %d, %d)\n", first_channel,
+ num_parsed_chan, max_pwr);
+ t = &domain->triplet[num_triplet];
+ t->chans.first_channel = first_channel;
+ t->chans.num_channels = num_parsed_chan;
+ t->chans.max_power = max_pwr;
+ num_triplet++;
+ }
+ }
+
+ lbs_deb_11d("# triplets %d\n", num_triplet);
+
+ /* Set command header sizes */
+ triplet_size = num_triplet * sizeof(struct ieee80211_country_ie_triplet);
+ domain->header.len = cpu_to_le16(sizeof(domain->country_code) +
+ triplet_size);
+
+ lbs_deb_hex(LBS_DEB_11D, "802.11D domain param set",
+ (u8 *) &cmd.domain.country_code,
+ le16_to_cpu(domain->header.len));
+
+ cmd.hdr.size = cpu_to_le16(sizeof(cmd.hdr) +
+ sizeof(cmd.action) +
+ sizeof(cmd.domain.header) +
+ sizeof(cmd.domain.country_code) +
+ triplet_size);
+
+ ret = lbs_cmd_with_response(priv, CMD_802_11D_DOMAIN_INFO, &cmd);
+
+ lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
+ return ret;
+}
+
static int lbs_cmd_reg_access(struct cmd_ds_command *cmdptr,
u8 cmd_action, void *pdata_buf)
{
lbs_deb_leave(LBS_DEB_CMD);
}
-/**
- * @brief This function implements command CMD_802_11D_DOMAIN_INFO
- * @param priv pointer to struct lbs_private
- * @param cmd pointer to cmd buffer
- * @param cmdno cmd ID
- * @param cmdOption cmd action
- * @return 0
-*/
-int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
- struct cmd_ds_command *cmd,
- u16 cmdoption)
-{
- struct cmd_ds_802_11d_domain_info *pdomaininfo =
- &cmd->params.domaininfo;
- struct mrvl_ie_domain_param_set *domain = &pdomaininfo->domain;
- u8 nr_triplet = priv->domain_reg.no_triplet;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- lbs_deb_11d("nr_triplet=%x\n", nr_triplet);
-
- pdomaininfo->action = cpu_to_le16(cmdoption);
- if (cmdoption == CMD_ACT_GET) {
- cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
- sizeof(struct cmd_header));
- lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
- le16_to_cpu(cmd->size));
- goto done;
- }
-
- domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
- memcpy(domain->countrycode, priv->domain_reg.country_code,
- sizeof(domain->countrycode));
-
- domain->header.len = cpu_to_le16(nr_triplet
- * sizeof(struct ieee80211_country_ie_triplet)
- + sizeof(domain->countrycode));
-
- if (nr_triplet) {
- memcpy(domain->triplet, priv->domain_reg.triplet,
- nr_triplet *
- sizeof(struct ieee80211_country_ie_triplet));
-
- cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
- le16_to_cpu(domain->header.len) +
- sizeof(struct mrvl_ie_header) +
- sizeof(struct cmd_header));
- } else {
- cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
- sizeof(struct cmd_header));
- }
-
- lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
- le16_to_cpu(cmd->size));
-
-done:
- lbs_deb_enter(LBS_DEB_11D);
- return 0;
-}
-
/**
* @brief This function prepare the command before send to firmware.
*
ret = 0;
goto done;
- case CMD_802_11D_DOMAIN_INFO:
- cmdptr->command = cpu_to_le16(cmd_no);
- ret = lbs_cmd_802_11d_domain_info(priv, cmdptr, cmd_action);
- break;
-
case CMD_802_11_TPC_CFG:
cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG);
cmdptr->size =
lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
return ret;
}
-
-/**
- * @brief This function parses countryinfo from AP and download country info to FW
- * @param priv pointer to struct lbs_private
- * @param resp pointer to command response buffer
- * @return 0; -1
- */
-static int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp)
-{
- struct cmd_ds_802_11d_domain_info *domaininfo =
- &resp->params.domaininforesp;
- struct mrvl_ie_domain_param_set *domain = &domaininfo->domain;
- u16 action = le16_to_cpu(domaininfo->action);
- s16 ret = 0;
- u8 nr_triplet = 0;
-
- lbs_deb_enter(LBS_DEB_11D);
-
- lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp,
- (int)le16_to_cpu(resp->size));
-
- nr_triplet = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) /
- sizeof(struct ieee80211_country_ie_triplet);
-
- lbs_deb_11d("domain info resp: nr_triplet %d\n", nr_triplet);
-
- if (nr_triplet > MRVDRV_MAX_TRIPLET_802_11D) {
- lbs_deb_11d("invalid number of triplets returned!!\n");
- return -1;
- }
-
- switch (action) {
- case CMD_ACT_SET: /*Proc set action */
- break;
-
- case CMD_ACT_GET:
- break;
- default:
- lbs_deb_11d("invalid action:%d\n", domaininfo->action);
- ret = -1;
- break;
- }
-
- lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
- return ret;
-}
-
static inline int handle_cmd_response(struct lbs_private *priv,
struct cmd_header *cmd_response)
{
case CMD_RET(CMD_802_11_BEACON_STOP):
break;
- case CMD_RET(CMD_802_11D_DOMAIN_INFO):
- ret = lbs_ret_802_11d_domain_info(resp);
- break;
-
case CMD_RET(CMD_802_11_TPC_CFG):
spin_lock_irqsave(&priv->driver_lock, flags);
memmove((void *)priv->cur_cmd->callback_arg, &resp->params.tpccfg,