[RFC] Libertas: Added 11d support using cfg80211
Dan Williams
dcbw at redhat.com
Tue May 18 00:26:46 EDT 2010
On Thu, 2010-05-13 at 04:00 -0700, Kiran Divekar wrote:
> Added 11d support for libertas driver using cfg80211. This is based on Holger Shurig's initial work to add cfg80211 support libertas.
> (https://patchwork.kernel.org/patch/64286/)
>
> Please let us know, if there are any improvements comments.
>
> Code is added to send 11d enable command to firmware while
> initialisation and pass 11d specific information to firmware
> when notifier handler is called by cfg80211.
Looks OK, though I'd prefer making 11D_DOMAIN_INFO a direct command
instead. But if you don't want to do that right now, that's fine, just
send the final patch and I'll ack it.
Acked-by: Dan Williams <dcbw at redhat.com>
> Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>
> Signed-off-by: Kiran Divekar <dkiran at marvell.com>
> ---
> drivers/net/wireless/libertas/cfg.c | 110 +++++++++++++++++++++++++++++++
> drivers/net/wireless/libertas/cfg.h | 5 ++
> drivers/net/wireless/libertas/cmd.c | 65 ++++++++++++++++++
> drivers/net/wireless/libertas/cmdresp.c | 50 ++++++++++++++
> drivers/net/wireless/libertas/decl.h | 5 ++
> drivers/net/wireless/libertas/dev.h | 3 +
> drivers/net/wireless/libertas/host.h | 28 ++++++++-
> 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 9b23728..7a44384 100644
> --- a/drivers/net/wireless/libertas/cfg.c
> +++ b/drivers/net/wireless/libertas/cfg.c
> @@ -8,6 +8,7 @@
>
> #include <linux/slab.h>
> #include <linux/if_arp.h>
> +#include <linux/ieee80211.h>
> #include <net/cfg80211.h>
> #include <asm/unaligned.h>
>
> @@ -2044,6 +2045,7 @@ int lbs_cfg_register(struct lbs_private *priv)
> */
> wdev->wiphy->cipher_suites = cipher_suites;
> wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
> + wdev->wiphy->reg_notifier = lbs_reg_notifier;
>
> ret = wiphy_register(wdev->wiphy);
> if (ret < 0)
> @@ -2063,6 +2065,114 @@ int lbs_cfg_register(struct lbs_private *priv)
> 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)
> +{
> + 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);
> +
> + lbs_deb_leave(LBS_DEB_CFG80211);
> +
> + return 0;
> +}
>
> void lbs_scan_deinit(struct lbs_private *priv)
> {
> diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
> index eae3fd9..756fb98 100644
> --- a/drivers/net/wireless/libertas/cfg.h
> +++ b/drivers/net/wireless/libertas/cfg.h
> @@ -3,11 +3,16 @@
>
> struct device;
> struct lbs_private;
> +struct regulatory_request;
> +struct wiphy;
>
> struct wireless_dev *lbs_cfg_alloc(struct device *dev);
> int lbs_cfg_register(struct lbs_private *priv);
> void lbs_cfg_free(struct lbs_private *priv);
>
> +int lbs_reg_notifier(struct wiphy *wiphy,
> + struct regulatory_request *request);
> +
> /* All of those are TODOs: */
> #define lbs_cmd_802_11_rssi(priv, cmdptr) (0)
> #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 10067ca..cb93de9 100644
> --- a/drivers/net/wireless/libertas/cmd.c
> +++ b/drivers/net/wireless/libertas/cmd.c
> @@ -877,6 +877,66 @@ void lbs_set_mac_control(struct lbs_private *priv)
> }
>
> /**
> + * @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.
> *
> * @param priv A pointer to struct lbs_private structure
> @@ -974,6 +1034,11 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
> 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 =
> diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
> index d6c61bd..c31b32d 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,
> 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
> + */
> +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)
> {
> @@ -130,6 +176,10 @@ static inline int handle_cmd_response(struct lbs_private *priv,
> ret = lbs_ret_802_11_rssi(priv, resp);
> 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,
> diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
> index 2bccad3..9d2566e 100644
> --- a/drivers/net/wireless/libertas/decl.h
> +++ b/drivers/net/wireless/libertas/decl.h
> @@ -13,6 +13,7 @@
> struct lbs_private;
> struct sk_buff;
> struct net_device;
> +struct cmd_ds_command;
>
>
> /* ethtool.c */
> @@ -52,5 +53,9 @@ int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
> u32 lbs_fw_index_to_data_rate(u8 index);
> u8 lbs_data_rate_to_fw_index(u32 rate);
>
> +int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
> + struct cmd_ds_command *cmd, u16 cmdoption);
> +
> +int lbs_ret_802_11d_domain_info(struct cmd_ds_command *resp);
>
> #endif
> diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
> index afbd891..d9dad5d 100644
> --- a/drivers/net/wireless/libertas/dev.h
> +++ b/drivers/net/wireless/libertas/dev.h
> @@ -60,6 +60,9 @@ struct lbs_private {
> struct dentry *regs_dir;
> struct dentry *debugfs_regs_files[6];
>
> + /** 11D and domain regulatory data */
> + struct lbs_802_11d_domain_reg domain_reg;
> +
> /* Hardware debugging */
> u32 mac_offset;
> u32 bbp_offset;
> diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
> index 3809c0b..112fbf1 100644
> --- a/drivers/net/wireless/libertas/host.h
> +++ b/drivers/net/wireless/libertas/host.h
> @@ -389,6 +389,30 @@ struct lbs_offset_value {
> u32 value;
> } __attribute__ ((packed));
>
> +#define MRVDRV_MAX_TRIPLET_802_11D 83
> +
> +#define COUNTRY_CODE_LEN 3
> +
> +struct mrvl_ie_domain_param_set {
> + struct mrvl_ie_header header;
> +
> + u8 countrycode[COUNTRY_CODE_LEN];
> + struct ieee80211_country_ie_triplet triplet[1];
> +} __attribute__ ((packed));
> +
> +struct cmd_ds_802_11d_domain_info {
> + __le16 action;
> + struct mrvl_ie_domain_param_set domain;
> +} __attribute__ ((packed));
> +
> +struct lbs_802_11d_domain_reg {
> + /** Country code*/
> + u8 country_code[COUNTRY_CODE_LEN];
> + /** No. of triplet*/
> + u8 no_triplet;
> + struct ieee80211_country_ie_triplet triplet[MRVDRV_MAX_TRIPLET_802_11D];
> +} __attribute__ ((packed));
> +
> /*
> * Define data structure for CMD_GET_HW_SPEC
> * This structure defines the response for the GET_HW_SPEC command
> @@ -949,6 +973,9 @@ struct cmd_ds_command {
> struct cmd_ds_bbp_reg_access bbpreg;
> struct cmd_ds_rf_reg_access rfreg;
>
> + struct cmd_ds_802_11d_domain_info domaininfo;
> + struct cmd_ds_802_11d_domain_info domaininforesp;
> +
> struct cmd_ds_802_11_tpc_cfg tpccfg;
> struct cmd_ds_802_11_afc afc;
> struct cmd_ds_802_11_led_ctrl ledgpio;
> @@ -958,5 +985,4 @@ struct cmd_ds_command {
> struct cmd_ds_802_11_beacon_control bcn_ctrl;
> } params;
> } __attribute__ ((packed));
> -
> #endif
> diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
> index 2fd5042..ff4380e 100644
> --- a/drivers/net/wireless/libertas/main.c
> +++ b/drivers/net/wireless/libertas/main.c
> @@ -629,6 +629,9 @@ static int lbs_setup_firmware(struct lbs_private *priv)
> priv->txpower_max = maxlevel;
> }
>
> + /* Send cmd to FW to enable 11D function */
> + ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_11D_ENABLE, 1);
> +
> lbs_set_mac_control(priv);
> done:
> lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
More information about the libertas-dev
mailing list