aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSujith Manoharan <c_manoha@qca.qualcomm.com>2013-12-17 23:23:18 -0500
committerJohn W. Linville <linville@tuxdriver.com>2013-12-18 15:23:37 -0500
commitf65c0825512b7b8ed6cc6e43bb46ca2c758d9ae4 (patch)
tree191e71555134308420c306b2b338212ef58dd6c2 /drivers
parentf726ee65ae61e04762f722372095c99d10ed96a9 (diff)
ath9k: Cleanup spectral scan code
* Move definitions to spectral.h * Move processing/debug code to spectral.c Signed-off-by: Sujith Manoharan <c_manoha@qca.qualcomm.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/ath9k/Makefile4
-rw-r--r--drivers/net/wireless/ath/ath9k/ath9k.h179
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c311
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c181
-rw-r--r--drivers/net/wireless/ath/ath9k/spectral.c543
-rw-r--r--drivers/net/wireless/ath/ath9k/spectral.h212
7 files changed, 761 insertions, 671 deletions
diff --git a/drivers/net/wireless/ath/ath9k/Makefile b/drivers/net/wireless/ath/ath9k/Makefile
index 337c459eda28..e9904e5ccd81 100644
--- a/drivers/net/wireless/ath/ath9k/Makefile
+++ b/drivers/net/wireless/ath/ath9k/Makefile
@@ -11,12 +11,14 @@ ath9k-$(CONFIG_ATH9K_BTCOEX_SUPPORT) += mci.o
11ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o 11ath9k-$(CONFIG_ATH9K_LEGACY_RATE_CONTROL) += rc.o
12ath9k-$(CONFIG_ATH9K_PCI) += pci.o 12ath9k-$(CONFIG_ATH9K_PCI) += pci.o
13ath9k-$(CONFIG_ATH9K_AHB) += ahb.o 13ath9k-$(CONFIG_ATH9K_AHB) += ahb.o
14ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o
15ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o 14ath9k-$(CONFIG_ATH9K_DFS_DEBUGFS) += dfs_debug.o
16ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o 15ath9k-$(CONFIG_ATH9K_DFS_CERTIFIED) += dfs.o
17ath9k-$(CONFIG_ATH9K_TX99) += tx99.o 16ath9k-$(CONFIG_ATH9K_TX99) += tx99.o
18ath9k-$(CONFIG_ATH9K_WOW) += wow.o 17ath9k-$(CONFIG_ATH9K_WOW) += wow.o
19 18
19ath9k-$(CONFIG_ATH9K_DEBUGFS) += debug.o \
20 spectral.o
21
20obj-$(CONFIG_ATH9K) += ath9k.o 22obj-$(CONFIG_ATH9K) += ath9k.o
21 23
22ath9k_hw-y:= \ 24ath9k_hw-y:= \
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index dbf2a97d4daa..55bb87400b1a 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -27,6 +27,7 @@
27#include "common.h" 27#include "common.h"
28#include "mci.h" 28#include "mci.h"
29#include "dfs.h" 29#include "dfs.h"
30#include "spectral.h"
30 31
31/* 32/*
32 * Header for the ath9k.ko driver core *only* -- hw code nor any other driver 33 * Header for the ath9k.ko driver core *only* -- hw code nor any other driver
@@ -716,23 +717,6 @@ struct ath9k_vif_iter_data {
716 int nadhocs; /* number of adhoc vifs */ 717 int nadhocs; /* number of adhoc vifs */
717}; 718};
718 719
719/* enum spectral_mode:
720 *
721 * @SPECTRAL_DISABLED: spectral mode is disabled
722 * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
723 * something else.
724 * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
725 * is performed manually.
726 * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels
727 * during a channel scan.
728 */
729enum spectral_mode {
730 SPECTRAL_DISABLED = 0,
731 SPECTRAL_BACKGROUND,
732 SPECTRAL_MANUAL,
733 SPECTRAL_CHANSCAN,
734};
735
736struct ath_softc { 720struct ath_softc {
737 struct ieee80211_hw *hw; 721 struct ieee80211_hw *hw;
738 struct device *dev; 722 struct device *dev;
@@ -822,162 +806,6 @@ struct ath_softc {
822#endif 806#endif
823}; 807};
824 808
825#define SPECTRAL_SCAN_BITMASK 0x10
826/* Radar info packet format, used for DFS and spectral formats. */
827struct ath_radar_info {
828 u8 pulse_length_pri;
829 u8 pulse_length_ext;
830 u8 pulse_bw_info;
831} __packed;
832
833/* The HT20 spectral data has 4 bytes of additional information at it's end.
834 *
835 * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]}
836 * [7:0]: all bins max_magnitude[9:2]
837 * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]}
838 * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
839 */
840struct ath_ht20_mag_info {
841 u8 all_bins[3];
842 u8 max_exp;
843} __packed;
844
845#define SPECTRAL_HT20_NUM_BINS 56
846
847/* WARNING: don't actually use this struct! MAC may vary the amount of
848 * data by -1/+2. This struct is for reference only.
849 */
850struct ath_ht20_fft_packet {
851 u8 data[SPECTRAL_HT20_NUM_BINS];
852 struct ath_ht20_mag_info mag_info;
853 struct ath_radar_info radar_info;
854} __packed;
855
856#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet))
857
858/* Dynamic 20/40 mode:
859 *
860 * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]}
861 * [7:0]: lower bins max_magnitude[9:2]
862 * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]}
863 * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]}
864 * [7:0]: upper bins max_magnitude[9:2]
865 * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]}
866 * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
867 */
868struct ath_ht20_40_mag_info {
869 u8 lower_bins[3];
870 u8 upper_bins[3];
871 u8 max_exp;
872} __packed;
873
874#define SPECTRAL_HT20_40_NUM_BINS 128
875
876/* WARNING: don't actually use this struct! MAC may vary the amount of
877 * data. This struct is for reference only.
878 */
879struct ath_ht20_40_fft_packet {
880 u8 data[SPECTRAL_HT20_40_NUM_BINS];
881 struct ath_ht20_40_mag_info mag_info;
882 struct ath_radar_info radar_info;
883} __packed;
884
885
886#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet))
887
888/* grabs the max magnitude from the all/upper/lower bins */
889static inline u16 spectral_max_magnitude(u8 *bins)
890{
891 return (bins[0] & 0xc0) >> 6 |
892 (bins[1] & 0xff) << 2 |
893 (bins[2] & 0x03) << 10;
894}
895
896/* return the max magnitude from the all/upper/lower bins */
897static inline u8 spectral_max_index(u8 *bins)
898{
899 s8 m = (bins[2] & 0xfc) >> 2;
900
901 /* TODO: this still doesn't always report the right values ... */
902 if (m > 32)
903 m |= 0xe0;
904 else
905 m &= ~0xe0;
906
907 return m + 29;
908}
909
910/* return the bitmap weight from the all/upper/lower bins */
911static inline u8 spectral_bitmap_weight(u8 *bins)
912{
913 return bins[0] & 0x3f;
914}
915
916/* FFT sample format given to userspace via debugfs.
917 *
918 * Please keep the type/length at the front position and change
919 * other fields after adding another sample type
920 *
921 * TODO: this might need rework when switching to nl80211-based
922 * interface.
923 */
924enum ath_fft_sample_type {
925 ATH_FFT_SAMPLE_HT20 = 1,
926 ATH_FFT_SAMPLE_HT20_40,
927};
928
929struct fft_sample_tlv {
930 u8 type; /* see ath_fft_sample */
931 __be16 length;
932 /* type dependent data follows */
933} __packed;
934
935struct fft_sample_ht20 {
936 struct fft_sample_tlv tlv;
937
938 u8 max_exp;
939
940 __be16 freq;
941 s8 rssi;
942 s8 noise;
943
944 __be16 max_magnitude;
945 u8 max_index;
946 u8 bitmap_weight;
947
948 __be64 tsf;
949
950 u8 data[SPECTRAL_HT20_NUM_BINS];
951} __packed;
952
953struct fft_sample_ht20_40 {
954 struct fft_sample_tlv tlv;
955
956 u8 channel_type;
957 __be16 freq;
958
959 s8 lower_rssi;
960 s8 upper_rssi;
961
962 __be64 tsf;
963
964 s8 lower_noise;
965 s8 upper_noise;
966
967 __be16 lower_max_magnitude;
968 __be16 upper_max_magnitude;
969
970 u8 lower_max_index;
971 u8 upper_max_index;
972
973 u8 lower_bitmap_weight;
974 u8 upper_bitmap_weight;
975
976 u8 max_exp;
977
978 u8 data[SPECTRAL_HT20_40_NUM_BINS];
979} __packed;
980
981/********/ 809/********/
982/* TX99 */ 810/* TX99 */
983/********/ 811/********/
@@ -1022,11 +850,6 @@ void ath9k_deinit_device(struct ath_softc *sc);
1022void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw); 850void ath9k_set_hw_capab(struct ath_softc *sc, struct ieee80211_hw *hw);
1023void ath9k_reload_chainmask_settings(struct ath_softc *sc); 851void ath9k_reload_chainmask_settings(struct ath_softc *sc);
1024 852
1025void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
1026int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
1027 enum spectral_mode spectral_mode);
1028
1029
1030#ifdef CONFIG_ATH9K_PCI 853#ifdef CONFIG_ATH9K_PCI
1031int ath_pci_init(void); 854int ath_pci_init(void);
1032void ath_pci_exit(void); 855void ath_pci_exit(void);
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 2f7dccfdb727..4c6f8b107ed6 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -17,7 +17,6 @@
17#include <linux/slab.h> 17#include <linux/slab.h>
18#include <linux/vmalloc.h> 18#include <linux/vmalloc.h>
19#include <linux/export.h> 19#include <linux/export.h>
20#include <linux/relay.h>
21#include <asm/unaligned.h> 20#include <asm/unaligned.h>
22 21
23#include "ath9k.h" 22#include "ath9k.h"
@@ -1016,293 +1015,6 @@ static const struct file_operations fops_recv = {
1016 .llseek = default_llseek, 1015 .llseek = default_llseek,
1017}; 1016};
1018 1017
1019static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
1020 size_t count, loff_t *ppos)
1021{
1022 struct ath_softc *sc = file->private_data;
1023 char *mode = "";
1024 unsigned int len;
1025
1026 switch (sc->spectral_mode) {
1027 case SPECTRAL_DISABLED:
1028 mode = "disable";
1029 break;
1030 case SPECTRAL_BACKGROUND:
1031 mode = "background";
1032 break;
1033 case SPECTRAL_CHANSCAN:
1034 mode = "chanscan";
1035 break;
1036 case SPECTRAL_MANUAL:
1037 mode = "manual";
1038 break;
1039 }
1040 len = strlen(mode);
1041 return simple_read_from_buffer(user_buf, count, ppos, mode, len);
1042}
1043
1044static ssize_t write_file_spec_scan_ctl(struct file *file,
1045 const char __user *user_buf,
1046 size_t count, loff_t *ppos)
1047{
1048 struct ath_softc *sc = file->private_data;
1049 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
1050 char buf[32];
1051 ssize_t len;
1052
1053 if (config_enabled(CONFIG_ATH9K_TX99))
1054 return -EOPNOTSUPP;
1055
1056 len = min(count, sizeof(buf) - 1);
1057 if (copy_from_user(buf, user_buf, len))
1058 return -EFAULT;
1059
1060 buf[len] = '\0';
1061
1062 if (strncmp("trigger", buf, 7) == 0) {
1063 ath9k_spectral_scan_trigger(sc->hw);
1064 } else if (strncmp("background", buf, 9) == 0) {
1065 ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
1066 ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
1067 } else if (strncmp("chanscan", buf, 8) == 0) {
1068 ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
1069 ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
1070 } else if (strncmp("manual", buf, 6) == 0) {
1071 ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
1072 ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
1073 } else if (strncmp("disable", buf, 7) == 0) {
1074 ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
1075 ath_dbg(common, CONFIG, "spectral scan: disabled\n");
1076 } else {
1077 return -EINVAL;
1078 }
1079
1080 return count;
1081}
1082
1083static const struct file_operations fops_spec_scan_ctl = {
1084 .read = read_file_spec_scan_ctl,
1085 .write = write_file_spec_scan_ctl,
1086 .open = simple_open,
1087 .owner = THIS_MODULE,
1088 .llseek = default_llseek,
1089};
1090
1091static ssize_t read_file_spectral_short_repeat(struct file *file,
1092 char __user *user_buf,
1093 size_t count, loff_t *ppos)
1094{
1095 struct ath_softc *sc = file->private_data;
1096 char buf[32];
1097 unsigned int len;
1098
1099 len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
1100 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1101}
1102
1103static ssize_t write_file_spectral_short_repeat(struct file *file,
1104 const char __user *user_buf,
1105 size_t count, loff_t *ppos)
1106{
1107 struct ath_softc *sc = file->private_data;
1108 unsigned long val;
1109 char buf[32];
1110 ssize_t len;
1111
1112 len = min(count, sizeof(buf) - 1);
1113 if (copy_from_user(buf, user_buf, len))
1114 return -EFAULT;
1115
1116 buf[len] = '\0';
1117 if (kstrtoul(buf, 0, &val))
1118 return -EINVAL;
1119
1120 if (val < 0 || val > 1)
1121 return -EINVAL;
1122
1123 sc->spec_config.short_repeat = val;
1124 return count;
1125}
1126
1127static const struct file_operations fops_spectral_short_repeat = {
1128 .read = read_file_spectral_short_repeat,
1129 .write = write_file_spectral_short_repeat,
1130 .open = simple_open,
1131 .owner = THIS_MODULE,
1132 .llseek = default_llseek,
1133};
1134
1135static ssize_t read_file_spectral_count(struct file *file,
1136 char __user *user_buf,
1137 size_t count, loff_t *ppos)
1138{
1139 struct ath_softc *sc = file->private_data;
1140 char buf[32];
1141 unsigned int len;
1142
1143 len = sprintf(buf, "%d\n", sc->spec_config.count);
1144 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1145}
1146
1147static ssize_t write_file_spectral_count(struct file *file,
1148 const char __user *user_buf,
1149 size_t count, loff_t *ppos)
1150{
1151 struct ath_softc *sc = file->private_data;
1152 unsigned long val;
1153 char buf[32];
1154 ssize_t len;
1155
1156 len = min(count, sizeof(buf) - 1);
1157 if (copy_from_user(buf, user_buf, len))
1158 return -EFAULT;
1159
1160 buf[len] = '\0';
1161 if (kstrtoul(buf, 0, &val))
1162 return -EINVAL;
1163
1164 if (val < 0 || val > 255)
1165 return -EINVAL;
1166
1167 sc->spec_config.count = val;
1168 return count;
1169}
1170
1171static const struct file_operations fops_spectral_count = {
1172 .read = read_file_spectral_count,
1173 .write = write_file_spectral_count,
1174 .open = simple_open,
1175 .owner = THIS_MODULE,
1176 .llseek = default_llseek,
1177};
1178
1179static ssize_t read_file_spectral_period(struct file *file,
1180 char __user *user_buf,
1181 size_t count, loff_t *ppos)
1182{
1183 struct ath_softc *sc = file->private_data;
1184 char buf[32];
1185 unsigned int len;
1186
1187 len = sprintf(buf, "%d\n", sc->spec_config.period);
1188 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1189}
1190
1191static ssize_t write_file_spectral_period(struct file *file,
1192 const char __user *user_buf,
1193 size_t count, loff_t *ppos)
1194{
1195 struct ath_softc *sc = file->private_data;
1196 unsigned long val;
1197 char buf[32];
1198 ssize_t len;
1199
1200 len = min(count, sizeof(buf) - 1);
1201 if (copy_from_user(buf, user_buf, len))
1202 return -EFAULT;
1203
1204 buf[len] = '\0';
1205 if (kstrtoul(buf, 0, &val))
1206 return -EINVAL;
1207
1208 if (val < 0 || val > 255)
1209 return -EINVAL;
1210
1211 sc->spec_config.period = val;
1212 return count;
1213}
1214
1215static const struct file_operations fops_spectral_period = {
1216 .read = read_file_spectral_period,
1217 .write = write_file_spectral_period,
1218 .open = simple_open,
1219 .owner = THIS_MODULE,
1220 .llseek = default_llseek,
1221};
1222
1223static ssize_t read_file_spectral_fft_period(struct file *file,
1224 char __user *user_buf,
1225 size_t count, loff_t *ppos)
1226{
1227 struct ath_softc *sc = file->private_data;
1228 char buf[32];
1229 unsigned int len;
1230
1231 len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
1232 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
1233}
1234
1235static ssize_t write_file_spectral_fft_period(struct file *file,
1236 const char __user *user_buf,
1237 size_t count, loff_t *ppos)
1238{
1239 struct ath_softc *sc = file->private_data;
1240 unsigned long val;
1241 char buf[32];
1242 ssize_t len;
1243
1244 len = min(count, sizeof(buf) - 1);
1245 if (copy_from_user(buf, user_buf, len))
1246 return -EFAULT;
1247
1248 buf[len] = '\0';
1249 if (kstrtoul(buf, 0, &val))
1250 return -EINVAL;
1251
1252 if (val < 0 || val > 15)
1253 return -EINVAL;
1254
1255 sc->spec_config.fft_period = val;
1256 return count;
1257}
1258
1259static const struct file_operations fops_spectral_fft_period = {
1260 .read = read_file_spectral_fft_period,
1261 .write = write_file_spectral_fft_period,
1262 .open = simple_open,
1263 .owner = THIS_MODULE,
1264 .llseek = default_llseek,
1265};
1266
1267static struct dentry *create_buf_file_handler(const char *filename,
1268 struct dentry *parent,
1269 umode_t mode,
1270 struct rchan_buf *buf,
1271 int *is_global)
1272{
1273 struct dentry *buf_file;
1274
1275 buf_file = debugfs_create_file(filename, mode, parent, buf,
1276 &relay_file_operations);
1277 *is_global = 1;
1278 return buf_file;
1279}
1280
1281static int remove_buf_file_handler(struct dentry *dentry)
1282{
1283 debugfs_remove(dentry);
1284
1285 return 0;
1286}
1287
1288void ath_debug_send_fft_sample(struct ath_softc *sc,
1289 struct fft_sample_tlv *fft_sample_tlv)
1290{
1291 int length;
1292 if (!sc->rfs_chan_spec_scan)
1293 return;
1294
1295 length = __be16_to_cpu(fft_sample_tlv->length) +
1296 sizeof(*fft_sample_tlv);
1297 relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
1298}
1299
1300static struct rchan_callbacks rfs_spec_scan_cb = {
1301 .create_buf_file = create_buf_file_handler,
1302 .remove_buf_file = remove_buf_file_handler,
1303};
1304
1305
1306static ssize_t read_file_regidx(struct file *file, char __user *user_buf, 1018static ssize_t read_file_regidx(struct file *file, char __user *user_buf,
1307 size_t count, loff_t *ppos) 1019 size_t count, loff_t *ppos)
1308{ 1020{
@@ -1772,10 +1484,7 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw,
1772 1484
1773void ath9k_deinit_debug(struct ath_softc *sc) 1485void ath9k_deinit_debug(struct ath_softc *sc)
1774{ 1486{
1775 if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) { 1487 ath9k_spectral_deinit_debug(sc);
1776 relay_close(sc->rfs_chan_spec_scan);
1777 sc->rfs_chan_spec_scan = NULL;
1778 }
1779} 1488}
1780 1489
1781int ath9k_init_debug(struct ath_hw *ah) 1490int ath9k_init_debug(struct ath_hw *ah)
@@ -1795,6 +1504,7 @@ int ath9k_init_debug(struct ath_hw *ah)
1795 1504
1796 ath9k_dfs_init_debug(sc); 1505 ath9k_dfs_init_debug(sc);
1797 ath9k_tx99_init_debug(sc); 1506 ath9k_tx99_init_debug(sc);
1507 ath9k_spectral_init_debug(sc);
1798 1508
1799 debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc, 1509 debugfs_create_file("dma", S_IRUSR, sc->debug.debugfs_phy, sc,
1800 &fops_dma); 1510 &fops_dma);
@@ -1841,23 +1551,6 @@ int ath9k_init_debug(struct ath_hw *ah)
1841 &fops_base_eeprom); 1551 &fops_base_eeprom);
1842 debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc, 1552 debugfs_create_file("modal_eeprom", S_IRUSR, sc->debug.debugfs_phy, sc,
1843 &fops_modal_eeprom); 1553 &fops_modal_eeprom);
1844 sc->rfs_chan_spec_scan = relay_open("spectral_scan",
1845 sc->debug.debugfs_phy,
1846 1024, 256, &rfs_spec_scan_cb,
1847 NULL);
1848 debugfs_create_file("spectral_scan_ctl", S_IRUSR | S_IWUSR,
1849 sc->debug.debugfs_phy, sc,
1850 &fops_spec_scan_ctl);
1851 debugfs_create_file("spectral_short_repeat", S_IRUSR | S_IWUSR,
1852 sc->debug.debugfs_phy, sc,
1853 &fops_spectral_short_repeat);
1854 debugfs_create_file("spectral_count", S_IRUSR | S_IWUSR,
1855 sc->debug.debugfs_phy, sc, &fops_spectral_count);
1856 debugfs_create_file("spectral_period", S_IRUSR | S_IWUSR,
1857 sc->debug.debugfs_phy, sc, &fops_spectral_period);
1858 debugfs_create_file("spectral_fft_period", S_IRUSR | S_IWUSR,
1859 sc->debug.debugfs_phy, sc,
1860 &fops_spectral_fft_period);
1861 debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR, 1554 debugfs_create_u32("gpio_mask", S_IRUSR | S_IWUSR,
1862 sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask); 1555 sc->debug.debugfs_phy, &sc->sc_ah->gpio_mask);
1863 debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR, 1556 debugfs_create_u32("gpio_val", S_IRUSR | S_IWUSR,
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h
index d6e3fa4299a4..4f596ddeb15d 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -292,8 +292,6 @@ void ath9k_sta_add_debugfs(struct ieee80211_hw *hw,
292 struct ieee80211_vif *vif, 292 struct ieee80211_vif *vif,
293 struct ieee80211_sta *sta, 293 struct ieee80211_sta *sta,
294 struct dentry *dir); 294 struct dentry *dir);
295void ath_debug_send_fft_sample(struct ath_softc *sc,
296 struct fft_sample_tlv *fft_sample);
297void ath9k_debug_stat_ant(struct ath_softc *sc, 295void ath9k_debug_stat_ant(struct ath_softc *sc,
298 struct ath_hw_antcomb_conf *div_ant_conf, 296 struct ath_hw_antcomb_conf *div_ant_conf,
299 int main_rssi_avg, int alt_rssi_avg); 297 int main_rssi_avg, int alt_rssi_avg);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 2410224b6def..3692b2a501a2 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -15,7 +15,6 @@
15 */ 15 */
16 16
17#include <linux/dma-mapping.h> 17#include <linux/dma-mapping.h>
18#include <linux/relay.h>
19#include "ath9k.h" 18#include "ath9k.h"
20#include "ar9003_mac.h" 19#include "ar9003_mac.h"
21 20
@@ -975,186 +974,6 @@ static void ath9k_process_tsf(struct ath_rx_status *rs,
975 rxs->mactime += 0x100000000ULL; 974 rxs->mactime += 0x100000000ULL;
976} 975}
977 976
978#ifdef CONFIG_ATH9K_DEBUGFS
979static s8 fix_rssi_inv_only(u8 rssi_val)
980{
981 if (rssi_val == 128)
982 rssi_val = 0;
983 return (s8) rssi_val;
984}
985#endif
986
987/* returns 1 if this was a spectral frame, even if not handled. */
988static int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
989 struct ath_rx_status *rs, u64 tsf)
990{
991#ifdef CONFIG_ATH9K_DEBUGFS
992 struct ath_hw *ah = sc->sc_ah;
993 u8 num_bins, *bins, *vdata = (u8 *)hdr;
994 struct fft_sample_ht20 fft_sample_20;
995 struct fft_sample_ht20_40 fft_sample_40;
996 struct fft_sample_tlv *tlv;
997 struct ath_radar_info *radar_info;
998 int len = rs->rs_datalen;
999 int dc_pos;
1000 u16 fft_len, length, freq = ah->curchan->chan->center_freq;
1001 enum nl80211_channel_type chan_type;
1002
1003 /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
1004 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
1005 * yet, but this is supposed to be possible as well.
1006 */
1007 if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
1008 rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
1009 rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
1010 return 0;
1011
1012 /* check if spectral scan bit is set. This does not have to be checked
1013 * if received through a SPECTRAL phy error, but shouldn't hurt.
1014 */
1015 radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
1016 if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
1017 return 0;
1018
1019 chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
1020 if ((chan_type == NL80211_CHAN_HT40MINUS) ||
1021 (chan_type == NL80211_CHAN_HT40PLUS)) {
1022 fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
1023 num_bins = SPECTRAL_HT20_40_NUM_BINS;
1024 bins = (u8 *)fft_sample_40.data;
1025 } else {
1026 fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
1027 num_bins = SPECTRAL_HT20_NUM_BINS;
1028 bins = (u8 *)fft_sample_20.data;
1029 }
1030
1031 /* Variation in the data length is possible and will be fixed later */
1032 if ((len > fft_len + 2) || (len < fft_len - 1))
1033 return 1;
1034
1035 switch (len - fft_len) {
1036 case 0:
1037 /* length correct, nothing to do. */
1038 memcpy(bins, vdata, num_bins);
1039 break;
1040 case -1:
1041 /* first byte missing, duplicate it. */
1042 memcpy(&bins[1], vdata, num_bins - 1);
1043 bins[0] = vdata[0];
1044 break;
1045 case 2:
1046 /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
1047 memcpy(bins, vdata, 30);
1048 bins[30] = vdata[31];
1049 memcpy(&bins[31], &vdata[33], num_bins - 31);
1050 break;
1051 case 1:
1052 /* MAC added 2 extra bytes AND first byte is missing. */
1053 bins[0] = vdata[0];
1054 memcpy(&bins[1], vdata, 30);
1055 bins[31] = vdata[31];
1056 memcpy(&bins[32], &vdata[33], num_bins - 32);
1057 break;
1058 default:
1059 return 1;
1060 }
1061
1062 /* DC value (value in the middle) is the blind spot of the spectral
1063 * sample and invalid, interpolate it.
1064 */
1065 dc_pos = num_bins / 2;
1066 bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
1067
1068 if ((chan_type == NL80211_CHAN_HT40MINUS) ||
1069 (chan_type == NL80211_CHAN_HT40PLUS)) {
1070 s8 lower_rssi, upper_rssi;
1071 s16 ext_nf;
1072 u8 lower_max_index, upper_max_index;
1073 u8 lower_bitmap_w, upper_bitmap_w;
1074 u16 lower_mag, upper_mag;
1075 struct ath9k_hw_cal_data *caldata = ah->caldata;
1076 struct ath_ht20_40_mag_info *mag_info;
1077
1078 if (caldata)
1079 ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
1080 caldata->nfCalHist[3].privNF);
1081 else
1082 ext_nf = ATH_DEFAULT_NOISE_FLOOR;
1083
1084 length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
1085 fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
1086 fft_sample_40.tlv.length = __cpu_to_be16(length);
1087 fft_sample_40.freq = __cpu_to_be16(freq);
1088 fft_sample_40.channel_type = chan_type;
1089
1090 if (chan_type == NL80211_CHAN_HT40PLUS) {
1091 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
1092 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
1093
1094 fft_sample_40.lower_noise = ah->noise;
1095 fft_sample_40.upper_noise = ext_nf;
1096 } else {
1097 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
1098 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
1099
1100 fft_sample_40.lower_noise = ext_nf;
1101 fft_sample_40.upper_noise = ah->noise;
1102 }
1103 fft_sample_40.lower_rssi = lower_rssi;
1104 fft_sample_40.upper_rssi = upper_rssi;
1105
1106 mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
1107 lower_mag = spectral_max_magnitude(mag_info->lower_bins);
1108 upper_mag = spectral_max_magnitude(mag_info->upper_bins);
1109 fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
1110 fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
1111 lower_max_index = spectral_max_index(mag_info->lower_bins);
1112 upper_max_index = spectral_max_index(mag_info->upper_bins);
1113 fft_sample_40.lower_max_index = lower_max_index;
1114 fft_sample_40.upper_max_index = upper_max_index;
1115 lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
1116 upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
1117 fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
1118 fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
1119 fft_sample_40.max_exp = mag_info->max_exp & 0xf;
1120
1121 fft_sample_40.tsf = __cpu_to_be64(tsf);
1122
1123 tlv = (struct fft_sample_tlv *)&fft_sample_40;
1124 } else {
1125 u8 max_index, bitmap_w;
1126 u16 magnitude;
1127 struct ath_ht20_mag_info *mag_info;
1128
1129 length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
1130 fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
1131 fft_sample_20.tlv.length = __cpu_to_be16(length);
1132 fft_sample_20.freq = __cpu_to_be16(freq);
1133
1134 fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
1135 fft_sample_20.noise = ah->noise;
1136
1137 mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
1138 magnitude = spectral_max_magnitude(mag_info->all_bins);
1139 fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
1140 max_index = spectral_max_index(mag_info->all_bins);
1141 fft_sample_20.max_index = max_index;
1142 bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
1143 fft_sample_20.bitmap_weight = bitmap_w;
1144 fft_sample_20.max_exp = mag_info->max_exp & 0xf;
1145
1146 fft_sample_20.tsf = __cpu_to_be64(tsf);
1147
1148 tlv = (struct fft_sample_tlv *)&fft_sample_20;
1149 }
1150
1151 ath_debug_send_fft_sample(sc, tlv);
1152 return 1;
1153#else
1154 return 0;
1155#endif
1156}
1157
1158static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr) 977static bool ath9k_is_mybeacon(struct ath_softc *sc, struct ieee80211_hdr *hdr)
1159{ 978{
1160 struct ath_hw *ah = sc->sc_ah; 979 struct ath_hw *ah = sc->sc_ah;
diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/spectral.c
new file mode 100644
index 000000000000..11adb5eb51cc
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/spectral.c
@@ -0,0 +1,543 @@
1/*
2 * Copyright (c) 2013 Qualcomm Atheros, Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include <linux/relay.h>
18#include "ath9k.h"
19
20static s8 fix_rssi_inv_only(u8 rssi_val)
21{
22 if (rssi_val == 128)
23 rssi_val = 0;
24 return (s8) rssi_val;
25}
26
27static void ath_debug_send_fft_sample(struct ath_softc *sc,
28 struct fft_sample_tlv *fft_sample_tlv)
29{
30 int length;
31 if (!sc->rfs_chan_spec_scan)
32 return;
33
34 length = __be16_to_cpu(fft_sample_tlv->length) +
35 sizeof(*fft_sample_tlv);
36 relay_write(sc->rfs_chan_spec_scan, fft_sample_tlv, length);
37}
38
39/* returns 1 if this was a spectral frame, even if not handled. */
40int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
41 struct ath_rx_status *rs, u64 tsf)
42{
43 struct ath_hw *ah = sc->sc_ah;
44 u8 num_bins, *bins, *vdata = (u8 *)hdr;
45 struct fft_sample_ht20 fft_sample_20;
46 struct fft_sample_ht20_40 fft_sample_40;
47 struct fft_sample_tlv *tlv;
48 struct ath_radar_info *radar_info;
49 int len = rs->rs_datalen;
50 int dc_pos;
51 u16 fft_len, length, freq = ah->curchan->chan->center_freq;
52 enum nl80211_channel_type chan_type;
53
54 /* AR9280 and before report via ATH9K_PHYERR_RADAR, AR93xx and newer
55 * via ATH9K_PHYERR_SPECTRAL. Haven't seen ATH9K_PHYERR_FALSE_RADAR_EXT
56 * yet, but this is supposed to be possible as well.
57 */
58 if (rs->rs_phyerr != ATH9K_PHYERR_RADAR &&
59 rs->rs_phyerr != ATH9K_PHYERR_FALSE_RADAR_EXT &&
60 rs->rs_phyerr != ATH9K_PHYERR_SPECTRAL)
61 return 0;
62
63 /* check if spectral scan bit is set. This does not have to be checked
64 * if received through a SPECTRAL phy error, but shouldn't hurt.
65 */
66 radar_info = ((struct ath_radar_info *)&vdata[len]) - 1;
67 if (!(radar_info->pulse_bw_info & SPECTRAL_SCAN_BITMASK))
68 return 0;
69
70 chan_type = cfg80211_get_chandef_type(&sc->hw->conf.chandef);
71 if ((chan_type == NL80211_CHAN_HT40MINUS) ||
72 (chan_type == NL80211_CHAN_HT40PLUS)) {
73 fft_len = SPECTRAL_HT20_40_TOTAL_DATA_LEN;
74 num_bins = SPECTRAL_HT20_40_NUM_BINS;
75 bins = (u8 *)fft_sample_40.data;
76 } else {
77 fft_len = SPECTRAL_HT20_TOTAL_DATA_LEN;
78 num_bins = SPECTRAL_HT20_NUM_BINS;
79 bins = (u8 *)fft_sample_20.data;
80 }
81
82 /* Variation in the data length is possible and will be fixed later */
83 if ((len > fft_len + 2) || (len < fft_len - 1))
84 return 1;
85
86 switch (len - fft_len) {
87 case 0:
88 /* length correct, nothing to do. */
89 memcpy(bins, vdata, num_bins);
90 break;
91 case -1:
92 /* first byte missing, duplicate it. */
93 memcpy(&bins[1], vdata, num_bins - 1);
94 bins[0] = vdata[0];
95 break;
96 case 2:
97 /* MAC added 2 extra bytes at bin 30 and 32, remove them. */
98 memcpy(bins, vdata, 30);
99 bins[30] = vdata[31];
100 memcpy(&bins[31], &vdata[33], num_bins - 31);
101 break;
102 case 1:
103 /* MAC added 2 extra bytes AND first byte is missing. */
104 bins[0] = vdata[0];
105 memcpy(&bins[1], vdata, 30);
106 bins[31] = vdata[31];
107 memcpy(&bins[32], &vdata[33], num_bins - 32);
108 break;
109 default:
110 return 1;
111 }
112
113 /* DC value (value in the middle) is the blind spot of the spectral
114 * sample and invalid, interpolate it.
115 */
116 dc_pos = num_bins / 2;
117 bins[dc_pos] = (bins[dc_pos + 1] + bins[dc_pos - 1]) / 2;
118
119 if ((chan_type == NL80211_CHAN_HT40MINUS) ||
120 (chan_type == NL80211_CHAN_HT40PLUS)) {
121 s8 lower_rssi, upper_rssi;
122 s16 ext_nf;
123 u8 lower_max_index, upper_max_index;
124 u8 lower_bitmap_w, upper_bitmap_w;
125 u16 lower_mag, upper_mag;
126 struct ath9k_hw_cal_data *caldata = ah->caldata;
127 struct ath_ht20_40_mag_info *mag_info;
128
129 if (caldata)
130 ext_nf = ath9k_hw_getchan_noise(ah, ah->curchan,
131 caldata->nfCalHist[3].privNF);
132 else
133 ext_nf = ATH_DEFAULT_NOISE_FLOOR;
134
135 length = sizeof(fft_sample_40) - sizeof(struct fft_sample_tlv);
136 fft_sample_40.tlv.type = ATH_FFT_SAMPLE_HT20_40;
137 fft_sample_40.tlv.length = __cpu_to_be16(length);
138 fft_sample_40.freq = __cpu_to_be16(freq);
139 fft_sample_40.channel_type = chan_type;
140
141 if (chan_type == NL80211_CHAN_HT40PLUS) {
142 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
143 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
144
145 fft_sample_40.lower_noise = ah->noise;
146 fft_sample_40.upper_noise = ext_nf;
147 } else {
148 lower_rssi = fix_rssi_inv_only(rs->rs_rssi_ext[0]);
149 upper_rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
150
151 fft_sample_40.lower_noise = ext_nf;
152 fft_sample_40.upper_noise = ah->noise;
153 }
154 fft_sample_40.lower_rssi = lower_rssi;
155 fft_sample_40.upper_rssi = upper_rssi;
156
157 mag_info = ((struct ath_ht20_40_mag_info *)radar_info) - 1;
158 lower_mag = spectral_max_magnitude(mag_info->lower_bins);
159 upper_mag = spectral_max_magnitude(mag_info->upper_bins);
160 fft_sample_40.lower_max_magnitude = __cpu_to_be16(lower_mag);
161 fft_sample_40.upper_max_magnitude = __cpu_to_be16(upper_mag);
162 lower_max_index = spectral_max_index(mag_info->lower_bins);
163 upper_max_index = spectral_max_index(mag_info->upper_bins);
164 fft_sample_40.lower_max_index = lower_max_index;
165 fft_sample_40.upper_max_index = upper_max_index;
166 lower_bitmap_w = spectral_bitmap_weight(mag_info->lower_bins);
167 upper_bitmap_w = spectral_bitmap_weight(mag_info->upper_bins);
168 fft_sample_40.lower_bitmap_weight = lower_bitmap_w;
169 fft_sample_40.upper_bitmap_weight = upper_bitmap_w;
170 fft_sample_40.max_exp = mag_info->max_exp & 0xf;
171
172 fft_sample_40.tsf = __cpu_to_be64(tsf);
173
174 tlv = (struct fft_sample_tlv *)&fft_sample_40;
175 } else {
176 u8 max_index, bitmap_w;
177 u16 magnitude;
178 struct ath_ht20_mag_info *mag_info;
179
180 length = sizeof(fft_sample_20) - sizeof(struct fft_sample_tlv);
181 fft_sample_20.tlv.type = ATH_FFT_SAMPLE_HT20;
182 fft_sample_20.tlv.length = __cpu_to_be16(length);
183 fft_sample_20.freq = __cpu_to_be16(freq);
184
185 fft_sample_20.rssi = fix_rssi_inv_only(rs->rs_rssi_ctl[0]);
186 fft_sample_20.noise = ah->noise;
187
188 mag_info = ((struct ath_ht20_mag_info *)radar_info) - 1;
189 magnitude = spectral_max_magnitude(mag_info->all_bins);
190 fft_sample_20.max_magnitude = __cpu_to_be16(magnitude);
191 max_index = spectral_max_index(mag_info->all_bins);
192 fft_sample_20.max_index = max_index;
193 bitmap_w = spectral_bitmap_weight(mag_info->all_bins);
194 fft_sample_20.bitmap_weight = bitmap_w;
195 fft_sample_20.max_exp = mag_info->max_exp & 0xf;
196
197 fft_sample_20.tsf = __cpu_to_be64(tsf);
198
199 tlv = (struct fft_sample_tlv *)&fft_sample_20;
200 }
201
202 ath_debug_send_fft_sample(sc, tlv);
203
204 return 1;
205}
206
207/*********************/
208/* spectral_scan_ctl */
209/*********************/
210
211static ssize_t read_file_spec_scan_ctl(struct file *file, char __user *user_buf,
212 size_t count, loff_t *ppos)
213{
214 struct ath_softc *sc = file->private_data;
215 char *mode = "";
216 unsigned int len;
217
218 switch (sc->spectral_mode) {
219 case SPECTRAL_DISABLED:
220 mode = "disable";
221 break;
222 case SPECTRAL_BACKGROUND:
223 mode = "background";
224 break;
225 case SPECTRAL_CHANSCAN:
226 mode = "chanscan";
227 break;
228 case SPECTRAL_MANUAL:
229 mode = "manual";
230 break;
231 }
232 len = strlen(mode);
233 return simple_read_from_buffer(user_buf, count, ppos, mode, len);
234}
235
236static ssize_t write_file_spec_scan_ctl(struct file *file,
237 const char __user *user_buf,
238 size_t count, loff_t *ppos)
239{
240 struct ath_softc *sc = file->private_data;
241 struct ath_common *common = ath9k_hw_common(sc->sc_ah);
242 char buf[32];
243 ssize_t len;
244
245 if (config_enabled(CONFIG_ATH9K_TX99))
246 return -EOPNOTSUPP;
247
248 len = min(count, sizeof(buf) - 1);
249 if (copy_from_user(buf, user_buf, len))
250 return -EFAULT;
251
252 buf[len] = '\0';
253
254 if (strncmp("trigger", buf, 7) == 0) {
255 ath9k_spectral_scan_trigger(sc->hw);
256 } else if (strncmp("background", buf, 9) == 0) {
257 ath9k_spectral_scan_config(sc->hw, SPECTRAL_BACKGROUND);
258 ath_dbg(common, CONFIG, "spectral scan: background mode enabled\n");
259 } else if (strncmp("chanscan", buf, 8) == 0) {
260 ath9k_spectral_scan_config(sc->hw, SPECTRAL_CHANSCAN);
261 ath_dbg(common, CONFIG, "spectral scan: channel scan mode enabled\n");
262 } else if (strncmp("manual", buf, 6) == 0) {
263 ath9k_spectral_scan_config(sc->hw, SPECTRAL_MANUAL);
264 ath_dbg(common, CONFIG, "spectral scan: manual mode enabled\n");
265 } else if (strncmp("disable", buf, 7) == 0) {
266 ath9k_spectral_scan_config(sc->hw, SPECTRAL_DISABLED);
267 ath_dbg(common, CONFIG, "spectral scan: disabled\n");
268 } else {
269 return -EINVAL;
270 }
271
272 return count;
273}
274
275static const struct file_operations fops_spec_scan_ctl = {
276 .read = read_file_spec_scan_ctl,
277 .write = write_file_spec_scan_ctl,
278 .open = simple_open,
279 .owner = THIS_MODULE,
280 .llseek = default_llseek,
281};
282
283/*************************/
284/* spectral_short_repeat */
285/*************************/
286
287static ssize_t read_file_spectral_short_repeat(struct file *file,
288 char __user *user_buf,
289 size_t count, loff_t *ppos)
290{
291 struct ath_softc *sc = file->private_data;
292 char buf[32];
293 unsigned int len;
294
295 len = sprintf(buf, "%d\n", sc->spec_config.short_repeat);
296 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
297}
298
299static ssize_t write_file_spectral_short_repeat(struct file *file,
300 const char __user *user_buf,
301 size_t count, loff_t *ppos)
302{
303 struct ath_softc *sc = file->private_data;
304 unsigned long val;
305 char buf[32];
306 ssize_t len;
307
308 len = min(count, sizeof(buf) - 1);
309 if (copy_from_user(buf, user_buf, len))
310 return -EFAULT;
311
312 buf[len] = '\0';
313 if (kstrtoul(buf, 0, &val))
314 return -EINVAL;
315
316 if (val < 0 || val > 1)
317 return -EINVAL;
318
319 sc->spec_config.short_repeat = val;
320 return count;
321}
322
323static const struct file_operations fops_spectral_short_repeat = {
324 .read = read_file_spectral_short_repeat,
325 .write = write_file_spectral_short_repeat,
326 .open = simple_open,
327 .owner = THIS_MODULE,
328 .llseek = default_llseek,
329};
330
331/******************/
332/* spectral_count */
333/******************/
334
335static ssize_t read_file_spectral_count(struct file *file,
336 char __user *user_buf,
337 size_t count, loff_t *ppos)
338{
339 struct ath_softc *sc = file->private_data;
340 char buf[32];
341 unsigned int len;
342
343 len = sprintf(buf, "%d\n", sc->spec_config.count);
344 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
345}
346
347static ssize_t write_file_spectral_count(struct file *file,
348 const char __user *user_buf,
349 size_t count, loff_t *ppos)
350{
351 struct ath_softc *sc = file->private_data;
352 unsigned long val;
353 char buf[32];
354 ssize_t len;
355
356 len = min(count, sizeof(buf) - 1);
357 if (copy_from_user(buf, user_buf, len))
358 return -EFAULT;
359
360 buf[len] = '\0';
361 if (kstrtoul(buf, 0, &val))
362 return -EINVAL;
363
364 if (val < 0 || val > 255)
365 return -EINVAL;
366
367 sc->spec_config.count = val;
368 return count;
369}
370
371static const struct file_operations fops_spectral_count = {
372 .read = read_file_spectral_count,
373 .write = write_file_spectral_count,
374 .open = simple_open,
375 .owner = THIS_MODULE,
376 .llseek = default_llseek,
377};
378
379/*******************/
380/* spectral_period */
381/*******************/
382
383static ssize_t read_file_spectral_period(struct file *file,
384 char __user *user_buf,
385 size_t count, loff_t *ppos)
386{
387 struct ath_softc *sc = file->private_data;
388 char buf[32];
389 unsigned int len;
390
391 len = sprintf(buf, "%d\n", sc->spec_config.period);
392 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
393}
394
395static ssize_t write_file_spectral_period(struct file *file,
396 const char __user *user_buf,
397 size_t count, loff_t *ppos)
398{
399 struct ath_softc *sc = file->private_data;
400 unsigned long val;
401 char buf[32];
402 ssize_t len;
403
404 len = min(count, sizeof(buf) - 1);
405 if (copy_from_user(buf, user_buf, len))
406 return -EFAULT;
407
408 buf[len] = '\0';
409 if (kstrtoul(buf, 0, &val))
410 return -EINVAL;
411
412 if (val < 0 || val > 255)
413 return -EINVAL;
414
415 sc->spec_config.period = val;
416 return count;
417}
418
419static const struct file_operations fops_spectral_period = {
420 .read = read_file_spectral_period,
421 .write = write_file_spectral_period,
422 .open = simple_open,
423 .owner = THIS_MODULE,
424 .llseek = default_llseek,
425};
426
427/***********************/
428/* spectral_fft_period */
429/***********************/
430
431static ssize_t read_file_spectral_fft_period(struct file *file,
432 char __user *user_buf,
433 size_t count, loff_t *ppos)
434{
435 struct ath_softc *sc = file->private_data;
436 char buf[32];
437 unsigned int len;
438
439 len = sprintf(buf, "%d\n", sc->spec_config.fft_period);
440 return simple_read_from_buffer(user_buf, count, ppos, buf, len);
441}
442
443static ssize_t write_file_spectral_fft_period(struct file *file,
444 const char __user *user_buf,
445 size_t count, loff_t *ppos)
446{
447 struct ath_softc *sc = file->private_data;
448 unsigned long val;
449 char buf[32];
450 ssize_t len;
451
452 len = min(count, sizeof(buf) - 1);
453 if (copy_from_user(buf, user_buf, len))
454 return -EFAULT;
455
456 buf[len] = '\0';
457 if (kstrtoul(buf, 0, &val))
458 return -EINVAL;
459
460 if (val < 0 || val > 15)
461 return -EINVAL;
462
463 sc->spec_config.fft_period = val;
464 return count;
465}
466
467static const struct file_operations fops_spectral_fft_period = {
468 .read = read_file_spectral_fft_period,
469 .write = write_file_spectral_fft_period,
470 .open = simple_open,
471 .owner = THIS_MODULE,
472 .llseek = default_llseek,
473};
474
475/*******************/
476/* Relay interface */
477/*******************/
478
479static struct dentry *create_buf_file_handler(const char *filename,
480 struct dentry *parent,
481 umode_t mode,
482 struct rchan_buf *buf,
483 int *is_global)
484{
485 struct dentry *buf_file;
486
487 buf_file = debugfs_create_file(filename, mode, parent, buf,
488 &relay_file_operations);
489 *is_global = 1;
490 return buf_file;
491}
492
493static int remove_buf_file_handler(struct dentry *dentry)
494{
495 debugfs_remove(dentry);
496
497 return 0;
498}
499
500struct rchan_callbacks rfs_spec_scan_cb = {
501 .create_buf_file = create_buf_file_handler,
502 .remove_buf_file = remove_buf_file_handler,
503};
504
505/*********************/
506/* Debug Init/Deinit */
507/*********************/
508
509void ath9k_spectral_deinit_debug(struct ath_softc *sc)
510{
511 if (config_enabled(CONFIG_ATH9K_DEBUGFS) && sc->rfs_chan_spec_scan) {
512 relay_close(sc->rfs_chan_spec_scan);
513 sc->rfs_chan_spec_scan = NULL;
514 }
515}
516
517void ath9k_spectral_init_debug(struct ath_softc *sc)
518{
519 sc->rfs_chan_spec_scan = relay_open("spectral_scan",
520 sc->debug.debugfs_phy,
521 1024, 256, &rfs_spec_scan_cb,
522 NULL);
523 debugfs_create_file("spectral_scan_ctl",
524 S_IRUSR | S_IWUSR,
525 sc->debug.debugfs_phy, sc,
526 &fops_spec_scan_ctl);
527 debugfs_create_file("spectral_short_repeat",
528 S_IRUSR | S_IWUSR,
529 sc->debug.debugfs_phy, sc,
530 &fops_spectral_short_repeat);
531 debugfs_create_file("spectral_count",
532 S_IRUSR | S_IWUSR,
533 sc->debug.debugfs_phy, sc,
534 &fops_spectral_count);
535 debugfs_create_file("spectral_period",
536 S_IRUSR | S_IWUSR,
537 sc->debug.debugfs_phy, sc,
538 &fops_spectral_period);
539 debugfs_create_file("spectral_fft_period",
540 S_IRUSR | S_IWUSR,
541 sc->debug.debugfs_phy, sc,
542 &fops_spectral_fft_period);
543}
diff --git a/drivers/net/wireless/ath/ath9k/spectral.h b/drivers/net/wireless/ath/ath9k/spectral.h
new file mode 100644
index 000000000000..ead63412ee1a
--- /dev/null
+++ b/drivers/net/wireless/ath/ath9k/spectral.h
@@ -0,0 +1,212 @@
1/*
2 * Copyright (c) 2013 Qualcomm Atheros, Inc.
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#ifndef SPECTRAL_H
18#define SPECTRAL_H
19
20/* enum spectral_mode:
21 *
22 * @SPECTRAL_DISABLED: spectral mode is disabled
23 * @SPECTRAL_BACKGROUND: hardware sends samples when it is not busy with
24 * something else.
25 * @SPECTRAL_MANUAL: spectral scan is enabled, triggering for samples
26 * is performed manually.
27 * @SPECTRAL_CHANSCAN: Like manual, but also triggered when changing channels
28 * during a channel scan.
29 */
30enum spectral_mode {
31 SPECTRAL_DISABLED = 0,
32 SPECTRAL_BACKGROUND,
33 SPECTRAL_MANUAL,
34 SPECTRAL_CHANSCAN,
35};
36
37#define SPECTRAL_SCAN_BITMASK 0x10
38/* Radar info packet format, used for DFS and spectral formats. */
39struct ath_radar_info {
40 u8 pulse_length_pri;
41 u8 pulse_length_ext;
42 u8 pulse_bw_info;
43} __packed;
44
45/* The HT20 spectral data has 4 bytes of additional information at it's end.
46 *
47 * [7:0]: all bins {max_magnitude[1:0], bitmap_weight[5:0]}
48 * [7:0]: all bins max_magnitude[9:2]
49 * [7:0]: all bins {max_index[5:0], max_magnitude[11:10]}
50 * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
51 */
52struct ath_ht20_mag_info {
53 u8 all_bins[3];
54 u8 max_exp;
55} __packed;
56
57#define SPECTRAL_HT20_NUM_BINS 56
58
59/* WARNING: don't actually use this struct! MAC may vary the amount of
60 * data by -1/+2. This struct is for reference only.
61 */
62struct ath_ht20_fft_packet {
63 u8 data[SPECTRAL_HT20_NUM_BINS];
64 struct ath_ht20_mag_info mag_info;
65 struct ath_radar_info radar_info;
66} __packed;
67
68#define SPECTRAL_HT20_TOTAL_DATA_LEN (sizeof(struct ath_ht20_fft_packet))
69
70/* Dynamic 20/40 mode:
71 *
72 * [7:0]: lower bins {max_magnitude[1:0], bitmap_weight[5:0]}
73 * [7:0]: lower bins max_magnitude[9:2]
74 * [7:0]: lower bins {max_index[5:0], max_magnitude[11:10]}
75 * [7:0]: upper bins {max_magnitude[1:0], bitmap_weight[5:0]}
76 * [7:0]: upper bins max_magnitude[9:2]
77 * [7:0]: upper bins {max_index[5:0], max_magnitude[11:10]}
78 * [3:0]: max_exp (shift amount to size max bin to 8-bit unsigned)
79 */
80struct ath_ht20_40_mag_info {
81 u8 lower_bins[3];
82 u8 upper_bins[3];
83 u8 max_exp;
84} __packed;
85
86#define SPECTRAL_HT20_40_NUM_BINS 128
87
88/* WARNING: don't actually use this struct! MAC may vary the amount of
89 * data. This struct is for reference only.
90 */
91struct ath_ht20_40_fft_packet {
92 u8 data[SPECTRAL_HT20_40_NUM_BINS];
93 struct ath_ht20_40_mag_info mag_info;
94 struct ath_radar_info radar_info;
95} __packed;
96
97
98#define SPECTRAL_HT20_40_TOTAL_DATA_LEN (sizeof(struct ath_ht20_40_fft_packet))
99
100/* grabs the max magnitude from the all/upper/lower bins */
101static inline u16 spectral_max_magnitude(u8 *bins)
102{
103 return (bins[0] & 0xc0) >> 6 |
104 (bins[1] & 0xff) << 2 |
105 (bins[2] & 0x03) << 10;
106}
107
108/* return the max magnitude from the all/upper/lower bins */
109static inline u8 spectral_max_index(u8 *bins)
110{
111 s8 m = (bins[2] & 0xfc) >> 2;
112
113 /* TODO: this still doesn't always report the right values ... */
114 if (m > 32)
115 m |= 0xe0;
116 else
117 m &= ~0xe0;
118
119 return m + 29;
120}
121
122/* return the bitmap weight from the all/upper/lower bins */
123static inline u8 spectral_bitmap_weight(u8 *bins)
124{
125 return bins[0] & 0x3f;
126}
127
128/* FFT sample format given to userspace via debugfs.
129 *
130 * Please keep the type/length at the front position and change
131 * other fields after adding another sample type
132 *
133 * TODO: this might need rework when switching to nl80211-based
134 * interface.
135 */
136enum ath_fft_sample_type {
137 ATH_FFT_SAMPLE_HT20 = 1,
138 ATH_FFT_SAMPLE_HT20_40,
139};
140
141struct fft_sample_tlv {
142 u8 type; /* see ath_fft_sample */
143 __be16 length;
144 /* type dependent data follows */
145} __packed;
146
147struct fft_sample_ht20 {
148 struct fft_sample_tlv tlv;
149
150 u8 max_exp;
151
152 __be16 freq;
153 s8 rssi;
154 s8 noise;
155
156 __be16 max_magnitude;
157 u8 max_index;
158 u8 bitmap_weight;
159
160 __be64 tsf;
161
162 u8 data[SPECTRAL_HT20_NUM_BINS];
163} __packed;
164
165struct fft_sample_ht20_40 {
166 struct fft_sample_tlv tlv;
167
168 u8 channel_type;
169 __be16 freq;
170
171 s8 lower_rssi;
172 s8 upper_rssi;
173
174 __be64 tsf;
175
176 s8 lower_noise;
177 s8 upper_noise;
178
179 __be16 lower_max_magnitude;
180 __be16 upper_max_magnitude;
181
182 u8 lower_max_index;
183 u8 upper_max_index;
184
185 u8 lower_bitmap_weight;
186 u8 upper_bitmap_weight;
187
188 u8 max_exp;
189
190 u8 data[SPECTRAL_HT20_40_NUM_BINS];
191} __packed;
192
193void ath9k_spectral_init_debug(struct ath_softc *sc);
194void ath9k_spectral_deinit_debug(struct ath_softc *sc);
195
196void ath9k_spectral_scan_trigger(struct ieee80211_hw *hw);
197int ath9k_spectral_scan_config(struct ieee80211_hw *hw,
198 enum spectral_mode spectral_mode);
199
200#ifdef CONFIG_ATH9K_DEBUGFS
201int ath_process_fft(struct ath_softc *sc, struct ieee80211_hdr *hdr,
202 struct ath_rx_status *rs, u64 tsf);
203#else
204static inline int ath_process_fft(struct ath_softc *sc,
205 struct ieee80211_hdr *hdr,
206 struct ath_rx_status *rs, u64 tsf)
207{
208 return 0;
209}
210#endif /* CONFIG_ATH9K_DEBUGFS */
211
212#endif /* SPECTRAL_H */