Signed-off-by: Daniel Golle <[email protected]>

 create mode 100644 
package/mac80211/patches/580-cfg80211-antenna-configuration-profiles.patch
 create mode 100644 
package/mac80211/patches/581-mac80211-antenna-configuration-ops.patch
 create mode 100644 
package/mac80211/patches/582-ath9k-antenna-switch-profile-support-for-ar9285.patch

diff --git 
a/package/mac80211/patches/580-cfg80211-antenna-configuration-profiles.patch 
b/package/mac80211/patches/580-cfg80211-antenna-configuration-profiles.patch
new file mode 100644
index 0000000..e3429c5
--- /dev/null
+++ b/package/mac80211/patches/580-cfg80211-antenna-configuration-profiles.patch
@@ -0,0 +1,247 @@
+From bb2d00ec0ccd141170c61fb66639ab9234a69915 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <[email protected]>
+Date: Tue, 13 Dec 2011 17:31:52 +0200
+Subject: [PATCH 1/3] cfg80211: add support for antenna configuration profiles
+
+This patch adds support for antenna configuration profiles to
+cfg80211/nl80211. It provides an infrastructure to allow the user
+to select from pre-defined configartion profiles such as
+vertical/horizontal polarization or built-in/external antenna.
+
+Cc: Adrian Chadd <[email protected]>
+Cc: Johannes Berg <[email protected]>
+Signed-off-by: Daniel Golle <[email protected]>
+---
+ include/linux/nl80211.h | 47 ++++++++++++++++++++++++++++++++++++++++
+ include/net/cfg80211.h  | 27 +++++++++++++++++++++++
+ net/wireless/nl80211.c  | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 131 insertions(+)
+
+--- a/include/linux/nl80211.h
++++ b/include/linux/nl80211.h
+@@ -1245,6 +1245,12 @@ enum nl80211_commands {
+  * @NL80211_ATTR_BG_SCAN_PERIOD: Background scan period in seconds
+  *      or 0 to disable background scan.
+  *
++ * @NL80211_ATTR_WIPHY_EXTANT: External antenna switch profiles. This nested
++ *    structure contains a list of available profiles as well as the current
++ *    profile selection status.
++ *
++ * @NL80211_ATTR_WIPHY_EXTANT_SELECT: Select extant profile.
++ *
+  * @NL80211_ATTR_MAX: highest attribute number currently defined
+  * @__NL80211_ATTR_AFTER_LAST: internal use
+  */
+@@ -1498,6 +1504,9 @@ enum nl80211_attrs {
+ 
+       NL80211_ATTR_WDEV,
+ 
++      NL80211_ATTR_WIPHY_EXTANT,
++      NL80211_ATTR_WIPHY_EXTANT_SELECT,
++
+       /* add attributes here, update the policy in nl80211.c */
+ 
+       __NL80211_ATTR_AFTER_LAST,
+@@ -1819,6 +1828,44 @@ enum nl80211_mpath_info {
+       NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
+ };
+ 
++
++
++/**
++ * enum nl80211_extant_attr - external antenna switch
++ * @__NL80211_EXTANT_ATTR_INVALID: attribute number 0 is reserved
++ * @NL80211_EXTANT_ATTR_STATE: currently selected profile
++ * @NL80211_EXTANT_ATTR_PROFILES: highest profile attribute currently defined
++ * @NL80211_EXTANT_ATTR_MAX: highest profile attribute currently defined
++ * @__NL80211_EXTANT_ATTR_AFTER_LAST: internal use
++ */
++enum nl80211_extant_attr {
++      __NL80211_EXTANT_ATTR_INVALID,
++      NL80211_EXTANT_ATTR_STATE,
++      NL80211_EXTANT_ATTR_PROFILES,
++      /* keep last */
++      __NL80211_EXTANT_ATTR_AFTER_LAST,
++      NL80211_EXTANT_ATTR_MAX = __NL80211_EXTANT_ATTR_AFTER_LAST - 1
++};
++
++/**
++ * enum nl80211_easp_attr - external antenna switch configuration profile
++ * @__NL80211_EASP_ATTR_INVALID: attribute number 0 is reserved
++ * @NL80211_EASP_ATTR_ID: unique identifier
++ * @NL80211_EASP_ATTR_NAME: human-readable name of the configuration profile
++ * @NL80211_EASP_ATTR_DESC: human-readable description of the configuration 
profile
++ * @NL80211_EASP_ATTR_MAX: highest profile attribute currently defined
++ * @__NL80211_EASP_ATTR_AFTER_LAST: internal use
++ */
++enum nl80211_easp_attr {
++      __NL80211_EASP_ATTR_INVALID,
++      NL80211_EASP_ATTR_ID,
++      NL80211_EASP_ATTR_NAME,
++      NL80211_EASP_ATTR_DESC,
++      /* keep last */
++      __NL80211_EASP_ATTR_AFTER_LAST,
++      NL80211_EASP_ATTR_MAX = __NL80211_EASP_ATTR_AFTER_LAST - 1
++};
++
+ /**
+  * enum nl80211_band_attr - band attributes
+  * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved
+--- a/include/net/cfg80211.h
++++ b/include/net/cfg80211.h
+@@ -1601,6 +1601,9 @@ struct cfg80211_gtk_rekey_data {
+  *
+  * @get_antenna: Get current antenna configuration from device (tx_ant, 
rx_ant).
+  *
++ * @set_extant: Select the external antenna switch configuration profile.
++ * @get_extant: Get currently selected antenna switch profile.
++ *
+  * @set_ringparam: Set tx and rx ring sizes.
+  *
+  * @get_ringparam: Get tx and rx ring current and maximum sizes.
+@@ -1802,6 +1805,9 @@ struct cfg80211_ops {
+       int     (*set_antenna)(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant);
+       int     (*get_antenna)(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant);
+ 
++      int     (*set_extant)(struct wiphy *wiphy, u32 extant);
++      int     (*get_extant)(struct wiphy *wiphy, u32 *extant);
++
+       int     (*set_ringparam)(struct wiphy *wiphy, u32 tx, u32 rx);
+       void    (*get_ringparam)(struct wiphy *wiphy,
+                                u32 *tx, u32 *tx_max, u32 *rx, u32 *rx_max);
+@@ -1925,6 +1931,7 @@ enum wiphy_flags {
+       WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD        = BIT(19),
+       WIPHY_FLAG_OFFCHAN_TX                   = BIT(20),
+       WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL        = BIT(21),
++      WIPHY_FLAG_HAS_EXTANT_SWITCH            = BIT(22),
+ };
+ 
+ /**
+@@ -2052,6 +2059,21 @@ struct wiphy_wowlan_support {
+ };
+ 
+ /**
++ * struct wiphy_extant_profile - User-parsable external antenna switch info
++ *
++ * This structure is used to provide the available switch configurations.
++ * @id: unique identifier of the profile
++ * @name: profile name (a short string)
++ * @description: user-parsable description of the profile
++ */
++struct wiphy_extant_profile {
++      int id;
++      char *name;
++      char *desc;
++};
++
++
++/**
+  * struct wiphy - wireless hardware description
+  * @reg_notifier: the driver's regulatory notification callback,
+  *    note that if your driver uses wiphy_apply_custom_regulatory()
+@@ -2137,6 +2159,8 @@ struct wiphy_wowlan_support {
+  *     See &enum nl80211_probe_resp_offload_support_attr. Only valid
+  *     when the wiphy flag @WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD is set.
+  *
++ * @extant: struct holding the available antenna switch configuration 
profiles.
++ *
+  * @max_remain_on_channel_duration: Maximum time a remain-on-channel operation
+  *    may request, if implemented.
+  *
+@@ -2212,6 +2236,9 @@ struct wiphy {
+        */
+       u32 probe_resp_offload;
+ 
++      u8 num_extant_profiles;
++      struct wiphy_extant_profile *extant;
++
+       /* If multiple wiphys are registered and you're handed e.g.
+        * a regular netdev with assigned ieee80211_ptr, you won't
+        * know whether it points to a wiphy your driver has registered
+--- a/net/wireless/nl80211.c
++++ b/net/wireless/nl80211.c
+@@ -354,6 +354,8 @@ static const struct nla_policy nl80211_p
+       [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
+       [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
+       [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
++      [NL80211_ATTR_WIPHY_EXTANT] = { .type = NLA_NESTED },
++      [NL80211_ATTR_WIPHY_EXTANT_SELECT] = { .type = NLA_U32 },
+ };
+ 
+ /* policy for the key attributes */
+@@ -857,6 +859,8 @@ static int nl80211_send_wiphy(struct sk_
+       struct nlattr *nl_bands, *nl_band;
+       struct nlattr *nl_freqs, *nl_freq;
+       struct nlattr *nl_rates, *nl_rate;
++      struct nlattr *nl_easps, *nl_easp;
++      struct nlattr *nl_extant;
+       struct nlattr *nl_cmds;
+       enum ieee80211_band band;
+       struct ieee80211_channel *chan;
+@@ -952,6 +956,45 @@ static int nl80211_send_wiphy(struct sk_
+               }
+       }
+ 
++      /*
++       * antenna switch configuration profile support
++       */
++      if (dev->wiphy.flags & WIPHY_FLAG_HAS_EXTANT_SWITCH) {
++              nl_extant = nla_nest_start(msg, NL80211_ATTR_WIPHY_EXTANT);
++              if (!nl_extant)
++                      goto nla_put_failure;
++
++              /* currently selected configuration profile */
++              if (dev->ops->get_extant) {
++                      u32 extant;
++                      int res;
++                      res = dev->ops->get_extant(&dev->wiphy, &extant);
++                      if (!res)
++                              NLA_PUT_U32(msg,
++                                      NL80211_EXTANT_ATTR_STATE,
++                                      extant);
++              }
++
++              /* available profiles */
++              nl_easps = nla_nest_start(msg, NL80211_EXTANT_ATTR_PROFILES);
++              for (i=0; i < dev->wiphy.num_extant_profiles; i++) {
++                      nl_easp = nla_nest_start(msg, i);
++                      if (!nl_easp)
++                              goto nla_put_failure;
++                      NLA_PUT_U32(msg, NL80211_EASP_ATTR_ID,
++                              dev->wiphy.extant[i].id);
++                      NLA_PUT(msg, NL80211_EASP_ATTR_NAME,
++                              strlen(dev->wiphy.extant[i].name),
++                              dev->wiphy.extant[i].name);
++                      NLA_PUT(msg, NL80211_EASP_ATTR_DESC,
++                              strlen(dev->wiphy.extant[i].desc),
++                              dev->wiphy.extant[i].desc);
++                      nla_nest_end(msg, nl_easp);
++              }
++              nla_nest_end(msg, nl_easps);
++              nla_nest_end(msg, nl_extant);
++      }
++
+       if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
+                               dev->wiphy.interface_modes))
+               goto nla_put_failure;
+@@ -1630,6 +1673,20 @@ static int nl80211_set_wiphy(struct sk_b
+               if (result)
+                       goto bad_res;
+       }
++
++      if (info->attrs[NL80211_ATTR_WIPHY_EXTANT_SELECT]) {
++              u32 extant;
++              if (!rdev->ops->set_extant) {
++                      result = -EOPNOTSUPP;
++                      goto bad_res;
++              }
++
++              extant = 
nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_EXTANT_SELECT]);
++
++              result = rdev->ops->set_extant(&rdev->wiphy, extant);
++              if (result)
++                      goto bad_res;
++      }
+ 
+       changed = 0;
+ 
diff --git 
a/package/mac80211/patches/581-mac80211-antenna-configuration-ops.patch 
b/package/mac80211/patches/581-mac80211-antenna-configuration-ops.patch
new file mode 100644
index 0000000..df8ccfb
--- /dev/null
+++ b/package/mac80211/patches/581-mac80211-antenna-configuration-ops.patch
@@ -0,0 +1,92 @@
+From cd01191c9d7386ffb99b9087d4665f740d1be725 Mon Sep 17 00:00:00 2001
+From: Daniel Golle <[email protected]>
+Date: Tue, 13 Dec 2011 17:37:33 +0200
+Subject: [PATCH 2/3] mac80211: add operations for antenna configuration
+ profiles
+
+This adds get_extant and set_extant operations to mac80211 thus
+allowing mac80211 drivers to expose those operations to the user.
+
+Cc: Adrian Chadd <[email protected]>
+Cc: Johannes Berg <[email protected]>
+Signed-off-by: Daniel Golle <[email protected]>
+---
+ include/net/mac80211.h      |  3 +++
+ net/mac80211/cfg.c          | 19 +++++++++++++++++++
+ net/mac80211/driver-ops.h   | 20 ++++++++++++++++++++
+ 4 files changed, 88 insertions(+)
+
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -2376,6 +2376,9 @@ struct ieee80211_ops {
+       int (*set_antenna)(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant);
+       int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant);
+ 
++      int (*set_extant)(struct ieee80211_hw *hw, u32 extant);
++      int (*get_extant)(struct ieee80211_hw *hw, u32 *extant);
++
+       int (*remain_on_channel)(struct ieee80211_hw *hw,
+                                struct ieee80211_channel *chan,
+                                enum nl80211_channel_type channel_type,
+--- a/net/mac80211/cfg.c
++++ b/net/mac80211/cfg.c
+@@ -2593,6 +2593,23 @@ static int ieee80211_get_antenna(struct
+       return drv_get_antenna(local, tx_ant, rx_ant);
+ }
+ 
++static int ieee80211_set_extant(struct wiphy *wiphy, u32 extant)
++{
++      struct ieee80211_local *local = wiphy_priv(wiphy);
++
++      if (local->started)
++              return -EOPNOTSUPP;
++
++      return drv_set_extant(local, extant);
++}
++
++static int ieee80211_get_extant(struct wiphy *wiphy, u32 *extant)
++{
++      struct ieee80211_local *local = wiphy_priv(wiphy);
++
++      return drv_get_extant(local, extant);
++}
++
+ static int ieee80211_set_ringparam(struct wiphy *wiphy, u32 tx, u32 rx)
+ {
+       struct ieee80211_local *local = wiphy_priv(wiphy);
+@@ -3082,6 +3099,8 @@ struct cfg80211_ops mac80211_config_ops
+       .mgmt_frame_register = ieee80211_mgmt_frame_register,
+       .set_antenna = ieee80211_set_antenna,
+       .get_antenna = ieee80211_get_antenna,
++      .set_extant = ieee80211_set_extant,
++      .get_extant = ieee80211_get_extant,
+       .set_ringparam = ieee80211_set_ringparam,
+       .get_ringparam = ieee80211_get_ringparam,
+       .set_rekey_data = ieee80211_set_rekey_data,
+--- a/net/mac80211/driver-ops.h
++++ b/net/mac80211/driver-ops.h
+@@ -712,6 +712,24 @@ static inline int drv_get_antenna(struct
+       return ret;
+ }
+ 
++static inline int drv_set_extant(struct ieee80211_local *local, u32 extant)
++{
++      int ret = -EOPNOTSUPP;
++      might_sleep();
++      if (local->ops->set_extant)
++              ret = local->ops->set_extant(&local->hw, extant);
++      return ret;
++}
++
++static inline int drv_get_extant(struct ieee80211_local *local, u32 *extant)
++{
++      int ret = -EOPNOTSUPP;
++      might_sleep();
++      if (local->ops->get_extant)
++              ret = local->ops->get_extant(&local->hw, extant);
++      return ret;
++}
++
+ static inline int drv_remain_on_channel(struct ieee80211_local *local,
+                                       struct ieee80211_channel *chan,
+                                       enum nl80211_channel_type chantype,
diff --git 
a/package/mac80211/patches/582-ath9k-antenna-switch-profile-support-for-ar9285.patch
 
b/package/mac80211/patches/582-ath9k-antenna-switch-profile-support-for-ar9285.patch
new file mode 100644
index 0000000..d2e040e
--- /dev/null
+++ 
b/package/mac80211/patches/582-ath9k-antenna-switch-profile-support-for-ar9285.patch
@@ -0,0 +1,448 @@
+From 564b3c4c8cf0ea10dbb090f2dc818d282a795cbd Mon Sep 17 00:00:00 2001
+From: Daniel Golle <[email protected]>
+Date: Tue, 13 Dec 2011 17:53:31 +0200
+Subject: [PATCH 3/3] ath9k: antenna switch profile support for ar9285
+
+This implements support for antenna configuration profiles for
+devices based on Ar9285 using the AR_PHY_SWITCH_COM register, such
+as the Allnet ALL0258N, Senao/EnGenius ENH200 and maybe others.
+
+Cc: Adrian Chadd <[email protected]>
+Cc: Johannes Berg <[email protected]>
+Signed-off-by: Daniel Golle <[email protected]>
+---
+ drivers/net/wireless/ath/ath9k/ar9002_phy.c    | 13 ++++
+ drivers/net/wireless/ath/ath9k/ar9003_eeprom.c |  8 ++-
+ drivers/net/wireless/ath/ath9k/eeprom.h        |  1 +
+ drivers/net/wireless/ath/ath9k/eeprom_4k.c     | 29 +++++++-
+ drivers/net/wireless/ath/ath9k/eeprom_9287.c   |  8 ++-
+ drivers/net/wireless/ath/ath9k/eeprom_def.c    |  8 ++-
+ drivers/net/wireless/ath/ath9k/hw.h            | 28 ++++++++
+ drivers/net/wireless/ath/ath9k/init.c          | 92 ++++++++++++++++++++++++++
+ drivers/net/wireless/ath/ath9k/main.c          | 36 ++++++++++
+ include/linux/ath9k_platform.h                 |  6 ++
+ 10 files changed, 224 insertions(+), 5 deletions(-)
+
+--- a/drivers/net/wireless/ath/ath9k/ar9002_phy.c
++++ b/drivers/net/wireless/ath/ath9k/ar9002_phy.c
+@@ -555,6 +555,16 @@ static void ar9002_hw_antdiv_comb_conf_s
+       REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regval);
+ }
+ 
++static void ar9002_hw_extant_switchcom_set(struct ath_hw *ah, u32 
switch_com_value)
++{
++      REG_WRITE(ah, AR_PHY_SWITCH_COM, switch_com_value);
++}
++
++static u32 ar9002_hw_extant_switchcom_get(struct ath_hw *ah)
++{
++      return REG_READ(ah, AR_PHY_SWITCH_COM);
++}
++
+ void ar9002_hw_attach_phy_ops(struct ath_hw *ah)
+ {
+       struct ath_hw_private_ops *priv_ops = ath9k_hw_private_ops(ah);
+@@ -572,5 +582,8 @@ void ar9002_hw_attach_phy_ops(struct ath
+       ops->antdiv_comb_conf_get = ar9002_hw_antdiv_comb_conf_get;
+       ops->antdiv_comb_conf_set = ar9002_hw_antdiv_comb_conf_set;
+ 
++      ops->extant_switchcom_set = ar9002_hw_extant_switchcom_set;
++      ops->extant_switchcom_get = ar9002_hw_extant_switchcom_get;
++
+       ar9002_hw_set_nf_limits(ah);
+ }
+--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
++++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+@@ -5153,6 +5153,11 @@ unsigned int ar9003_get_paprd_scale_fact
+       }
+ }
+ 
++static u32 ath9k_hw_ar9300_get_switch_com(struct ath_hw *ah)
++{
++      return 0;
++}
++
+ const struct eeprom_ops eep_ar9300_ops = {
+       .check_eeprom = ath9k_hw_ar9300_check_eeprom,
+       .get_eeprom = ath9k_hw_ar9300_get_eeprom,
+@@ -5163,5 +5168,6 @@ const struct eeprom_ops eep_ar9300_ops =
+       .set_board_values = ath9k_hw_ar9300_set_board_values,
+       .set_addac = ath9k_hw_ar9300_set_addac,
+       .set_txpower = ath9k_hw_ar9300_set_txpower,
+-      .get_spur_channel = ath9k_hw_ar9300_get_spur_channel
++      .get_spur_channel = ath9k_hw_ar9300_get_spur_channel,
++      .get_switch_com = ath9k_hw_ar9300_get_switch_com
+ };
+--- a/drivers/net/wireless/ath/ath9k/eeprom.h
++++ b/drivers/net/wireless/ath/ath9k/eeprom.h
+@@ -652,6 +652,7 @@ struct eeprom_ops {
+                          u16 cfgCtl, u8 twiceAntennaReduction,
+                          u8 powerLimit, bool test);
+       u16 (*get_spur_channel)(struct ath_hw *ah, u16 i, bool is2GHz);
++      u32 (*get_switch_com)(struct ath_hw *ah);
+ };
+ 
+ void ath9k_hw_analog_shift_regwrite(struct ath_hw *ah, u32 reg, u32 val);
+--- a/drivers/net/wireless/ath/ath9k/eeprom_4k.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_4k.c
+@@ -811,6 +811,30 @@ static void ath9k_hw_4k_set_gain(struct
+ }
+ 
+ /*
++ * Get common antenna configuration bits
++ */
++static u32 ath9k_hw_4k_get_switch_com(struct ath_hw *ah)
++{
++      struct ath_hw_switch_com_profile *easp;
++      struct ar5416_eeprom_4k *eep = &ah->eeprom.map4k;
++      struct modal_eep_4k_header *pModal = &eep->modalHeader;
++
++      /* return value from eeprom if there are no profiles */
++      if (!ah->switch_com_profiles)
++              return pModal->antCtrlCommon;
++
++      /* find matching value for profile id */
++      for(easp = ah->switch_com_profiles;easp->id >= 0; easp++) {
++              if ( easp->id == ah->selected_extant_profile )
++                      return easp->switch_com_value;
++      }
++
++      /* invalid profile selected, return an error */
++      return 1;
++}
++
++
++/*
+  * Read EEPROM header info and program the device for correct operation
+  * given the channel value.
+  */
+@@ -829,7 +853,7 @@ static void ath9k_hw_4k_set_board_values
+       pModal = &eep->modalHeader;
+       txRxAttenLocal = 23;
+ 
+-      REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon);
++      REG_WRITE(ah, AR_PHY_SWITCH_COM, ath9k_hw_4k_get_switch_com(ah));
+ 
+       /* Single chain for 4K EEPROM*/
+       ath9k_hw_4k_set_gain(ah, pModal, eep, txRxAttenLocal);
+@@ -1107,5 +1131,6 @@ const struct eeprom_ops eep_4k_ops = {
+       .get_eeprom_rev         = ath9k_hw_4k_get_eeprom_rev,
+       .set_board_values       = ath9k_hw_4k_set_board_values,
+       .set_txpower            = ath9k_hw_4k_set_txpower,
+-      .get_spur_channel       = ath9k_hw_4k_get_spur_channel
++      .get_spur_channel       = ath9k_hw_4k_get_spur_channel,
++      .get_switch_com         = ath9k_hw_4k_get_switch_com
+ };
+--- a/drivers/net/wireless/ath/ath9k/eeprom_9287.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_9287.c
+@@ -1036,6 +1036,11 @@ static u16 ath9k_hw_ar9287_get_spur_chan
+ #undef EEP_MAP9287_SPURCHAN
+ }
+ 
++static u32 ath9k_hw_ar9287_get_switch_com(struct ath_hw *ah)
++{
++      return 0;
++}
++
+ const struct eeprom_ops eep_ar9287_ops = {
+       .check_eeprom           = ath9k_hw_ar9287_check_eeprom,
+       .get_eeprom             = ath9k_hw_ar9287_get_eeprom,
+@@ -1045,5 +1050,6 @@ const struct eeprom_ops eep_ar9287_ops =
+       .get_eeprom_rev         = ath9k_hw_ar9287_get_eeprom_rev,
+       .set_board_values       = ath9k_hw_ar9287_set_board_values,
+       .set_txpower            = ath9k_hw_ar9287_set_txpower,
+-      .get_spur_channel       = ath9k_hw_ar9287_get_spur_channel
++      .get_spur_channel       = ath9k_hw_ar9287_get_spur_channel,
++      .get_switch_com         = ath9k_hw_ar9287_get_switch_com
+ };
+--- a/drivers/net/wireless/ath/ath9k/eeprom_def.c
++++ b/drivers/net/wireless/ath/ath9k/eeprom_def.c
+@@ -1369,6 +1369,11 @@ static u16 ath9k_hw_def_get_spur_channel
+ #undef EEP_DEF_SPURCHAN
+ }
+ 
++static u32 ath9k_hw_def_get_switch_com(struct ath_hw *ah)
++{
++      return 0;
++}
++
+ const struct eeprom_ops eep_def_ops = {
+       .check_eeprom           = ath9k_hw_def_check_eeprom,
+       .get_eeprom             = ath9k_hw_def_get_eeprom,
+@@ -1379,5 +1384,6 @@ const struct eeprom_ops eep_def_ops = {
+       .set_board_values       = ath9k_hw_def_set_board_values,
+       .set_addac              = ath9k_hw_def_set_addac,
+       .set_txpower            = ath9k_hw_def_set_txpower,
+-      .get_spur_channel       = ath9k_hw_def_get_spur_channel
++      .get_spur_channel       = ath9k_hw_def_get_spur_channel,
++      .get_switch_com         = ath9k_hw_def_get_switch_com
+ };
+--- a/drivers/net/wireless/ath/ath9k/hw.h
++++ b/drivers/net/wireless/ath/ath9k/hw.h
+@@ -587,6 +587,25 @@ struct ath_hw_radar_conf {
+       bool ext_channel;
+ };
+ 
++
++/**
++ * struct ath_hw_switch_com_profile - external antenna switch settings
++ *
++ * This structure holds a platform_data-supplied alternative value
++ * of AR_PHY_SWITCH_COM.
++ *
++ * @id: unique identifier
++ * @switch_com_value: bit-field to be set in register AR_PHY_SWITCH_COM
++ * @name: profile name
++ * @desc: profile description
++ */
++struct ath_hw_switch_com_profile {
++      int id;
++      u32 switch_com_value;
++      char *name;
++      char *desc;
++};
++
+ /**
+  * struct ath_hw_private_ops - callbacks used internally by hardware code
+  *
+@@ -685,6 +704,9 @@ struct ath_hw_ops {
+       void (*antdiv_comb_conf_set)(struct ath_hw *ah,
+                       struct ath_hw_antcomb_conf *antconf);
+ 
++      u32 (*extant_switchcom_get)(struct ath_hw *ah);
++      void (*extant_switchcom_set)(struct ath_hw *ah,
++                      u32 switch_com_value);
+ };
+ 
+ struct ath_nf_limits {
+@@ -918,6 +940,12 @@ struct ath_hw {
+       bool is_clk_25mhz;
+       int (*get_mac_revision)(void);
+       int (*external_reset)(void);
++
++      /*
++       * External antenna switch configuration profiles
++       */
++      struct ath_hw_switch_com_profile *switch_com_profiles;
++      u32 selected_extant_profile;
+ };
+ 
+ struct ath_bus_ops {
+--- a/drivers/net/wireless/ath/ath9k/init.c
++++ b/drivers/net/wireless/ath/ath9k/init.c
+@@ -504,6 +504,43 @@ static void ath9k_init_misc(struct ath_s
+               sc->ant_comb.count = ATH_ANT_DIV_COMB_INIT_COUNT;
+ }
+ 
++static int ath9k_init_switch_com_profiles(struct ath_softc *sc, struct ath_hw 
*ah)
++{
++      struct ath9k_platform_data *pdata = sc->dev->platform_data;
++      int i,j;
++
++      /* count valid profiles in platform data */
++      j=0;
++      for(i=0;i<ATH9K_PLAT_MAX_SWITCH_COM_PROFILES;i++) {
++              if ( pdata->switch_com_profiles[i].name )
++                      j++;
++      }
++      /*
++       * allocate space for the j profiles found + 1 for termination
++       */
++      if ( j>0 && ! ah->switch_com_profiles ) {
++              ah->switch_com_profiles = kcalloc(j + 1,
++                      sizeof(struct ath_hw_switch_com_profile),
++                      GFP_KERNEL);
++              if ( ! ah->switch_com_profiles )
++                      return -ENOMEM;
++      }
++      /* populate the space from platform_data */
++      j=0;
++      for(i=0;i<ATH9K_PLAT_MAX_SWITCH_COM_PROFILES;i++) {
++              if ( pdata->switch_com_profiles[i].name ) {
++                      ah->switch_com_profiles[j].id = j;
++                      ah->switch_com_profiles[j].switch_com_value = 
pdata->switch_com_profiles[i].val;
++                      ah->switch_com_profiles[j].name = 
pdata->switch_com_profiles[i].name;
++                      ah->switch_com_profiles[j].desc = 
pdata->switch_com_profiles[i].desc;
++                      j++;
++              }
++      }
++      if ( j>0 )
++              ah->switch_com_profiles[j].id = -1;
++      return 0;
++}
++
+ static int ath9k_init_softc(u16 devid, struct ath_softc *sc,
+                           const struct ath_bus_ops *bus_ops)
+ {
+@@ -599,6 +636,10 @@ static int ath9k_init_softc(u16 devid, s
+       if (ret)
+               goto err_btcoex;
+ 
++      ret = ath9k_init_switch_com_profiles(sc, ah);
++      if (ret)
++              goto err_btcoex;
++
+       ath9k_cmn_init_crypto(sc->sc_ah);
+       ath9k_init_misc(sc);
+ 
+@@ -615,6 +656,8 @@ err_queues:
+       ath9k_hw_deinit(ah);
+ err_hw:
+ 
++      if ( ah->switch_com_profiles )
++              kfree(ah->switch_com_profiles);
+       kfree(ah);
+       sc->sc_ah = NULL;
+ 
+@@ -771,6 +814,47 @@ void ath9k_set_hw_capab(struct ath_softc
+       SET_IEEE80211_PERM_ADDR(hw, common->macaddr);
+ }
+ 
++int ath9k_set_extant_profiles(struct ath_hw *ah, struct ieee80211_hw *hw)
++{
++      struct ath_common *common = ath9k_hw_common(ah);
++      int i;
++      /* nothing to do if there are no profiles */
++      if (! ah->switch_com_profiles)
++              return 0;
++
++      ath_dbg(common, CONFIG,
++              "Device got extant support.\n");
++      hw->wiphy->flags |= WIPHY_FLAG_HAS_EXTANT_SWITCH;
++
++      /* allocate profile structure in wiphy for all available profiles + 1 */
++      for (i=0; ah->switch_com_profiles[i].id >= 0; i++);
++      hw->wiphy->extant = kcalloc(i, sizeof(struct wiphy_extant_profile), 
GFP_KERNEL);
++      if ( ! hw->wiphy->extant )
++              return -ENOMEM;
++
++      ath_dbg(common, CONFIG,
++              "Detected %d antenna configuration profiles:\n", i);
++      hw->wiphy->num_extant_profiles = i;
++      /* populate wiphy->exant structure */
++      for (i=0; ah->switch_com_profiles[i].id >= 0; i++) {
++              hw->wiphy->extant[i].id = ah->switch_com_profiles[i].id;
++              hw->wiphy->extant[i].name = ah->switch_com_profiles[i].name;
++              hw->wiphy->extant[i].desc = ah->switch_com_profiles[i].desc;
++      ath_dbg(common, CONFIG,
++              "\t%d \"%s\" (%s)\n",
++              hw->wiphy->extant[i].id,
++              hw->wiphy->extant[i].name,
++              hw->wiphy->extant[i].desc);
++      }
++      return 0;
++}
++
++void ath9k_free_extant_profiles(struct ieee80211_hw *hw)
++{
++      if (hw->wiphy->extant)
++              kfree(hw->wiphy->extant);
++}
++
+ int ath9k_init_device(u16 devid, struct ath_softc *sc,
+                   const struct ath_bus_ops *bus_ops)
+ {
+@@ -789,6 +873,11 @@ int ath9k_init_device(u16 devid, struct
+       common = ath9k_hw_common(ah);
+       ath9k_set_hw_capab(sc, hw);
+ 
++      /* Initialize extant profiles */
++      error = ath9k_set_extant_profiles(ah, hw);
++      if (error != 0)
++              goto error_extant;
++
+       /* Initialize regulatory */
+       error = ath_regd_init(&common->regulatory, sc->hw->wiphy,
+                             ath9k_reg_notifier);
+@@ -848,6 +937,8 @@ error_rx:
+ error_tx:
+       /* Nothing */
+ error_regd:
++      ath9k_free_extant_profiles(hw);
++error_extant:
+       ath9k_deinit_softc(sc);
+ error_init:
+       return error;
+@@ -895,6 +986,7 @@ void ath9k_deinit_device(struct ath_soft
+       ieee80211_unregister_hw(hw);
+       ath_rx_cleanup(sc);
+       ath_tx_cleanup(sc);
++      ath9k_free_extant_profiles(hw);
+       ath9k_deinit_softc(sc);
+ }
+ 
+--- a/drivers/net/wireless/ath/ath9k/main.c
++++ b/drivers/net/wireless/ath/ath9k/main.c
+@@ -2390,6 +2390,40 @@ static void ath9k_set_wakeup(struct ieee
+ 
+ #endif
+ 
++static int ath9k_set_extant(struct ieee80211_hw *hw, u32 extant)
++{
++      struct ath_softc *sc = hw->priv;
++      struct ath_hw *ah = sc->sc_ah;
++      struct ath_common *common = ath9k_hw_common(ah);
++      u32 backup_extant = ah->selected_extant_profile;
++      u32 switch_com_value = 0;
++
++      ath_dbg(common, CONFIG, "entering set_extant(%d)\n", extant);
++
++      ah->selected_extant_profile = extant;
++      switch_com_value = ah->eep_ops->get_switch_com(ah);
++      if (switch_com_value & 0xf)
++              goto invalid_profile;
++
++      ath_dbg(common, CONFIG, "switch_com: %08x\n", switch_com_value);
++      return 0;
++
++invalid_profile:
++      ath_dbg(common, CONFIG, "invalid profile (%08x)\n", switch_com_value);
++      ah->selected_extant_profile = backup_extant;
++      return -EINVAL;
++}
++
++static int ath9k_get_extant(struct ieee80211_hw *hw, u32 *extant)
++{
++      struct ath_softc *sc = hw->priv;
++      struct ath_hw *ah = sc->sc_ah;
++      if (!ah->switch_com_profiles)
++              return -EINVAL;
++      *extant = ah->selected_extant_profile;
++      return 0;
++}
++
+ struct ieee80211_ops ath9k_ops = {
+       .tx                 = ath9k_tx,
+       .start              = ath9k_start,
+@@ -2418,6 +2452,8 @@ struct ieee80211_ops ath9k_ops = {
+       .get_stats          = ath9k_get_stats,
+       .set_antenna        = ath9k_set_antenna,
+       .get_antenna        = ath9k_get_antenna,
++      .set_extant         = ath9k_set_extant,
++      .get_extant         = ath9k_get_extant,
+ 
+ #ifdef CONFIG_PM_SLEEP
+       .suspend            = ath9k_suspend,
+--- a/include/linux/ath9k_platform.h
++++ b/include/linux/ath9k_platform.h
+@@ -20,6 +20,7 @@
+ #define _LINUX_ATH9K_PLATFORM_H
+ 
+ #define ATH9K_PLAT_EEP_MAX_WORDS      2048
++#define ATH9K_PLAT_MAX_SWITCH_COM_PROFILES    4
+ 
+ struct ath9k_platform_data {
+       u16 eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS];
+@@ -36,6 +37,12 @@ struct ath9k_platform_data {
+ 
+       int num_leds;
+       const struct gpio_led *leds;
++
++      struct {
++              u32 val;
++              char *name;
++              char *desc;
++      } switch_com_profiles[ATH9K_PLAT_MAX_SWITCH_COM_PROFILES];
+ };
+ 
+ #endif /* _LINUX_ATH9K_PLATFORM_H */
-- 
1.7.11.3

Attachment: pgpM6EA5SuClH.pgp
Description: PGP signature

_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/mailman/listinfo/openwrt-devel

Reply via email to