diff options
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi')
27 files changed, 10546 insertions, 0 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/Kconfig b/drivers/net/wireless/iwmc3200wifi/Kconfig new file mode 100644 index 00000000000..03f998d098c --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/Kconfig | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | config IWM | ||
| 2 | tristate "Intel Wireless Multicomm 3200 WiFi driver" | ||
| 3 | depends on MMC && EXPERIMENTAL | ||
| 4 | depends on CFG80211 | ||
| 5 | select FW_LOADER | ||
| 6 | select IWMC3200TOP | ||
| 7 | help | ||
| 8 | The Intel Wireless Multicomm 3200 hardware is a combo | ||
| 9 | card with GPS, Bluetooth, WiMax and 802.11 radios. It | ||
| 10 | runs over SDIO and is typically found on Moorestown | ||
| 11 | based platform. This driver takes care of the 802.11 | ||
| 12 | part, which is a fullmac one. | ||
| 13 | |||
| 14 | If you choose to build it as a module, it'll be called | ||
| 15 | iwmc3200wifi.ko. | ||
| 16 | |||
| 17 | config IWM_DEBUG | ||
| 18 | bool "Enable full debugging output in iwmc3200wifi" | ||
| 19 | depends on IWM && DEBUG_FS | ||
| 20 | help | ||
| 21 | This option will enable debug tracing and setting for iwm | ||
| 22 | |||
| 23 | You can set the debug level and module through debugfs. By | ||
| 24 | default all modules are set to the IWL_DL_ERR level. | ||
| 25 | To see the list of debug modules and levels, see iwm/debug.h | ||
| 26 | |||
| 27 | For example, if you want the full MLME debug output: | ||
| 28 | echo 0xff > /sys/kernel/debug/iwm/phyN/debug/mlme | ||
| 29 | |||
| 30 | Or, if you want the full debug, for all modules: | ||
| 31 | echo 0xff > /sys/kernel/debug/iwm/phyN/debug/level | ||
| 32 | echo 0xff > /sys/kernel/debug/iwm/phyN/debug/modules | ||
| 33 | |||
| 34 | config IWM_TRACING | ||
| 35 | bool "Enable event tracing for iwmc3200wifi" | ||
| 36 | depends on IWM && EVENT_TRACING | ||
| 37 | help | ||
| 38 | Say Y here to trace all the commands and responses between | ||
| 39 | the driver and firmware (including TX/RX frames) with ftrace. | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/Makefile b/drivers/net/wireless/iwmc3200wifi/Makefile new file mode 100644 index 00000000000..cdc7e07ba11 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/Makefile | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | obj-$(CONFIG_IWM) := iwmc3200wifi.o | ||
| 2 | iwmc3200wifi-objs += main.o netdev.o rx.o tx.o sdio.o hal.o fw.o | ||
| 3 | iwmc3200wifi-objs += commands.o cfg80211.o eeprom.o | ||
| 4 | |||
| 5 | iwmc3200wifi-$(CONFIG_IWM_DEBUG) += debugfs.o | ||
| 6 | iwmc3200wifi-$(CONFIG_IWM_TRACING) += trace.o | ||
| 7 | |||
| 8 | CFLAGS_trace.o := -I$(src) | ||
| 9 | |||
| 10 | ccflags-y += -D__CHECK_ENDIAN__ | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/bus.h b/drivers/net/wireless/iwmc3200wifi/bus.h new file mode 100644 index 00000000000..62edd5888a7 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/bus.h | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> | ||
| 5 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 6 | * Zhu Yi <yi.zhu@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #ifndef __IWM_BUS_H__ | ||
| 25 | #define __IWM_BUS_H__ | ||
| 26 | |||
| 27 | #include "iwm.h" | ||
| 28 | |||
| 29 | struct iwm_if_ops { | ||
| 30 | int (*enable)(struct iwm_priv *iwm); | ||
| 31 | int (*disable)(struct iwm_priv *iwm); | ||
| 32 | int (*send_chunk)(struct iwm_priv *iwm, u8* buf, int count); | ||
| 33 | |||
| 34 | void (*debugfs_init)(struct iwm_priv *iwm, struct dentry *parent_dir); | ||
| 35 | void (*debugfs_exit)(struct iwm_priv *iwm); | ||
| 36 | |||
| 37 | const char *umac_name; | ||
| 38 | const char *calib_lmac_name; | ||
| 39 | const char *lmac_name; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static inline int iwm_bus_send_chunk(struct iwm_priv *iwm, u8 *buf, int count) | ||
| 43 | { | ||
| 44 | return iwm->bus_ops->send_chunk(iwm, buf, count); | ||
| 45 | } | ||
| 46 | |||
| 47 | static inline int iwm_bus_enable(struct iwm_priv *iwm) | ||
| 48 | { | ||
| 49 | return iwm->bus_ops->enable(iwm); | ||
| 50 | } | ||
| 51 | |||
| 52 | static inline int iwm_bus_disable(struct iwm_priv *iwm) | ||
| 53 | { | ||
| 54 | return iwm->bus_ops->disable(iwm); | ||
| 55 | } | ||
| 56 | |||
| 57 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c new file mode 100644 index 00000000000..ed57e440280 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c | |||
| @@ -0,0 +1,867 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> | ||
| 5 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 6 | * Zhu Yi <yi.zhu@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/kernel.h> | ||
| 25 | #include <linux/netdevice.h> | ||
| 26 | #include <linux/sched.h> | ||
| 27 | #include <linux/etherdevice.h> | ||
| 28 | #include <linux/wireless.h> | ||
| 29 | #include <linux/ieee80211.h> | ||
| 30 | #include <linux/slab.h> | ||
| 31 | #include <net/cfg80211.h> | ||
| 32 | |||
| 33 | #include "iwm.h" | ||
| 34 | #include "commands.h" | ||
| 35 | #include "cfg80211.h" | ||
| 36 | #include "debug.h" | ||
| 37 | |||
| 38 | #define RATETAB_ENT(_rate, _rateid, _flags) \ | ||
| 39 | { \ | ||
| 40 | .bitrate = (_rate), \ | ||
| 41 | .hw_value = (_rateid), \ | ||
| 42 | .flags = (_flags), \ | ||
| 43 | } | ||
| 44 | |||
| 45 | #define CHAN2G(_channel, _freq, _flags) { \ | ||
| 46 | .band = IEEE80211_BAND_2GHZ, \ | ||
| 47 | .center_freq = (_freq), \ | ||
| 48 | .hw_value = (_channel), \ | ||
| 49 | .flags = (_flags), \ | ||
| 50 | .max_antenna_gain = 0, \ | ||
| 51 | .max_power = 30, \ | ||
| 52 | } | ||
| 53 | |||
| 54 | #define CHAN5G(_channel, _flags) { \ | ||
| 55 | .band = IEEE80211_BAND_5GHZ, \ | ||
| 56 | .center_freq = 5000 + (5 * (_channel)), \ | ||
| 57 | .hw_value = (_channel), \ | ||
| 58 | .flags = (_flags), \ | ||
| 59 | .max_antenna_gain = 0, \ | ||
| 60 | .max_power = 30, \ | ||
| 61 | } | ||
| 62 | |||
| 63 | static struct ieee80211_rate iwm_rates[] = { | ||
| 64 | RATETAB_ENT(10, 0x1, 0), | ||
| 65 | RATETAB_ENT(20, 0x2, 0), | ||
| 66 | RATETAB_ENT(55, 0x4, 0), | ||
| 67 | RATETAB_ENT(110, 0x8, 0), | ||
| 68 | RATETAB_ENT(60, 0x10, 0), | ||
| 69 | RATETAB_ENT(90, 0x20, 0), | ||
| 70 | RATETAB_ENT(120, 0x40, 0), | ||
| 71 | RATETAB_ENT(180, 0x80, 0), | ||
| 72 | RATETAB_ENT(240, 0x100, 0), | ||
| 73 | RATETAB_ENT(360, 0x200, 0), | ||
| 74 | RATETAB_ENT(480, 0x400, 0), | ||
| 75 | RATETAB_ENT(540, 0x800, 0), | ||
| 76 | }; | ||
| 77 | |||
| 78 | #define iwm_a_rates (iwm_rates + 4) | ||
| 79 | #define iwm_a_rates_size 8 | ||
| 80 | #define iwm_g_rates (iwm_rates + 0) | ||
| 81 | #define iwm_g_rates_size 12 | ||
| 82 | |||
| 83 | static struct ieee80211_channel iwm_2ghz_channels[] = { | ||
| 84 | CHAN2G(1, 2412, 0), | ||
| 85 | CHAN2G(2, 2417, 0), | ||
| 86 | CHAN2G(3, 2422, 0), | ||
| 87 | CHAN2G(4, 2427, 0), | ||
| 88 | CHAN2G(5, 2432, 0), | ||
| 89 | CHAN2G(6, 2437, 0), | ||
| 90 | CHAN2G(7, 2442, 0), | ||
| 91 | CHAN2G(8, 2447, 0), | ||
| 92 | CHAN2G(9, 2452, 0), | ||
| 93 | CHAN2G(10, 2457, 0), | ||
| 94 | CHAN2G(11, 2462, 0), | ||
| 95 | CHAN2G(12, 2467, 0), | ||
| 96 | CHAN2G(13, 2472, 0), | ||
| 97 | CHAN2G(14, 2484, 0), | ||
| 98 | }; | ||
| 99 | |||
| 100 | static struct ieee80211_channel iwm_5ghz_a_channels[] = { | ||
| 101 | CHAN5G(34, 0), CHAN5G(36, 0), | ||
| 102 | CHAN5G(38, 0), CHAN5G(40, 0), | ||
| 103 | CHAN5G(42, 0), CHAN5G(44, 0), | ||
| 104 | CHAN5G(46, 0), CHAN5G(48, 0), | ||
| 105 | CHAN5G(52, 0), CHAN5G(56, 0), | ||
| 106 | CHAN5G(60, 0), CHAN5G(64, 0), | ||
| 107 | CHAN5G(100, 0), CHAN5G(104, 0), | ||
| 108 | CHAN5G(108, 0), CHAN5G(112, 0), | ||
| 109 | CHAN5G(116, 0), CHAN5G(120, 0), | ||
| 110 | CHAN5G(124, 0), CHAN5G(128, 0), | ||
| 111 | CHAN5G(132, 0), CHAN5G(136, 0), | ||
| 112 | CHAN5G(140, 0), CHAN5G(149, 0), | ||
| 113 | CHAN5G(153, 0), CHAN5G(157, 0), | ||
| 114 | CHAN5G(161, 0), CHAN5G(165, 0), | ||
| 115 | CHAN5G(184, 0), CHAN5G(188, 0), | ||
| 116 | CHAN5G(192, 0), CHAN5G(196, 0), | ||
| 117 | CHAN5G(200, 0), CHAN5G(204, 0), | ||
| 118 | CHAN5G(208, 0), CHAN5G(212, 0), | ||
| 119 | CHAN5G(216, 0), | ||
| 120 | }; | ||
| 121 | |||
| 122 | static struct ieee80211_supported_band iwm_band_2ghz = { | ||
| 123 | .channels = iwm_2ghz_channels, | ||
| 124 | .n_channels = ARRAY_SIZE(iwm_2ghz_channels), | ||
| 125 | .bitrates = iwm_g_rates, | ||
| 126 | .n_bitrates = iwm_g_rates_size, | ||
| 127 | }; | ||
| 128 | |||
| 129 | static struct ieee80211_supported_band iwm_band_5ghz = { | ||
| 130 | .channels = iwm_5ghz_a_channels, | ||
| 131 | .n_channels = ARRAY_SIZE(iwm_5ghz_a_channels), | ||
| 132 | .bitrates = iwm_a_rates, | ||
| 133 | .n_bitrates = iwm_a_rates_size, | ||
| 134 | }; | ||
| 135 | |||
| 136 | static int iwm_key_init(struct iwm_key *key, u8 key_index, | ||
| 137 | const u8 *mac_addr, struct key_params *params) | ||
| 138 | { | ||
| 139 | key->hdr.key_idx = key_index; | ||
| 140 | if (!mac_addr || is_broadcast_ether_addr(mac_addr)) { | ||
| 141 | key->hdr.multicast = 1; | ||
| 142 | memset(key->hdr.mac, 0xff, ETH_ALEN); | ||
| 143 | } else { | ||
| 144 | key->hdr.multicast = 0; | ||
| 145 | memcpy(key->hdr.mac, mac_addr, ETH_ALEN); | ||
| 146 | } | ||
| 147 | |||
| 148 | if (params) { | ||
| 149 | if (params->key_len > WLAN_MAX_KEY_LEN || | ||
| 150 | params->seq_len > IW_ENCODE_SEQ_MAX_SIZE) | ||
| 151 | return -EINVAL; | ||
| 152 | |||
| 153 | key->cipher = params->cipher; | ||
| 154 | key->key_len = params->key_len; | ||
| 155 | key->seq_len = params->seq_len; | ||
| 156 | memcpy(key->key, params->key, key->key_len); | ||
| 157 | memcpy(key->seq, params->seq, key->seq_len); | ||
| 158 | } | ||
| 159 | |||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | static int iwm_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, | ||
| 164 | u8 key_index, bool pairwise, const u8 *mac_addr, | ||
| 165 | struct key_params *params) | ||
| 166 | { | ||
| 167 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 168 | struct iwm_key *key = &iwm->keys[key_index]; | ||
| 169 | int ret; | ||
| 170 | |||
| 171 | IWM_DBG_WEXT(iwm, DBG, "Adding key for %pM\n", mac_addr); | ||
| 172 | |||
| 173 | memset(key, 0, sizeof(struct iwm_key)); | ||
| 174 | ret = iwm_key_init(key, key_index, mac_addr, params); | ||
| 175 | if (ret < 0) { | ||
| 176 | IWM_ERR(iwm, "Invalid key_params\n"); | ||
| 177 | return ret; | ||
| 178 | } | ||
| 179 | |||
| 180 | return iwm_set_key(iwm, 0, key); | ||
| 181 | } | ||
| 182 | |||
| 183 | static int iwm_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, | ||
| 184 | u8 key_index, bool pairwise, const u8 *mac_addr, | ||
| 185 | void *cookie, | ||
| 186 | void (*callback)(void *cookie, | ||
| 187 | struct key_params*)) | ||
| 188 | { | ||
| 189 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 190 | struct iwm_key *key = &iwm->keys[key_index]; | ||
| 191 | struct key_params params; | ||
| 192 | |||
| 193 | IWM_DBG_WEXT(iwm, DBG, "Getting key %d\n", key_index); | ||
| 194 | |||
| 195 | memset(¶ms, 0, sizeof(params)); | ||
| 196 | |||
| 197 | params.cipher = key->cipher; | ||
| 198 | params.key_len = key->key_len; | ||
| 199 | params.seq_len = key->seq_len; | ||
| 200 | params.seq = key->seq; | ||
| 201 | params.key = key->key; | ||
| 202 | |||
| 203 | callback(cookie, ¶ms); | ||
| 204 | |||
| 205 | return key->key_len ? 0 : -ENOENT; | ||
| 206 | } | ||
| 207 | |||
| 208 | |||
| 209 | static int iwm_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, | ||
| 210 | u8 key_index, bool pairwise, const u8 *mac_addr) | ||
| 211 | { | ||
| 212 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 213 | struct iwm_key *key = &iwm->keys[key_index]; | ||
| 214 | |||
| 215 | if (!iwm->keys[key_index].key_len) { | ||
| 216 | IWM_DBG_WEXT(iwm, DBG, "Key %d not used\n", key_index); | ||
| 217 | return 0; | ||
| 218 | } | ||
| 219 | |||
| 220 | if (key_index == iwm->default_key) | ||
| 221 | iwm->default_key = -1; | ||
| 222 | |||
| 223 | return iwm_set_key(iwm, 1, key); | ||
| 224 | } | ||
| 225 | |||
| 226 | static int iwm_cfg80211_set_default_key(struct wiphy *wiphy, | ||
| 227 | struct net_device *ndev, | ||
| 228 | u8 key_index, bool unicast, | ||
| 229 | bool multicast) | ||
| 230 | { | ||
| 231 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 232 | |||
| 233 | IWM_DBG_WEXT(iwm, DBG, "Default key index is: %d\n", key_index); | ||
| 234 | |||
| 235 | if (!iwm->keys[key_index].key_len) { | ||
| 236 | IWM_ERR(iwm, "Key %d not used\n", key_index); | ||
| 237 | return -EINVAL; | ||
| 238 | } | ||
| 239 | |||
| 240 | iwm->default_key = key_index; | ||
| 241 | |||
| 242 | return iwm_set_tx_key(iwm, key_index); | ||
| 243 | } | ||
| 244 | |||
| 245 | static int iwm_cfg80211_get_station(struct wiphy *wiphy, | ||
| 246 | struct net_device *ndev, | ||
| 247 | u8 *mac, struct station_info *sinfo) | ||
| 248 | { | ||
| 249 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 250 | |||
| 251 | if (memcmp(mac, iwm->bssid, ETH_ALEN)) | ||
| 252 | return -ENOENT; | ||
| 253 | |||
| 254 | sinfo->filled |= STATION_INFO_TX_BITRATE; | ||
| 255 | sinfo->txrate.legacy = iwm->rate * 10; | ||
| 256 | |||
| 257 | if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { | ||
| 258 | sinfo->filled |= STATION_INFO_SIGNAL; | ||
| 259 | sinfo->signal = iwm->wstats.qual.level; | ||
| 260 | } | ||
| 261 | |||
| 262 | return 0; | ||
| 263 | } | ||
| 264 | |||
| 265 | |||
| 266 | int iwm_cfg80211_inform_bss(struct iwm_priv *iwm) | ||
| 267 | { | ||
| 268 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 269 | struct iwm_bss_info *bss; | ||
| 270 | struct iwm_umac_notif_bss_info *umac_bss; | ||
| 271 | struct ieee80211_mgmt *mgmt; | ||
| 272 | struct ieee80211_channel *channel; | ||
| 273 | struct ieee80211_supported_band *band; | ||
| 274 | s32 signal; | ||
| 275 | int freq; | ||
| 276 | |||
| 277 | list_for_each_entry(bss, &iwm->bss_list, node) { | ||
| 278 | umac_bss = bss->bss; | ||
| 279 | mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf); | ||
| 280 | |||
| 281 | if (umac_bss->band == UMAC_BAND_2GHZ) | ||
| 282 | band = wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
| 283 | else if (umac_bss->band == UMAC_BAND_5GHZ) | ||
| 284 | band = wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
| 285 | else { | ||
| 286 | IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band); | ||
| 287 | return -EINVAL; | ||
| 288 | } | ||
| 289 | |||
| 290 | freq = ieee80211_channel_to_frequency(umac_bss->channel, | ||
| 291 | band->band); | ||
| 292 | channel = ieee80211_get_channel(wiphy, freq); | ||
| 293 | signal = umac_bss->rssi * 100; | ||
| 294 | |||
| 295 | if (!cfg80211_inform_bss_frame(wiphy, channel, mgmt, | ||
| 296 | le16_to_cpu(umac_bss->frame_len), | ||
| 297 | signal, GFP_KERNEL)) | ||
| 298 | return -EINVAL; | ||
| 299 | } | ||
| 300 | |||
| 301 | return 0; | ||
| 302 | } | ||
| 303 | |||
| 304 | static int iwm_cfg80211_change_iface(struct wiphy *wiphy, | ||
| 305 | struct net_device *ndev, | ||
| 306 | enum nl80211_iftype type, u32 *flags, | ||
| 307 | struct vif_params *params) | ||
| 308 | { | ||
| 309 | struct wireless_dev *wdev; | ||
| 310 | struct iwm_priv *iwm; | ||
| 311 | u32 old_mode; | ||
| 312 | |||
| 313 | wdev = ndev->ieee80211_ptr; | ||
| 314 | iwm = ndev_to_iwm(ndev); | ||
| 315 | old_mode = iwm->conf.mode; | ||
| 316 | |||
| 317 | switch (type) { | ||
| 318 | case NL80211_IFTYPE_STATION: | ||
| 319 | iwm->conf.mode = UMAC_MODE_BSS; | ||
| 320 | break; | ||
| 321 | case NL80211_IFTYPE_ADHOC: | ||
| 322 | iwm->conf.mode = UMAC_MODE_IBSS; | ||
| 323 | break; | ||
| 324 | default: | ||
| 325 | return -EOPNOTSUPP; | ||
| 326 | } | ||
| 327 | |||
| 328 | wdev->iftype = type; | ||
| 329 | |||
| 330 | if ((old_mode == iwm->conf.mode) || !iwm->umac_profile) | ||
| 331 | return 0; | ||
| 332 | |||
| 333 | iwm->umac_profile->mode = cpu_to_le32(iwm->conf.mode); | ||
| 334 | |||
| 335 | if (iwm->umac_profile_active) | ||
| 336 | iwm_invalidate_mlme_profile(iwm); | ||
| 337 | |||
| 338 | return 0; | ||
| 339 | } | ||
| 340 | |||
| 341 | static int iwm_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev, | ||
| 342 | struct cfg80211_scan_request *request) | ||
| 343 | { | ||
| 344 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 345 | int ret; | ||
| 346 | |||
| 347 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) { | ||
| 348 | IWM_ERR(iwm, "Scan while device is not ready\n"); | ||
| 349 | return -EIO; | ||
| 350 | } | ||
| 351 | |||
| 352 | if (test_bit(IWM_STATUS_SCANNING, &iwm->status)) { | ||
| 353 | IWM_ERR(iwm, "Scanning already\n"); | ||
| 354 | return -EAGAIN; | ||
| 355 | } | ||
| 356 | |||
| 357 | if (test_bit(IWM_STATUS_SCAN_ABORTING, &iwm->status)) { | ||
| 358 | IWM_ERR(iwm, "Scanning being aborted\n"); | ||
| 359 | return -EAGAIN; | ||
| 360 | } | ||
| 361 | |||
| 362 | set_bit(IWM_STATUS_SCANNING, &iwm->status); | ||
| 363 | |||
| 364 | ret = iwm_scan_ssids(iwm, request->ssids, request->n_ssids); | ||
| 365 | if (ret) { | ||
| 366 | clear_bit(IWM_STATUS_SCANNING, &iwm->status); | ||
| 367 | return ret; | ||
| 368 | } | ||
| 369 | |||
| 370 | iwm->scan_request = request; | ||
| 371 | return 0; | ||
| 372 | } | ||
| 373 | |||
| 374 | static int iwm_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) | ||
| 375 | { | ||
| 376 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 377 | |||
| 378 | if (changed & WIPHY_PARAM_RTS_THRESHOLD && | ||
| 379 | (iwm->conf.rts_threshold != wiphy->rts_threshold)) { | ||
| 380 | int ret; | ||
| 381 | |||
| 382 | iwm->conf.rts_threshold = wiphy->rts_threshold; | ||
| 383 | |||
| 384 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 385 | CFG_RTS_THRESHOLD, | ||
| 386 | iwm->conf.rts_threshold); | ||
| 387 | if (ret < 0) | ||
| 388 | return ret; | ||
| 389 | } | ||
| 390 | |||
| 391 | if (changed & WIPHY_PARAM_FRAG_THRESHOLD && | ||
| 392 | (iwm->conf.frag_threshold != wiphy->frag_threshold)) { | ||
| 393 | int ret; | ||
| 394 | |||
| 395 | iwm->conf.frag_threshold = wiphy->frag_threshold; | ||
| 396 | |||
| 397 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX, | ||
| 398 | CFG_FRAG_THRESHOLD, | ||
| 399 | iwm->conf.frag_threshold); | ||
| 400 | if (ret < 0) | ||
| 401 | return ret; | ||
| 402 | } | ||
| 403 | |||
| 404 | return 0; | ||
| 405 | } | ||
| 406 | |||
| 407 | static int iwm_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, | ||
| 408 | struct cfg80211_ibss_params *params) | ||
| 409 | { | ||
| 410 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 411 | struct ieee80211_channel *chan = params->channel; | ||
| 412 | |||
| 413 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | ||
| 414 | return -EIO; | ||
| 415 | |||
| 416 | /* UMAC doesn't support creating or joining an IBSS network | ||
| 417 | * with specified bssid. */ | ||
| 418 | if (params->bssid) | ||
| 419 | return -EOPNOTSUPP; | ||
| 420 | |||
| 421 | iwm->channel = ieee80211_frequency_to_channel(chan->center_freq); | ||
| 422 | iwm->umac_profile->ibss.band = chan->band; | ||
| 423 | iwm->umac_profile->ibss.channel = iwm->channel; | ||
| 424 | iwm->umac_profile->ssid.ssid_len = params->ssid_len; | ||
| 425 | memcpy(iwm->umac_profile->ssid.ssid, params->ssid, params->ssid_len); | ||
| 426 | |||
| 427 | return iwm_send_mlme_profile(iwm); | ||
| 428 | } | ||
| 429 | |||
| 430 | static int iwm_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) | ||
| 431 | { | ||
| 432 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 433 | |||
| 434 | if (iwm->umac_profile_active) | ||
| 435 | return iwm_invalidate_mlme_profile(iwm); | ||
| 436 | |||
| 437 | return 0; | ||
| 438 | } | ||
| 439 | |||
| 440 | static int iwm_set_auth_type(struct iwm_priv *iwm, | ||
| 441 | enum nl80211_auth_type sme_auth_type) | ||
| 442 | { | ||
| 443 | u8 *auth_type = &iwm->umac_profile->sec.auth_type; | ||
| 444 | |||
| 445 | switch (sme_auth_type) { | ||
| 446 | case NL80211_AUTHTYPE_AUTOMATIC: | ||
| 447 | case NL80211_AUTHTYPE_OPEN_SYSTEM: | ||
| 448 | IWM_DBG_WEXT(iwm, DBG, "OPEN auth\n"); | ||
| 449 | *auth_type = UMAC_AUTH_TYPE_OPEN; | ||
| 450 | break; | ||
| 451 | case NL80211_AUTHTYPE_SHARED_KEY: | ||
| 452 | if (iwm->umac_profile->sec.flags & | ||
| 453 | (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) { | ||
| 454 | IWM_DBG_WEXT(iwm, DBG, "WPA auth alg\n"); | ||
| 455 | *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; | ||
| 456 | } else { | ||
| 457 | IWM_DBG_WEXT(iwm, DBG, "WEP shared key auth alg\n"); | ||
| 458 | *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; | ||
| 459 | } | ||
| 460 | |||
| 461 | break; | ||
| 462 | default: | ||
| 463 | IWM_ERR(iwm, "Unsupported auth alg: 0x%x\n", sme_auth_type); | ||
| 464 | return -ENOTSUPP; | ||
| 465 | } | ||
| 466 | |||
| 467 | return 0; | ||
| 468 | } | ||
| 469 | |||
| 470 | static int iwm_set_wpa_version(struct iwm_priv *iwm, u32 wpa_version) | ||
| 471 | { | ||
| 472 | IWM_DBG_WEXT(iwm, DBG, "wpa_version: %d\n", wpa_version); | ||
| 473 | |||
| 474 | if (!wpa_version) { | ||
| 475 | iwm->umac_profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE; | ||
| 476 | return 0; | ||
| 477 | } | ||
| 478 | |||
| 479 | if (wpa_version & NL80211_WPA_VERSION_1) | ||
| 480 | iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WPA_ON_MSK; | ||
| 481 | |||
| 482 | if (wpa_version & NL80211_WPA_VERSION_2) | ||
| 483 | iwm->umac_profile->sec.flags = UMAC_SEC_FLG_RSNA_ON_MSK; | ||
| 484 | |||
| 485 | return 0; | ||
| 486 | } | ||
| 487 | |||
| 488 | static int iwm_set_cipher(struct iwm_priv *iwm, u32 cipher, bool ucast) | ||
| 489 | { | ||
| 490 | u8 *profile_cipher = ucast ? &iwm->umac_profile->sec.ucast_cipher : | ||
| 491 | &iwm->umac_profile->sec.mcast_cipher; | ||
| 492 | |||
| 493 | if (!cipher) { | ||
| 494 | *profile_cipher = UMAC_CIPHER_TYPE_NONE; | ||
| 495 | return 0; | ||
| 496 | } | ||
| 497 | |||
| 498 | IWM_DBG_WEXT(iwm, DBG, "%ccast cipher is 0x%x\n", ucast ? 'u' : 'm', | ||
| 499 | cipher); | ||
| 500 | |||
| 501 | switch (cipher) { | ||
| 502 | case IW_AUTH_CIPHER_NONE: | ||
| 503 | *profile_cipher = UMAC_CIPHER_TYPE_NONE; | ||
| 504 | break; | ||
| 505 | case WLAN_CIPHER_SUITE_WEP40: | ||
| 506 | *profile_cipher = UMAC_CIPHER_TYPE_WEP_40; | ||
| 507 | break; | ||
| 508 | case WLAN_CIPHER_SUITE_WEP104: | ||
| 509 | *profile_cipher = UMAC_CIPHER_TYPE_WEP_104; | ||
| 510 | break; | ||
| 511 | case WLAN_CIPHER_SUITE_TKIP: | ||
| 512 | *profile_cipher = UMAC_CIPHER_TYPE_TKIP; | ||
| 513 | break; | ||
| 514 | case WLAN_CIPHER_SUITE_CCMP: | ||
| 515 | *profile_cipher = UMAC_CIPHER_TYPE_CCMP; | ||
| 516 | break; | ||
| 517 | default: | ||
| 518 | IWM_ERR(iwm, "Unsupported cipher: 0x%x\n", cipher); | ||
| 519 | return -ENOTSUPP; | ||
| 520 | } | ||
| 521 | |||
| 522 | return 0; | ||
| 523 | } | ||
| 524 | |||
| 525 | static int iwm_set_key_mgt(struct iwm_priv *iwm, u32 key_mgt) | ||
| 526 | { | ||
| 527 | u8 *auth_type = &iwm->umac_profile->sec.auth_type; | ||
| 528 | |||
| 529 | IWM_DBG_WEXT(iwm, DBG, "key_mgt: 0x%x\n", key_mgt); | ||
| 530 | |||
| 531 | if (key_mgt == WLAN_AKM_SUITE_8021X) | ||
| 532 | *auth_type = UMAC_AUTH_TYPE_8021X; | ||
| 533 | else if (key_mgt == WLAN_AKM_SUITE_PSK) { | ||
| 534 | if (iwm->umac_profile->sec.flags & | ||
| 535 | (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) | ||
| 536 | *auth_type = UMAC_AUTH_TYPE_RSNA_PSK; | ||
| 537 | else | ||
| 538 | *auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; | ||
| 539 | } else { | ||
| 540 | IWM_ERR(iwm, "Invalid key mgt: 0x%x\n", key_mgt); | ||
| 541 | return -EINVAL; | ||
| 542 | } | ||
| 543 | |||
| 544 | return 0; | ||
| 545 | } | ||
| 546 | |||
| 547 | |||
| 548 | static int iwm_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, | ||
| 549 | struct cfg80211_connect_params *sme) | ||
| 550 | { | ||
| 551 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 552 | struct ieee80211_channel *chan = sme->channel; | ||
| 553 | struct key_params key_param; | ||
| 554 | int ret; | ||
| 555 | |||
| 556 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | ||
| 557 | return -EIO; | ||
| 558 | |||
| 559 | if (!sme->ssid) | ||
| 560 | return -EINVAL; | ||
| 561 | |||
| 562 | if (iwm->umac_profile_active) { | ||
| 563 | ret = iwm_invalidate_mlme_profile(iwm); | ||
| 564 | if (ret) { | ||
| 565 | IWM_ERR(iwm, "Couldn't invalidate profile\n"); | ||
| 566 | return ret; | ||
| 567 | } | ||
| 568 | } | ||
| 569 | |||
| 570 | if (chan) | ||
| 571 | iwm->channel = | ||
| 572 | ieee80211_frequency_to_channel(chan->center_freq); | ||
| 573 | |||
| 574 | iwm->umac_profile->ssid.ssid_len = sme->ssid_len; | ||
| 575 | memcpy(iwm->umac_profile->ssid.ssid, sme->ssid, sme->ssid_len); | ||
| 576 | |||
| 577 | if (sme->bssid) { | ||
| 578 | IWM_DBG_WEXT(iwm, DBG, "BSSID: %pM\n", sme->bssid); | ||
| 579 | memcpy(&iwm->umac_profile->bssid[0], sme->bssid, ETH_ALEN); | ||
| 580 | iwm->umac_profile->bss_num = 1; | ||
| 581 | } else { | ||
| 582 | memset(&iwm->umac_profile->bssid[0], 0, ETH_ALEN); | ||
| 583 | iwm->umac_profile->bss_num = 0; | ||
| 584 | } | ||
| 585 | |||
| 586 | ret = iwm_set_wpa_version(iwm, sme->crypto.wpa_versions); | ||
| 587 | if (ret < 0) | ||
| 588 | return ret; | ||
| 589 | |||
| 590 | ret = iwm_set_auth_type(iwm, sme->auth_type); | ||
| 591 | if (ret < 0) | ||
| 592 | return ret; | ||
| 593 | |||
| 594 | if (sme->crypto.n_ciphers_pairwise) { | ||
| 595 | ret = iwm_set_cipher(iwm, sme->crypto.ciphers_pairwise[0], | ||
| 596 | true); | ||
| 597 | if (ret < 0) | ||
| 598 | return ret; | ||
| 599 | } | ||
| 600 | |||
| 601 | ret = iwm_set_cipher(iwm, sme->crypto.cipher_group, false); | ||
| 602 | if (ret < 0) | ||
| 603 | return ret; | ||
| 604 | |||
| 605 | if (sme->crypto.n_akm_suites) { | ||
| 606 | ret = iwm_set_key_mgt(iwm, sme->crypto.akm_suites[0]); | ||
| 607 | if (ret < 0) | ||
| 608 | return ret; | ||
| 609 | } | ||
| 610 | |||
| 611 | /* | ||
| 612 | * We save the WEP key in case we want to do shared authentication. | ||
| 613 | * We have to do it so because UMAC will assert whenever it gets a | ||
| 614 | * key before a profile. | ||
| 615 | */ | ||
| 616 | if (sme->key) { | ||
| 617 | key_param.key = kmemdup(sme->key, sme->key_len, GFP_KERNEL); | ||
| 618 | if (key_param.key == NULL) | ||
| 619 | return -ENOMEM; | ||
| 620 | key_param.key_len = sme->key_len; | ||
| 621 | key_param.seq_len = 0; | ||
| 622 | key_param.cipher = sme->crypto.ciphers_pairwise[0]; | ||
| 623 | |||
| 624 | ret = iwm_key_init(&iwm->keys[sme->key_idx], sme->key_idx, | ||
| 625 | NULL, &key_param); | ||
| 626 | kfree(key_param.key); | ||
| 627 | if (ret < 0) { | ||
| 628 | IWM_ERR(iwm, "Invalid key_params\n"); | ||
| 629 | return ret; | ||
| 630 | } | ||
| 631 | |||
| 632 | iwm->default_key = sme->key_idx; | ||
| 633 | } | ||
| 634 | |||
| 635 | /* WPA and open AUTH type from wpa_s means WPS (a.k.a. WSC) */ | ||
| 636 | if ((iwm->umac_profile->sec.flags & | ||
| 637 | (UMAC_SEC_FLG_WPA_ON_MSK | UMAC_SEC_FLG_RSNA_ON_MSK)) && | ||
| 638 | iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN) { | ||
| 639 | iwm->umac_profile->sec.flags = UMAC_SEC_FLG_WSC_ON_MSK; | ||
| 640 | } | ||
| 641 | |||
| 642 | ret = iwm_send_mlme_profile(iwm); | ||
| 643 | |||
| 644 | if (iwm->umac_profile->sec.auth_type != UMAC_AUTH_TYPE_LEGACY_PSK || | ||
| 645 | sme->key == NULL) | ||
| 646 | return ret; | ||
| 647 | |||
| 648 | /* | ||
| 649 | * We want to do shared auth. | ||
| 650 | * We need to actually set the key we previously cached, | ||
| 651 | * and then tell the UMAC it's the default one. | ||
| 652 | * That will trigger the auth+assoc UMAC machinery, and again, | ||
| 653 | * this must be done after setting the profile. | ||
| 654 | */ | ||
| 655 | ret = iwm_set_key(iwm, 0, &iwm->keys[sme->key_idx]); | ||
| 656 | if (ret < 0) | ||
| 657 | return ret; | ||
| 658 | |||
| 659 | return iwm_set_tx_key(iwm, iwm->default_key); | ||
| 660 | } | ||
| 661 | |||
| 662 | static int iwm_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, | ||
| 663 | u16 reason_code) | ||
| 664 | { | ||
| 665 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 666 | |||
| 667 | IWM_DBG_WEXT(iwm, DBG, "Active: %d\n", iwm->umac_profile_active); | ||
| 668 | |||
| 669 | if (iwm->umac_profile_active) | ||
| 670 | iwm_invalidate_mlme_profile(iwm); | ||
| 671 | |||
| 672 | return 0; | ||
| 673 | } | ||
| 674 | |||
| 675 | static int iwm_cfg80211_set_txpower(struct wiphy *wiphy, | ||
| 676 | enum nl80211_tx_power_setting type, int mbm) | ||
| 677 | { | ||
| 678 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 679 | int ret; | ||
| 680 | |||
| 681 | switch (type) { | ||
| 682 | case NL80211_TX_POWER_AUTOMATIC: | ||
| 683 | return 0; | ||
| 684 | case NL80211_TX_POWER_FIXED: | ||
| 685 | if (mbm < 0 || (mbm % 100)) | ||
| 686 | return -EOPNOTSUPP; | ||
| 687 | |||
| 688 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | ||
| 689 | return 0; | ||
| 690 | |||
| 691 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 692 | CFG_TX_PWR_LIMIT_USR, | ||
| 693 | MBM_TO_DBM(mbm) * 2); | ||
| 694 | if (ret < 0) | ||
| 695 | return ret; | ||
| 696 | |||
| 697 | return iwm_tx_power_trigger(iwm); | ||
| 698 | default: | ||
| 699 | IWM_ERR(iwm, "Unsupported power type: %d\n", type); | ||
| 700 | return -EOPNOTSUPP; | ||
| 701 | } | ||
| 702 | |||
| 703 | return 0; | ||
| 704 | } | ||
| 705 | |||
| 706 | static int iwm_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) | ||
| 707 | { | ||
| 708 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 709 | |||
| 710 | *dbm = iwm->txpower >> 1; | ||
| 711 | |||
| 712 | return 0; | ||
| 713 | } | ||
| 714 | |||
| 715 | static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy, | ||
| 716 | struct net_device *dev, | ||
| 717 | bool enabled, int timeout) | ||
| 718 | { | ||
| 719 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 720 | u32 power_index; | ||
| 721 | |||
| 722 | if (enabled) | ||
| 723 | power_index = IWM_POWER_INDEX_DEFAULT; | ||
| 724 | else | ||
| 725 | power_index = IWM_POWER_INDEX_MIN; | ||
| 726 | |||
| 727 | if (power_index == iwm->conf.power_index) | ||
| 728 | return 0; | ||
| 729 | |||
| 730 | iwm->conf.power_index = power_index; | ||
| 731 | |||
| 732 | return iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 733 | CFG_POWER_INDEX, iwm->conf.power_index); | ||
| 734 | } | ||
| 735 | |||
| 736 | static int iwm_cfg80211_set_pmksa(struct wiphy *wiphy, | ||
| 737 | struct net_device *netdev, | ||
| 738 | struct cfg80211_pmksa *pmksa) | ||
| 739 | { | ||
| 740 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 741 | |||
| 742 | return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD); | ||
| 743 | } | ||
| 744 | |||
| 745 | static int iwm_cfg80211_del_pmksa(struct wiphy *wiphy, | ||
| 746 | struct net_device *netdev, | ||
| 747 | struct cfg80211_pmksa *pmksa) | ||
| 748 | { | ||
| 749 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 750 | |||
| 751 | return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL); | ||
| 752 | } | ||
| 753 | |||
| 754 | static int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy, | ||
| 755 | struct net_device *netdev) | ||
| 756 | { | ||
| 757 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
| 758 | struct cfg80211_pmksa pmksa; | ||
| 759 | |||
| 760 | memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); | ||
| 761 | |||
| 762 | return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH); | ||
| 763 | } | ||
| 764 | |||
| 765 | |||
| 766 | static struct cfg80211_ops iwm_cfg80211_ops = { | ||
| 767 | .change_virtual_intf = iwm_cfg80211_change_iface, | ||
| 768 | .add_key = iwm_cfg80211_add_key, | ||
| 769 | .get_key = iwm_cfg80211_get_key, | ||
| 770 | .del_key = iwm_cfg80211_del_key, | ||
| 771 | .set_default_key = iwm_cfg80211_set_default_key, | ||
| 772 | .get_station = iwm_cfg80211_get_station, | ||
| 773 | .scan = iwm_cfg80211_scan, | ||
| 774 | .set_wiphy_params = iwm_cfg80211_set_wiphy_params, | ||
| 775 | .connect = iwm_cfg80211_connect, | ||
| 776 | .disconnect = iwm_cfg80211_disconnect, | ||
| 777 | .join_ibss = iwm_cfg80211_join_ibss, | ||
| 778 | .leave_ibss = iwm_cfg80211_leave_ibss, | ||
| 779 | .set_tx_power = iwm_cfg80211_set_txpower, | ||
| 780 | .get_tx_power = iwm_cfg80211_get_txpower, | ||
| 781 | .set_power_mgmt = iwm_cfg80211_set_power_mgmt, | ||
| 782 | .set_pmksa = iwm_cfg80211_set_pmksa, | ||
| 783 | .del_pmksa = iwm_cfg80211_del_pmksa, | ||
| 784 | .flush_pmksa = iwm_cfg80211_flush_pmksa, | ||
| 785 | }; | ||
| 786 | |||
| 787 | static const u32 cipher_suites[] = { | ||
| 788 | WLAN_CIPHER_SUITE_WEP40, | ||
| 789 | WLAN_CIPHER_SUITE_WEP104, | ||
| 790 | WLAN_CIPHER_SUITE_TKIP, | ||
| 791 | WLAN_CIPHER_SUITE_CCMP, | ||
| 792 | }; | ||
| 793 | |||
| 794 | struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) | ||
| 795 | { | ||
| 796 | int ret = 0; | ||
| 797 | struct wireless_dev *wdev; | ||
| 798 | |||
| 799 | /* | ||
| 800 | * We're trying to have the following memory | ||
| 801 | * layout: | ||
| 802 | * | ||
| 803 | * +-------------------------+ | ||
| 804 | * | struct wiphy | | ||
| 805 | * +-------------------------+ | ||
| 806 | * | struct iwm_priv | | ||
| 807 | * +-------------------------+ | ||
| 808 | * | bus private data | | ||
| 809 | * | (e.g. iwm_priv_sdio) | | ||
| 810 | * +-------------------------+ | ||
| 811 | * | ||
| 812 | */ | ||
| 813 | |||
| 814 | wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); | ||
| 815 | if (!wdev) { | ||
| 816 | dev_err(dev, "Couldn't allocate wireless device\n"); | ||
| 817 | return ERR_PTR(-ENOMEM); | ||
| 818 | } | ||
| 819 | |||
| 820 | wdev->wiphy = wiphy_new(&iwm_cfg80211_ops, | ||
| 821 | sizeof(struct iwm_priv) + sizeof_bus); | ||
| 822 | if (!wdev->wiphy) { | ||
| 823 | dev_err(dev, "Couldn't allocate wiphy device\n"); | ||
| 824 | ret = -ENOMEM; | ||
| 825 | goto out_err_new; | ||
| 826 | } | ||
| 827 | |||
| 828 | set_wiphy_dev(wdev->wiphy, dev); | ||
| 829 | wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX; | ||
| 830 | wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS; | ||
| 831 | wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | ||
| 832 | BIT(NL80211_IFTYPE_ADHOC); | ||
| 833 | wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz; | ||
| 834 | wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &iwm_band_5ghz; | ||
| 835 | wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | ||
| 836 | |||
| 837 | wdev->wiphy->cipher_suites = cipher_suites; | ||
| 838 | wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); | ||
| 839 | |||
| 840 | ret = wiphy_register(wdev->wiphy); | ||
| 841 | if (ret < 0) { | ||
| 842 | dev_err(dev, "Couldn't register wiphy device\n"); | ||
| 843 | goto out_err_register; | ||
| 844 | } | ||
| 845 | |||
| 846 | return wdev; | ||
| 847 | |||
| 848 | out_err_register: | ||
| 849 | wiphy_free(wdev->wiphy); | ||
| 850 | |||
| 851 | out_err_new: | ||
| 852 | kfree(wdev); | ||
| 853 | |||
| 854 | return ERR_PTR(ret); | ||
| 855 | } | ||
| 856 | |||
| 857 | void iwm_wdev_free(struct iwm_priv *iwm) | ||
| 858 | { | ||
| 859 | struct wireless_dev *wdev = iwm_to_wdev(iwm); | ||
| 860 | |||
| 861 | if (!wdev) | ||
| 862 | return; | ||
| 863 | |||
| 864 | wiphy_unregister(wdev->wiphy); | ||
| 865 | wiphy_free(wdev->wiphy); | ||
| 866 | kfree(wdev); | ||
| 867 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.h b/drivers/net/wireless/iwmc3200wifi/cfg80211.h new file mode 100644 index 00000000000..56a34145acb --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> | ||
| 5 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 6 | * Zhu Yi <yi.zhu@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #ifndef __IWM_CFG80211_H__ | ||
| 25 | #define __IWM_CFG80211_H__ | ||
| 26 | |||
| 27 | int iwm_cfg80211_inform_bss(struct iwm_priv *iwm); | ||
| 28 | struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev); | ||
| 29 | void iwm_wdev_free(struct iwm_priv *iwm); | ||
| 30 | |||
| 31 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c new file mode 100644 index 00000000000..50dee6a0a5c --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/commands.c | |||
| @@ -0,0 +1,1001 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #include <linux/kernel.h> | ||
| 40 | #include <linux/wireless.h> | ||
| 41 | #include <linux/etherdevice.h> | ||
| 42 | #include <linux/ieee80211.h> | ||
| 43 | #include <linux/sched.h> | ||
| 44 | #include <linux/slab.h> | ||
| 45 | |||
| 46 | #include "iwm.h" | ||
| 47 | #include "bus.h" | ||
| 48 | #include "hal.h" | ||
| 49 | #include "umac.h" | ||
| 50 | #include "commands.h" | ||
| 51 | #include "debug.h" | ||
| 52 | |||
| 53 | static int iwm_send_lmac_ptrough_cmd(struct iwm_priv *iwm, | ||
| 54 | u8 lmac_cmd_id, | ||
| 55 | const void *lmac_payload, | ||
| 56 | u16 lmac_payload_size, | ||
| 57 | u8 resp) | ||
| 58 | { | ||
| 59 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_LMAC_INIT; | ||
| 60 | struct iwm_umac_cmd umac_cmd; | ||
| 61 | struct iwm_lmac_cmd lmac_cmd; | ||
| 62 | |||
| 63 | lmac_cmd.id = lmac_cmd_id; | ||
| 64 | |||
| 65 | umac_cmd.id = UMAC_CMD_OPCODE_WIFI_PASS_THROUGH; | ||
| 66 | umac_cmd.resp = resp; | ||
| 67 | |||
| 68 | return iwm_hal_send_host_cmd(iwm, &udma_cmd, &umac_cmd, &lmac_cmd, | ||
| 69 | lmac_payload, lmac_payload_size); | ||
| 70 | } | ||
| 71 | |||
| 72 | int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, | ||
| 73 | bool resp) | ||
| 74 | { | ||
| 75 | struct iwm_umac_wifi_if *hdr = (struct iwm_umac_wifi_if *)payload; | ||
| 76 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
| 77 | struct iwm_umac_cmd umac_cmd; | ||
| 78 | int ret; | ||
| 79 | u8 oid = hdr->oid; | ||
| 80 | |||
| 81 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) { | ||
| 82 | IWM_ERR(iwm, "Interface is not ready yet"); | ||
| 83 | return -EAGAIN; | ||
| 84 | } | ||
| 85 | |||
| 86 | umac_cmd.id = UMAC_CMD_OPCODE_WIFI_IF_WRAPPER; | ||
| 87 | umac_cmd.resp = resp; | ||
| 88 | |||
| 89 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, | ||
| 90 | payload, payload_size); | ||
| 91 | |||
| 92 | if (resp) { | ||
| 93 | ret = wait_event_interruptible_timeout(iwm->wifi_ntfy_queue, | ||
| 94 | test_and_clear_bit(oid, &iwm->wifi_ntfy[0]), | ||
| 95 | 3 * HZ); | ||
| 96 | |||
| 97 | return ret ? 0 : -EBUSY; | ||
| 98 | } | ||
| 99 | |||
| 100 | return ret; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int modparam_wiwi = COEX_MODE_CM; | ||
| 104 | module_param_named(wiwi, modparam_wiwi, int, 0644); | ||
| 105 | MODULE_PARM_DESC(wiwi, "Wifi-WiMAX coexistence: 1=SA, 2=XOR, 3=CM (default)"); | ||
| 106 | |||
| 107 | static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = | ||
| 108 | { | ||
| 109 | {4, 3, 0, COEX_UNASSOC_IDLE_FLAGS}, | ||
| 110 | {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, | ||
| 111 | {4, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, | ||
| 112 | {4, 3, 0, COEX_CALIBRATION_FLAGS}, | ||
| 113 | {4, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, | ||
| 114 | {4, 3, 0, COEX_CONNECTION_ESTAB_FLAGS}, | ||
| 115 | {4, 3, 0, COEX_ASSOCIATED_IDLE_FLAGS}, | ||
| 116 | {4, 3, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, | ||
| 117 | {4, 3, 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, | ||
| 118 | {4, 3, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, | ||
| 119 | {6, 3, 0, COEX_XOR_RF_ON_FLAGS}, | ||
| 120 | {4, 3, 0, COEX_RF_OFF_FLAGS}, | ||
| 121 | {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, | ||
| 122 | {4, 3, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, | ||
| 123 | {4, 3, 0, COEX_RSRVD1_FLAGS}, | ||
| 124 | {4, 3, 0, COEX_RSRVD2_FLAGS} | ||
| 125 | }; | ||
| 126 | |||
| 127 | static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] = | ||
| 128 | { | ||
| 129 | {1, 1, 0, COEX_UNASSOC_IDLE_FLAGS}, | ||
| 130 | {4, 4, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, | ||
| 131 | {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, | ||
| 132 | {6, 6, 0, COEX_CALIBRATION_FLAGS}, | ||
| 133 | {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, | ||
| 134 | {6, 5, 0, COEX_CONNECTION_ESTAB_FLAGS}, | ||
| 135 | {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS}, | ||
| 136 | {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, | ||
| 137 | {4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, | ||
| 138 | {4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, | ||
| 139 | {1, 1, 0, COEX_RF_ON_FLAGS}, | ||
| 140 | {1, 1, 0, COEX_RF_OFF_FLAGS}, | ||
| 141 | {7, 7, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, | ||
| 142 | {5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, | ||
| 143 | {1, 1, 0, COEX_RSRVD1_FLAGS}, | ||
| 144 | {1, 1, 0, COEX_RSRVD2_FLAGS} | ||
| 145 | }; | ||
| 146 | |||
| 147 | int iwm_send_prio_table(struct iwm_priv *iwm) | ||
| 148 | { | ||
| 149 | struct iwm_coex_prio_table_cmd coex_table_cmd; | ||
| 150 | u32 coex_enabled, mode_enabled; | ||
| 151 | |||
| 152 | memset(&coex_table_cmd, 0, sizeof(struct iwm_coex_prio_table_cmd)); | ||
| 153 | |||
| 154 | coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK; | ||
| 155 | |||
| 156 | switch (modparam_wiwi) { | ||
| 157 | case COEX_MODE_XOR: | ||
| 158 | case COEX_MODE_CM: | ||
| 159 | coex_enabled = 1; | ||
| 160 | break; | ||
| 161 | default: | ||
| 162 | coex_enabled = 0; | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | |||
| 166 | switch (iwm->conf.mode) { | ||
| 167 | case UMAC_MODE_BSS: | ||
| 168 | case UMAC_MODE_IBSS: | ||
| 169 | mode_enabled = 1; | ||
| 170 | break; | ||
| 171 | default: | ||
| 172 | mode_enabled = 0; | ||
| 173 | break; | ||
| 174 | } | ||
| 175 | |||
| 176 | if (coex_enabled && mode_enabled) { | ||
| 177 | coex_table_cmd.flags |= COEX_FLAGS_COEX_ENABLE_MSK | | ||
| 178 | COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK | | ||
| 179 | COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK; | ||
| 180 | |||
| 181 | switch (modparam_wiwi) { | ||
| 182 | case COEX_MODE_XOR: | ||
| 183 | memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl, | ||
| 184 | sizeof(iwm_sta_xor_prio_tbl)); | ||
| 185 | break; | ||
| 186 | case COEX_MODE_CM: | ||
| 187 | memcpy(coex_table_cmd.sta_prio, iwm_sta_cm_prio_tbl, | ||
| 188 | sizeof(iwm_sta_cm_prio_tbl)); | ||
| 189 | break; | ||
| 190 | default: | ||
| 191 | IWM_ERR(iwm, "Invalid coex_mode 0x%x\n", | ||
| 192 | modparam_wiwi); | ||
| 193 | break; | ||
| 194 | } | ||
| 195 | } else | ||
| 196 | IWM_WARN(iwm, "coexistense disabled\n"); | ||
| 197 | |||
| 198 | return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD, | ||
| 199 | &coex_table_cmd, | ||
| 200 | sizeof(struct iwm_coex_prio_table_cmd), 0); | ||
| 201 | } | ||
| 202 | |||
| 203 | int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested) | ||
| 204 | { | ||
| 205 | struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd; | ||
| 206 | |||
| 207 | memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd)); | ||
| 208 | |||
| 209 | cal_cfg_cmd.ucode_cfg.init.enable = cpu_to_le32(calib_requested); | ||
| 210 | cal_cfg_cmd.ucode_cfg.init.start = cpu_to_le32(calib_requested); | ||
| 211 | cal_cfg_cmd.ucode_cfg.init.send_res = cpu_to_le32(calib_requested); | ||
| 212 | cal_cfg_cmd.ucode_cfg.flags = | ||
| 213 | cpu_to_le32(CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK); | ||
| 214 | |||
| 215 | return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd, | ||
| 216 | sizeof(struct iwm_lmac_cal_cfg_cmd), 1); | ||
| 217 | } | ||
| 218 | |||
| 219 | int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested) | ||
| 220 | { | ||
| 221 | struct iwm_lmac_cal_cfg_cmd cal_cfg_cmd; | ||
| 222 | |||
| 223 | memset(&cal_cfg_cmd, 0, sizeof(struct iwm_lmac_cal_cfg_cmd)); | ||
| 224 | |||
| 225 | cal_cfg_cmd.ucode_cfg.periodic.enable = cpu_to_le32(calib_requested); | ||
| 226 | cal_cfg_cmd.ucode_cfg.periodic.start = cpu_to_le32(calib_requested); | ||
| 227 | |||
| 228 | return iwm_send_lmac_ptrough_cmd(iwm, CALIBRATION_CFG_CMD, &cal_cfg_cmd, | ||
| 229 | sizeof(struct iwm_lmac_cal_cfg_cmd), 0); | ||
| 230 | } | ||
| 231 | |||
| 232 | int iwm_store_rxiq_calib_result(struct iwm_priv *iwm) | ||
| 233 | { | ||
| 234 | struct iwm_calib_rxiq *rxiq; | ||
| 235 | u8 *eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ); | ||
| 236 | int grplen = sizeof(struct iwm_calib_rxiq_group); | ||
| 237 | |||
| 238 | rxiq = kzalloc(sizeof(struct iwm_calib_rxiq), GFP_KERNEL); | ||
| 239 | if (!rxiq) { | ||
| 240 | IWM_ERR(iwm, "Couldn't alloc memory for RX IQ\n"); | ||
| 241 | return -ENOMEM; | ||
| 242 | } | ||
| 243 | |||
| 244 | eeprom_rxiq = iwm_eeprom_access(iwm, IWM_EEPROM_CALIB_RXIQ); | ||
| 245 | if (IS_ERR(eeprom_rxiq)) { | ||
| 246 | IWM_ERR(iwm, "Couldn't access EEPROM RX IQ entry\n"); | ||
| 247 | kfree(rxiq); | ||
| 248 | return PTR_ERR(eeprom_rxiq); | ||
| 249 | } | ||
| 250 | |||
| 251 | iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].buf = (u8 *)rxiq; | ||
| 252 | iwm->calib_res[SHILOH_PHY_CALIBRATE_RX_IQ_CMD].size = sizeof(*rxiq); | ||
| 253 | |||
| 254 | rxiq->hdr.opcode = SHILOH_PHY_CALIBRATE_RX_IQ_CMD; | ||
| 255 | rxiq->hdr.first_grp = 0; | ||
| 256 | rxiq->hdr.grp_num = 1; | ||
| 257 | rxiq->hdr.all_data_valid = 1; | ||
| 258 | |||
| 259 | memcpy(&rxiq->group[0], eeprom_rxiq, 4 * grplen); | ||
| 260 | memcpy(&rxiq->group[4], eeprom_rxiq + 6 * grplen, grplen); | ||
| 261 | |||
| 262 | return 0; | ||
| 263 | } | ||
| 264 | |||
| 265 | int iwm_send_calib_results(struct iwm_priv *iwm) | ||
| 266 | { | ||
| 267 | int i, ret = 0; | ||
| 268 | |||
| 269 | for (i = PHY_CALIBRATE_OPCODES_NUM; i < CALIBRATION_CMD_NUM; i++) { | ||
| 270 | if (test_bit(i - PHY_CALIBRATE_OPCODES_NUM, | ||
| 271 | &iwm->calib_done_map)) { | ||
| 272 | IWM_DBG_CMD(iwm, DBG, | ||
| 273 | "Send calibration %d result\n", i); | ||
| 274 | ret |= iwm_send_lmac_ptrough_cmd(iwm, | ||
| 275 | REPLY_PHY_CALIBRATION_CMD, | ||
| 276 | iwm->calib_res[i].buf, | ||
| 277 | iwm->calib_res[i].size, 0); | ||
| 278 | |||
| 279 | kfree(iwm->calib_res[i].buf); | ||
| 280 | iwm->calib_res[i].buf = NULL; | ||
| 281 | iwm->calib_res[i].size = 0; | ||
| 282 | } | ||
| 283 | } | ||
| 284 | |||
| 285 | return ret; | ||
| 286 | } | ||
| 287 | |||
| 288 | int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit) | ||
| 289 | { | ||
| 290 | struct iwm_ct_kill_cfg_cmd cmd; | ||
| 291 | |||
| 292 | cmd.entry_threshold = entry; | ||
| 293 | cmd.exit_threshold = exit; | ||
| 294 | |||
| 295 | return iwm_send_lmac_ptrough_cmd(iwm, REPLY_CT_KILL_CONFIG_CMD, &cmd, | ||
| 296 | sizeof(struct iwm_ct_kill_cfg_cmd), 0); | ||
| 297 | } | ||
| 298 | |||
| 299 | int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp) | ||
| 300 | { | ||
| 301 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
| 302 | struct iwm_umac_cmd umac_cmd; | ||
| 303 | struct iwm_umac_cmd_reset reset; | ||
| 304 | |||
| 305 | reset.flags = reset_flags; | ||
| 306 | |||
| 307 | umac_cmd.id = UMAC_CMD_OPCODE_RESET; | ||
| 308 | umac_cmd.resp = resp; | ||
| 309 | |||
| 310 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &reset, | ||
| 311 | sizeof(struct iwm_umac_cmd_reset)); | ||
| 312 | } | ||
| 313 | |||
| 314 | int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value) | ||
| 315 | { | ||
| 316 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
| 317 | struct iwm_umac_cmd umac_cmd; | ||
| 318 | struct iwm_umac_cmd_set_param_fix param; | ||
| 319 | |||
| 320 | if ((tbl != UMAC_PARAM_TBL_CFG_FIX) && | ||
| 321 | (tbl != UMAC_PARAM_TBL_FA_CFG_FIX)) | ||
| 322 | return -EINVAL; | ||
| 323 | |||
| 324 | umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_FIX; | ||
| 325 | umac_cmd.resp = 0; | ||
| 326 | |||
| 327 | param.tbl = cpu_to_le16(tbl); | ||
| 328 | param.key = cpu_to_le16(key); | ||
| 329 | param.value = cpu_to_le32(value); | ||
| 330 | |||
| 331 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ¶m, | ||
| 332 | sizeof(struct iwm_umac_cmd_set_param_fix)); | ||
| 333 | } | ||
| 334 | |||
| 335 | int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key, | ||
| 336 | void *payload, u16 payload_size) | ||
| 337 | { | ||
| 338 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
| 339 | struct iwm_umac_cmd umac_cmd; | ||
| 340 | struct iwm_umac_cmd_set_param_var *param_hdr; | ||
| 341 | u8 *param; | ||
| 342 | int ret; | ||
| 343 | |||
| 344 | param = kzalloc(payload_size + | ||
| 345 | sizeof(struct iwm_umac_cmd_set_param_var), GFP_KERNEL); | ||
| 346 | if (!param) { | ||
| 347 | IWM_ERR(iwm, "Couldn't allocate param\n"); | ||
| 348 | return -ENOMEM; | ||
| 349 | } | ||
| 350 | |||
| 351 | param_hdr = (struct iwm_umac_cmd_set_param_var *)param; | ||
| 352 | |||
| 353 | umac_cmd.id = UMAC_CMD_OPCODE_SET_PARAM_VAR; | ||
| 354 | umac_cmd.resp = 0; | ||
| 355 | |||
| 356 | param_hdr->tbl = cpu_to_le16(UMAC_PARAM_TBL_CFG_VAR); | ||
| 357 | param_hdr->key = cpu_to_le16(key); | ||
| 358 | param_hdr->len = cpu_to_le16(payload_size); | ||
| 359 | memcpy(param + sizeof(struct iwm_umac_cmd_set_param_var), | ||
| 360 | payload, payload_size); | ||
| 361 | |||
| 362 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, param, | ||
| 363 | sizeof(struct iwm_umac_cmd_set_param_var) + | ||
| 364 | payload_size); | ||
| 365 | kfree(param); | ||
| 366 | |||
| 367 | return ret; | ||
| 368 | } | ||
| 369 | |||
| 370 | int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags) | ||
| 371 | { | ||
| 372 | int ret; | ||
| 373 | |||
| 374 | /* Use UMAC default values */ | ||
| 375 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 376 | CFG_POWER_INDEX, iwm->conf.power_index); | ||
| 377 | if (ret < 0) | ||
| 378 | return ret; | ||
| 379 | |||
| 380 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_FA_CFG_FIX, | ||
| 381 | CFG_FRAG_THRESHOLD, | ||
| 382 | iwm->conf.frag_threshold); | ||
| 383 | if (ret < 0) | ||
| 384 | return ret; | ||
| 385 | |||
| 386 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 387 | CFG_RTS_THRESHOLD, | ||
| 388 | iwm->conf.rts_threshold); | ||
| 389 | if (ret < 0) | ||
| 390 | return ret; | ||
| 391 | |||
| 392 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 393 | CFG_CTS_TO_SELF, iwm->conf.cts_to_self); | ||
| 394 | if (ret < 0) | ||
| 395 | return ret; | ||
| 396 | |||
| 397 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 398 | CFG_WIRELESS_MODE, | ||
| 399 | iwm->conf.wireless_mode); | ||
| 400 | if (ret < 0) | ||
| 401 | return ret; | ||
| 402 | |||
| 403 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 404 | CFG_COEX_MODE, modparam_wiwi); | ||
| 405 | if (ret < 0) | ||
| 406 | return ret; | ||
| 407 | |||
| 408 | /* | ||
| 409 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 410 | CFG_ASSOCIATION_TIMEOUT, | ||
| 411 | iwm->conf.assoc_timeout); | ||
| 412 | if (ret < 0) | ||
| 413 | return ret; | ||
| 414 | |||
| 415 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 416 | CFG_ROAM_TIMEOUT, | ||
| 417 | iwm->conf.roam_timeout); | ||
| 418 | if (ret < 0) | ||
| 419 | return ret; | ||
| 420 | |||
| 421 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 422 | CFG_WIRELESS_MODE, | ||
| 423 | WIRELESS_MODE_11A | WIRELESS_MODE_11G); | ||
| 424 | if (ret < 0) | ||
| 425 | return ret; | ||
| 426 | */ | ||
| 427 | |||
| 428 | ret = iwm_umac_set_config_var(iwm, CFG_NET_ADDR, | ||
| 429 | iwm_to_ndev(iwm)->dev_addr, ETH_ALEN); | ||
| 430 | if (ret < 0) | ||
| 431 | return ret; | ||
| 432 | |||
| 433 | /* UMAC PM static configurations */ | ||
| 434 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 435 | CFG_PM_LEGACY_RX_TIMEOUT, 0x12C); | ||
| 436 | if (ret < 0) | ||
| 437 | return ret; | ||
| 438 | |||
| 439 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 440 | CFG_PM_LEGACY_TX_TIMEOUT, 0x15E); | ||
| 441 | if (ret < 0) | ||
| 442 | return ret; | ||
| 443 | |||
| 444 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 445 | CFG_PM_CTRL_FLAGS, 0x1); | ||
| 446 | if (ret < 0) | ||
| 447 | return ret; | ||
| 448 | |||
| 449 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | ||
| 450 | CFG_PM_KEEP_ALIVE_IN_BEACONS, 0x80); | ||
| 451 | if (ret < 0) | ||
| 452 | return ret; | ||
| 453 | |||
| 454 | /* reset UMAC */ | ||
| 455 | ret = iwm_send_umac_reset(iwm, reset_flags, 1); | ||
| 456 | if (ret < 0) | ||
| 457 | return ret; | ||
| 458 | |||
| 459 | ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC, | ||
| 460 | WAIT_NOTIF_TIMEOUT); | ||
| 461 | if (ret) { | ||
| 462 | IWM_ERR(iwm, "Wait for UMAC RESET timeout\n"); | ||
| 463 | return ret; | ||
| 464 | } | ||
| 465 | |||
| 466 | return ret; | ||
| 467 | } | ||
| 468 | |||
| 469 | int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id) | ||
| 470 | { | ||
| 471 | struct iwm_udma_wifi_cmd udma_cmd; | ||
| 472 | struct iwm_umac_cmd umac_cmd; | ||
| 473 | struct iwm_tx_info *tx_info = skb_to_tx_info(skb); | ||
| 474 | |||
| 475 | udma_cmd.eop = 1; /* always set eop for non-concatenated Tx */ | ||
| 476 | udma_cmd.credit_group = pool_id; | ||
| 477 | udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid; | ||
| 478 | udma_cmd.lmac_offset = 0; | ||
| 479 | |||
| 480 | umac_cmd.id = REPLY_TX; | ||
| 481 | umac_cmd.color = tx_info->color; | ||
| 482 | umac_cmd.resp = 0; | ||
| 483 | |||
| 484 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, | ||
| 485 | skb->data, skb->len); | ||
| 486 | } | ||
| 487 | |||
| 488 | static int iwm_target_read(struct iwm_priv *iwm, __le32 address, | ||
| 489 | u8 *response, u32 resp_size) | ||
| 490 | { | ||
| 491 | struct iwm_udma_nonwifi_cmd target_cmd; | ||
| 492 | struct iwm_nonwifi_cmd *cmd; | ||
| 493 | u16 seq_num; | ||
| 494 | int ret = 0; | ||
| 495 | |||
| 496 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ; | ||
| 497 | target_cmd.addr = address; | ||
| 498 | target_cmd.op1_sz = cpu_to_le32(resp_size); | ||
| 499 | target_cmd.op2 = 0; | ||
| 500 | target_cmd.handle_by_hw = 0; | ||
| 501 | target_cmd.resp = 1; | ||
| 502 | target_cmd.eop = 1; | ||
| 503 | |||
| 504 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); | ||
| 505 | if (ret < 0) { | ||
| 506 | IWM_ERR(iwm, "Couldn't send READ command\n"); | ||
| 507 | return ret; | ||
| 508 | } | ||
| 509 | |||
| 510 | /* When succeeding, the send_target routine returns the seq number */ | ||
| 511 | seq_num = ret; | ||
| 512 | |||
| 513 | ret = wait_event_interruptible_timeout(iwm->nonwifi_queue, | ||
| 514 | (cmd = iwm_get_pending_nonwifi_cmd(iwm, seq_num, | ||
| 515 | UMAC_HDI_OUT_OPCODE_READ)) != NULL, | ||
| 516 | 2 * HZ); | ||
| 517 | |||
| 518 | if (!ret) { | ||
| 519 | IWM_ERR(iwm, "Didn't receive a target READ answer\n"); | ||
| 520 | return ret; | ||
| 521 | } | ||
| 522 | |||
| 523 | memcpy(response, cmd->buf.hdr + sizeof(struct iwm_udma_in_hdr), | ||
| 524 | resp_size); | ||
| 525 | |||
| 526 | kfree(cmd); | ||
| 527 | |||
| 528 | return 0; | ||
| 529 | } | ||
| 530 | |||
| 531 | int iwm_read_mac(struct iwm_priv *iwm, u8 *mac) | ||
| 532 | { | ||
| 533 | int ret; | ||
| 534 | u8 mac_align[ALIGN(ETH_ALEN, 8)]; | ||
| 535 | |||
| 536 | ret = iwm_target_read(iwm, cpu_to_le32(WICO_MAC_ADDRESS_ADDR), | ||
| 537 | mac_align, sizeof(mac_align)); | ||
| 538 | if (ret) | ||
| 539 | return ret; | ||
| 540 | |||
| 541 | if (is_valid_ether_addr(mac_align)) | ||
| 542 | memcpy(mac, mac_align, ETH_ALEN); | ||
| 543 | else { | ||
| 544 | IWM_ERR(iwm, "Invalid EEPROM MAC\n"); | ||
| 545 | memcpy(mac, iwm->conf.mac_addr, ETH_ALEN); | ||
| 546 | get_random_bytes(&mac[3], 3); | ||
| 547 | } | ||
| 548 | |||
| 549 | return 0; | ||
| 550 | } | ||
| 551 | |||
| 552 | static int iwm_check_profile(struct iwm_priv *iwm) | ||
| 553 | { | ||
| 554 | if (!iwm->umac_profile_active) | ||
| 555 | return -EAGAIN; | ||
| 556 | |||
| 557 | if (iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 && | ||
| 558 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104 && | ||
| 559 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_TKIP && | ||
| 560 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_CCMP) { | ||
| 561 | IWM_ERR(iwm, "Wrong unicast cipher: 0x%x\n", | ||
| 562 | iwm->umac_profile->sec.ucast_cipher); | ||
| 563 | return -EAGAIN; | ||
| 564 | } | ||
| 565 | |||
| 566 | if (iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_40 && | ||
| 567 | iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_WEP_104 && | ||
| 568 | iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_TKIP && | ||
| 569 | iwm->umac_profile->sec.mcast_cipher != UMAC_CIPHER_TYPE_CCMP) { | ||
| 570 | IWM_ERR(iwm, "Wrong multicast cipher: 0x%x\n", | ||
| 571 | iwm->umac_profile->sec.mcast_cipher); | ||
| 572 | return -EAGAIN; | ||
| 573 | } | ||
| 574 | |||
| 575 | if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 || | ||
| 576 | iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) && | ||
| 577 | (iwm->umac_profile->sec.ucast_cipher != | ||
| 578 | iwm->umac_profile->sec.mcast_cipher)) { | ||
| 579 | IWM_ERR(iwm, "Unicast and multicast ciphers differ for WEP\n"); | ||
| 580 | } | ||
| 581 | |||
| 582 | return 0; | ||
| 583 | } | ||
| 584 | |||
| 585 | int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx) | ||
| 586 | { | ||
| 587 | struct iwm_umac_tx_key_id tx_key_id; | ||
| 588 | int ret; | ||
| 589 | |||
| 590 | ret = iwm_check_profile(iwm); | ||
| 591 | if (ret < 0) | ||
| 592 | return ret; | ||
| 593 | |||
| 594 | /* UMAC only allows to set default key for WEP and auth type is | ||
| 595 | * NOT 802.1X or RSNA. */ | ||
| 596 | if ((iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_40 && | ||
| 597 | iwm->umac_profile->sec.ucast_cipher != UMAC_CIPHER_TYPE_WEP_104) || | ||
| 598 | iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_8021X || | ||
| 599 | iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_RSNA_PSK) | ||
| 600 | return 0; | ||
| 601 | |||
| 602 | tx_key_id.hdr.oid = UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID; | ||
| 603 | tx_key_id.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_tx_key_id) - | ||
| 604 | sizeof(struct iwm_umac_wifi_if)); | ||
| 605 | |||
| 606 | tx_key_id.key_idx = key_idx; | ||
| 607 | |||
| 608 | return iwm_send_wifi_if_cmd(iwm, &tx_key_id, sizeof(tx_key_id), 1); | ||
| 609 | } | ||
| 610 | |||
| 611 | int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key) | ||
| 612 | { | ||
| 613 | int ret = 0; | ||
| 614 | u8 cmd[64], *sta_addr, *key_data, key_len; | ||
| 615 | s8 key_idx; | ||
| 616 | u16 cmd_size = 0; | ||
| 617 | struct iwm_umac_key_hdr *key_hdr = &key->hdr; | ||
| 618 | struct iwm_umac_key_wep40 *wep40 = (struct iwm_umac_key_wep40 *)cmd; | ||
| 619 | struct iwm_umac_key_wep104 *wep104 = (struct iwm_umac_key_wep104 *)cmd; | ||
| 620 | struct iwm_umac_key_tkip *tkip = (struct iwm_umac_key_tkip *)cmd; | ||
| 621 | struct iwm_umac_key_ccmp *ccmp = (struct iwm_umac_key_ccmp *)cmd; | ||
| 622 | |||
| 623 | if (!remove) { | ||
| 624 | ret = iwm_check_profile(iwm); | ||
| 625 | if (ret < 0) | ||
| 626 | return ret; | ||
| 627 | } | ||
| 628 | |||
| 629 | sta_addr = key->hdr.mac; | ||
| 630 | key_data = key->key; | ||
| 631 | key_len = key->key_len; | ||
| 632 | key_idx = key->hdr.key_idx; | ||
| 633 | |||
| 634 | if (!remove) { | ||
| 635 | u8 auth_type = iwm->umac_profile->sec.auth_type; | ||
| 636 | |||
| 637 | IWM_DBG_WEXT(iwm, DBG, "key_idx:%d\n", key_idx); | ||
| 638 | IWM_DBG_WEXT(iwm, DBG, "key_len:%d\n", key_len); | ||
| 639 | IWM_DBG_WEXT(iwm, DBG, "MAC:%pM, idx:%d, multicast:%d\n", | ||
| 640 | key_hdr->mac, key_hdr->key_idx, key_hdr->multicast); | ||
| 641 | |||
| 642 | IWM_DBG_WEXT(iwm, DBG, "profile: mcast:0x%x, ucast:0x%x\n", | ||
| 643 | iwm->umac_profile->sec.mcast_cipher, | ||
| 644 | iwm->umac_profile->sec.ucast_cipher); | ||
| 645 | IWM_DBG_WEXT(iwm, DBG, "profile: auth_type:0x%x, flags:0x%x\n", | ||
| 646 | iwm->umac_profile->sec.auth_type, | ||
| 647 | iwm->umac_profile->sec.flags); | ||
| 648 | |||
| 649 | switch (key->cipher) { | ||
| 650 | case WLAN_CIPHER_SUITE_WEP40: | ||
| 651 | wep40->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP40_KEY; | ||
| 652 | wep40->hdr.buf_size = | ||
| 653 | cpu_to_le16(sizeof(struct iwm_umac_key_wep40) - | ||
| 654 | sizeof(struct iwm_umac_wifi_if)); | ||
| 655 | |||
| 656 | memcpy(&wep40->key_hdr, key_hdr, | ||
| 657 | sizeof(struct iwm_umac_key_hdr)); | ||
| 658 | memcpy(wep40->key, key_data, key_len); | ||
| 659 | wep40->static_key = | ||
| 660 | !!((auth_type != UMAC_AUTH_TYPE_8021X) && | ||
| 661 | (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); | ||
| 662 | |||
| 663 | cmd_size = sizeof(struct iwm_umac_key_wep40); | ||
| 664 | break; | ||
| 665 | |||
| 666 | case WLAN_CIPHER_SUITE_WEP104: | ||
| 667 | wep104->hdr.oid = UMAC_WIFI_IF_CMD_ADD_WEP104_KEY; | ||
| 668 | wep104->hdr.buf_size = | ||
| 669 | cpu_to_le16(sizeof(struct iwm_umac_key_wep104) - | ||
| 670 | sizeof(struct iwm_umac_wifi_if)); | ||
| 671 | |||
| 672 | memcpy(&wep104->key_hdr, key_hdr, | ||
| 673 | sizeof(struct iwm_umac_key_hdr)); | ||
| 674 | memcpy(wep104->key, key_data, key_len); | ||
| 675 | wep104->static_key = | ||
| 676 | !!((auth_type != UMAC_AUTH_TYPE_8021X) && | ||
| 677 | (auth_type != UMAC_AUTH_TYPE_RSNA_PSK)); | ||
| 678 | |||
| 679 | cmd_size = sizeof(struct iwm_umac_key_wep104); | ||
| 680 | break; | ||
| 681 | |||
| 682 | case WLAN_CIPHER_SUITE_CCMP: | ||
| 683 | key_hdr->key_idx++; | ||
| 684 | ccmp->hdr.oid = UMAC_WIFI_IF_CMD_ADD_CCMP_KEY; | ||
| 685 | ccmp->hdr.buf_size = | ||
| 686 | cpu_to_le16(sizeof(struct iwm_umac_key_ccmp) - | ||
| 687 | sizeof(struct iwm_umac_wifi_if)); | ||
| 688 | |||
| 689 | memcpy(&ccmp->key_hdr, key_hdr, | ||
| 690 | sizeof(struct iwm_umac_key_hdr)); | ||
| 691 | |||
| 692 | memcpy(ccmp->key, key_data, key_len); | ||
| 693 | |||
| 694 | if (key->seq_len) | ||
| 695 | memcpy(ccmp->iv_count, key->seq, key->seq_len); | ||
| 696 | |||
| 697 | cmd_size = sizeof(struct iwm_umac_key_ccmp); | ||
| 698 | break; | ||
| 699 | |||
| 700 | case WLAN_CIPHER_SUITE_TKIP: | ||
| 701 | key_hdr->key_idx++; | ||
| 702 | tkip->hdr.oid = UMAC_WIFI_IF_CMD_ADD_TKIP_KEY; | ||
| 703 | tkip->hdr.buf_size = | ||
| 704 | cpu_to_le16(sizeof(struct iwm_umac_key_tkip) - | ||
| 705 | sizeof(struct iwm_umac_wifi_if)); | ||
| 706 | |||
| 707 | memcpy(&tkip->key_hdr, key_hdr, | ||
| 708 | sizeof(struct iwm_umac_key_hdr)); | ||
| 709 | |||
| 710 | memcpy(tkip->tkip_key, key_data, IWM_TKIP_KEY_SIZE); | ||
| 711 | memcpy(tkip->mic_tx_key, key_data + IWM_TKIP_KEY_SIZE, | ||
| 712 | IWM_TKIP_MIC_SIZE); | ||
| 713 | memcpy(tkip->mic_rx_key, | ||
| 714 | key_data + IWM_TKIP_KEY_SIZE + IWM_TKIP_MIC_SIZE, | ||
| 715 | IWM_TKIP_MIC_SIZE); | ||
| 716 | |||
| 717 | if (key->seq_len) | ||
| 718 | memcpy(ccmp->iv_count, key->seq, key->seq_len); | ||
| 719 | |||
| 720 | cmd_size = sizeof(struct iwm_umac_key_tkip); | ||
| 721 | break; | ||
| 722 | |||
| 723 | default: | ||
| 724 | return -ENOTSUPP; | ||
| 725 | } | ||
| 726 | |||
| 727 | if ((key->cipher == WLAN_CIPHER_SUITE_TKIP) || | ||
| 728 | (key->cipher == WLAN_CIPHER_SUITE_CCMP)) | ||
| 729 | /* | ||
| 730 | * UGLY_UGLY_UGLY | ||
| 731 | * Copied HACK from the MWG driver. | ||
| 732 | * Without it, the key is set before the second | ||
| 733 | * EAPOL frame is sent, and the latter is thus | ||
| 734 | * encrypted. | ||
| 735 | */ | ||
| 736 | schedule_timeout_interruptible(usecs_to_jiffies(300)); | ||
| 737 | |||
| 738 | ret = iwm_send_wifi_if_cmd(iwm, cmd, cmd_size, 1); | ||
| 739 | } else { | ||
| 740 | struct iwm_umac_key_remove key_remove; | ||
| 741 | |||
| 742 | IWM_DBG_WEXT(iwm, ERR, "Removing key_idx:%d\n", key_idx); | ||
| 743 | |||
| 744 | key_remove.hdr.oid = UMAC_WIFI_IF_CMD_REMOVE_KEY; | ||
| 745 | key_remove.hdr.buf_size = | ||
| 746 | cpu_to_le16(sizeof(struct iwm_umac_key_remove) - | ||
| 747 | sizeof(struct iwm_umac_wifi_if)); | ||
| 748 | memcpy(&key_remove.key_hdr, key_hdr, | ||
| 749 | sizeof(struct iwm_umac_key_hdr)); | ||
| 750 | |||
| 751 | ret = iwm_send_wifi_if_cmd(iwm, &key_remove, | ||
| 752 | sizeof(struct iwm_umac_key_remove), | ||
| 753 | 1); | ||
| 754 | if (ret) | ||
| 755 | return ret; | ||
| 756 | |||
| 757 | iwm->keys[key_idx].key_len = 0; | ||
| 758 | } | ||
| 759 | |||
| 760 | return ret; | ||
| 761 | } | ||
| 762 | |||
| 763 | |||
| 764 | int iwm_send_mlme_profile(struct iwm_priv *iwm) | ||
| 765 | { | ||
| 766 | int ret; | ||
| 767 | struct iwm_umac_profile profile; | ||
| 768 | |||
| 769 | memcpy(&profile, iwm->umac_profile, sizeof(profile)); | ||
| 770 | |||
| 771 | profile.hdr.oid = UMAC_WIFI_IF_CMD_SET_PROFILE; | ||
| 772 | profile.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_profile) - | ||
| 773 | sizeof(struct iwm_umac_wifi_if)); | ||
| 774 | |||
| 775 | ret = iwm_send_wifi_if_cmd(iwm, &profile, sizeof(profile), 1); | ||
| 776 | if (ret) { | ||
| 777 | IWM_ERR(iwm, "Send profile command failed\n"); | ||
| 778 | return ret; | ||
| 779 | } | ||
| 780 | |||
| 781 | set_bit(IWM_STATUS_SME_CONNECTING, &iwm->status); | ||
| 782 | return 0; | ||
| 783 | } | ||
| 784 | |||
| 785 | int __iwm_invalidate_mlme_profile(struct iwm_priv *iwm) | ||
| 786 | { | ||
| 787 | struct iwm_umac_invalidate_profile invalid; | ||
| 788 | |||
| 789 | invalid.hdr.oid = UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE; | ||
| 790 | invalid.hdr.buf_size = | ||
| 791 | cpu_to_le16(sizeof(struct iwm_umac_invalidate_profile) - | ||
| 792 | sizeof(struct iwm_umac_wifi_if)); | ||
| 793 | |||
| 794 | invalid.reason = WLAN_REASON_UNSPECIFIED; | ||
| 795 | |||
| 796 | return iwm_send_wifi_if_cmd(iwm, &invalid, sizeof(invalid), 1); | ||
| 797 | } | ||
| 798 | |||
| 799 | int iwm_invalidate_mlme_profile(struct iwm_priv *iwm) | ||
| 800 | { | ||
| 801 | int ret; | ||
| 802 | |||
| 803 | ret = __iwm_invalidate_mlme_profile(iwm); | ||
| 804 | if (ret) | ||
| 805 | return ret; | ||
| 806 | |||
| 807 | ret = wait_event_interruptible_timeout(iwm->mlme_queue, | ||
| 808 | (iwm->umac_profile_active == 0), 5 * HZ); | ||
| 809 | |||
| 810 | return ret ? 0 : -EBUSY; | ||
| 811 | } | ||
| 812 | |||
| 813 | int iwm_tx_power_trigger(struct iwm_priv *iwm) | ||
| 814 | { | ||
| 815 | struct iwm_umac_pwr_trigger pwr_trigger; | ||
| 816 | |||
| 817 | pwr_trigger.hdr.oid = UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER; | ||
| 818 | pwr_trigger.hdr.buf_size = | ||
| 819 | cpu_to_le16(sizeof(struct iwm_umac_pwr_trigger) - | ||
| 820 | sizeof(struct iwm_umac_wifi_if)); | ||
| 821 | |||
| 822 | |||
| 823 | return iwm_send_wifi_if_cmd(iwm, &pwr_trigger, sizeof(pwr_trigger), 1); | ||
| 824 | } | ||
| 825 | |||
| 826 | int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags) | ||
| 827 | { | ||
| 828 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
| 829 | struct iwm_umac_cmd umac_cmd; | ||
| 830 | struct iwm_umac_cmd_stats_req stats_req; | ||
| 831 | |||
| 832 | stats_req.flags = cpu_to_le32(flags); | ||
| 833 | |||
| 834 | umac_cmd.id = UMAC_CMD_OPCODE_STATISTIC_REQUEST; | ||
| 835 | umac_cmd.resp = 0; | ||
| 836 | |||
| 837 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stats_req, | ||
| 838 | sizeof(struct iwm_umac_cmd_stats_req)); | ||
| 839 | } | ||
| 840 | |||
| 841 | int iwm_send_umac_channel_list(struct iwm_priv *iwm) | ||
| 842 | { | ||
| 843 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
| 844 | struct iwm_umac_cmd umac_cmd; | ||
| 845 | struct iwm_umac_cmd_get_channel_list *ch_list; | ||
| 846 | int size = sizeof(struct iwm_umac_cmd_get_channel_list) + | ||
| 847 | sizeof(struct iwm_umac_channel_info) * 4; | ||
| 848 | int ret; | ||
| 849 | |||
| 850 | ch_list = kzalloc(size, GFP_KERNEL); | ||
| 851 | if (!ch_list) { | ||
| 852 | IWM_ERR(iwm, "Couldn't allocate channel list cmd\n"); | ||
| 853 | return -ENOMEM; | ||
| 854 | } | ||
| 855 | |||
| 856 | ch_list->ch[0].band = UMAC_BAND_2GHZ; | ||
| 857 | ch_list->ch[0].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
| 858 | ch_list->ch[0].flags = UMAC_CHANNEL_FLAG_VALID; | ||
| 859 | |||
| 860 | ch_list->ch[1].band = UMAC_BAND_5GHZ; | ||
| 861 | ch_list->ch[1].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
| 862 | ch_list->ch[1].flags = UMAC_CHANNEL_FLAG_VALID; | ||
| 863 | |||
| 864 | ch_list->ch[2].band = UMAC_BAND_2GHZ; | ||
| 865 | ch_list->ch[2].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
| 866 | ch_list->ch[2].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; | ||
| 867 | |||
| 868 | ch_list->ch[3].band = UMAC_BAND_5GHZ; | ||
| 869 | ch_list->ch[3].type = UMAC_CHANNEL_WIDTH_20MHZ; | ||
| 870 | ch_list->ch[3].flags = UMAC_CHANNEL_FLAG_VALID | UMAC_CHANNEL_FLAG_IBSS; | ||
| 871 | |||
| 872 | ch_list->count = cpu_to_le16(4); | ||
| 873 | |||
| 874 | umac_cmd.id = UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST; | ||
| 875 | umac_cmd.resp = 1; | ||
| 876 | |||
| 877 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, ch_list, size); | ||
| 878 | |||
| 879 | kfree(ch_list); | ||
| 880 | |||
| 881 | return ret; | ||
| 882 | } | ||
| 883 | |||
| 884 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, | ||
| 885 | int ssid_num) | ||
| 886 | { | ||
| 887 | struct iwm_umac_cmd_scan_request req; | ||
| 888 | int i, ret; | ||
| 889 | |||
| 890 | memset(&req, 0, sizeof(struct iwm_umac_cmd_scan_request)); | ||
| 891 | |||
| 892 | req.hdr.oid = UMAC_WIFI_IF_CMD_SCAN_REQUEST; | ||
| 893 | req.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_cmd_scan_request) | ||
| 894 | - sizeof(struct iwm_umac_wifi_if)); | ||
| 895 | req.type = UMAC_WIFI_IF_SCAN_TYPE_USER; | ||
| 896 | req.timeout = 2; | ||
| 897 | req.seq_num = iwm->scan_id; | ||
| 898 | req.ssid_num = min(ssid_num, UMAC_WIFI_IF_PROBE_OPTION_MAX); | ||
| 899 | |||
| 900 | for (i = 0; i < req.ssid_num; i++) { | ||
| 901 | memcpy(req.ssids[i].ssid, ssids[i].ssid, ssids[i].ssid_len); | ||
| 902 | req.ssids[i].ssid_len = ssids[i].ssid_len; | ||
| 903 | } | ||
| 904 | |||
| 905 | ret = iwm_send_wifi_if_cmd(iwm, &req, sizeof(req), 0); | ||
| 906 | if (ret) { | ||
| 907 | IWM_ERR(iwm, "Couldn't send scan request\n"); | ||
| 908 | return ret; | ||
| 909 | } | ||
| 910 | |||
| 911 | iwm->scan_id = (iwm->scan_id + 1) % IWM_SCAN_ID_MAX; | ||
| 912 | |||
| 913 | return 0; | ||
| 914 | } | ||
| 915 | |||
| 916 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len) | ||
| 917 | { | ||
| 918 | struct cfg80211_ssid one_ssid; | ||
| 919 | |||
| 920 | if (test_and_set_bit(IWM_STATUS_SCANNING, &iwm->status)) | ||
| 921 | return 0; | ||
| 922 | |||
| 923 | one_ssid.ssid_len = min(ssid_len, IEEE80211_MAX_SSID_LEN); | ||
| 924 | memcpy(&one_ssid.ssid, ssid, one_ssid.ssid_len); | ||
| 925 | |||
| 926 | return iwm_scan_ssids(iwm, &one_ssid, 1); | ||
| 927 | } | ||
| 928 | |||
| 929 | int iwm_target_reset(struct iwm_priv *iwm) | ||
| 930 | { | ||
| 931 | struct iwm_udma_nonwifi_cmd target_cmd; | ||
| 932 | |||
| 933 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_REBOOT; | ||
| 934 | target_cmd.addr = 0; | ||
| 935 | target_cmd.op1_sz = 0; | ||
| 936 | target_cmd.op2 = 0; | ||
| 937 | target_cmd.handle_by_hw = 0; | ||
| 938 | target_cmd.resp = 0; | ||
| 939 | target_cmd.eop = 1; | ||
| 940 | |||
| 941 | return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); | ||
| 942 | } | ||
| 943 | |||
| 944 | int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, | ||
| 945 | struct iwm_umac_notif_stop_resume_tx *ntf) | ||
| 946 | { | ||
| 947 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
| 948 | struct iwm_umac_cmd umac_cmd; | ||
| 949 | struct iwm_umac_cmd_stop_resume_tx stp_res_cmd; | ||
| 950 | struct iwm_sta_info *sta_info; | ||
| 951 | u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id); | ||
| 952 | int i; | ||
| 953 | |||
| 954 | sta_info = &iwm->sta_table[sta_id]; | ||
| 955 | if (!sta_info->valid) { | ||
| 956 | IWM_ERR(iwm, "Invalid STA: %d\n", sta_id); | ||
| 957 | return -EINVAL; | ||
| 958 | } | ||
| 959 | |||
| 960 | umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX; | ||
| 961 | umac_cmd.resp = 0; | ||
| 962 | |||
| 963 | stp_res_cmd.flags = ntf->flags; | ||
| 964 | stp_res_cmd.sta_id = ntf->sta_id; | ||
| 965 | stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk; | ||
| 966 | for (i = 0; i < IWM_UMAC_TID_NR; i++) | ||
| 967 | stp_res_cmd.last_seq_num[i] = | ||
| 968 | sta_info->tid_info[i].last_seq_num; | ||
| 969 | |||
| 970 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd, | ||
| 971 | sizeof(struct iwm_umac_cmd_stop_resume_tx)); | ||
| 972 | |||
| 973 | } | ||
| 974 | |||
| 975 | int iwm_send_pmkid_update(struct iwm_priv *iwm, | ||
| 976 | struct cfg80211_pmksa *pmksa, u32 command) | ||
| 977 | { | ||
| 978 | struct iwm_umac_pmkid_update update; | ||
| 979 | int ret; | ||
| 980 | |||
| 981 | memset(&update, 0, sizeof(struct iwm_umac_pmkid_update)); | ||
| 982 | |||
| 983 | update.hdr.oid = UMAC_WIFI_IF_CMD_PMKID_UPDATE; | ||
| 984 | update.hdr.buf_size = cpu_to_le16(sizeof(struct iwm_umac_pmkid_update) - | ||
| 985 | sizeof(struct iwm_umac_wifi_if)); | ||
| 986 | |||
| 987 | update.command = cpu_to_le32(command); | ||
| 988 | if (pmksa->bssid) | ||
| 989 | memcpy(&update.bssid, pmksa->bssid, ETH_ALEN); | ||
| 990 | if (pmksa->pmkid) | ||
| 991 | memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN); | ||
| 992 | |||
| 993 | ret = iwm_send_wifi_if_cmd(iwm, &update, | ||
| 994 | sizeof(struct iwm_umac_pmkid_update), 0); | ||
| 995 | if (ret) { | ||
| 996 | IWM_ERR(iwm, "PMKID update command failed\n"); | ||
| 997 | return ret; | ||
| 998 | } | ||
| 999 | |||
| 1000 | return 0; | ||
| 1001 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h new file mode 100644 index 00000000000..6421689f5e8 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/commands.h | |||
| @@ -0,0 +1,509 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_COMMANDS_H__ | ||
| 40 | #define __IWM_COMMANDS_H__ | ||
| 41 | |||
| 42 | #include <linux/ieee80211.h> | ||
| 43 | |||
| 44 | #define IWM_BARKER_REBOOT_NOTIFICATION 0xF | ||
| 45 | #define IWM_ACK_BARKER_NOTIFICATION 0x10 | ||
| 46 | |||
| 47 | /* UMAC commands */ | ||
| 48 | #define UMAC_RST_CTRL_FLG_LARC_CLK_EN 0x0001 | ||
| 49 | #define UMAC_RST_CTRL_FLG_LARC_RESET 0x0002 | ||
| 50 | #define UMAC_RST_CTRL_FLG_FUNC_RESET 0x0004 | ||
| 51 | #define UMAC_RST_CTRL_FLG_DEV_RESET 0x0008 | ||
| 52 | #define UMAC_RST_CTRL_FLG_WIFI_CORE_EN 0x0010 | ||
| 53 | #define UMAC_RST_CTRL_FLG_WIFI_LINK_EN 0x0040 | ||
| 54 | #define UMAC_RST_CTRL_FLG_WIFI_MLME_EN 0x0080 | ||
| 55 | #define UMAC_RST_CTRL_FLG_NVM_RELOAD 0x0100 | ||
| 56 | |||
| 57 | struct iwm_umac_cmd_reset { | ||
| 58 | __le32 flags; | ||
| 59 | } __packed; | ||
| 60 | |||
| 61 | #define UMAC_PARAM_TBL_ORD_FIX 0x0 | ||
| 62 | #define UMAC_PARAM_TBL_ORD_VAR 0x1 | ||
| 63 | #define UMAC_PARAM_TBL_CFG_FIX 0x2 | ||
| 64 | #define UMAC_PARAM_TBL_CFG_VAR 0x3 | ||
| 65 | #define UMAC_PARAM_TBL_BSS_TRK 0x4 | ||
| 66 | #define UMAC_PARAM_TBL_FA_CFG_FIX 0x5 | ||
| 67 | #define UMAC_PARAM_TBL_STA 0x6 | ||
| 68 | #define UMAC_PARAM_TBL_CHN 0x7 | ||
| 69 | #define UMAC_PARAM_TBL_STATISTICS 0x8 | ||
| 70 | |||
| 71 | /* fast access table */ | ||
| 72 | enum { | ||
| 73 | CFG_FRAG_THRESHOLD = 0, | ||
| 74 | CFG_FRAME_RETRY_LIMIT, | ||
| 75 | CFG_OS_QUEUE_UTIL_TH, | ||
| 76 | CFG_RX_FILTER, | ||
| 77 | /* <-- LAST --> */ | ||
| 78 | FAST_ACCESS_CFG_TBL_FIX_LAST | ||
| 79 | }; | ||
| 80 | |||
| 81 | /* fixed size table */ | ||
| 82 | enum { | ||
| 83 | CFG_POWER_INDEX = 0, | ||
| 84 | CFG_PM_LEGACY_RX_TIMEOUT, | ||
| 85 | CFG_PM_LEGACY_TX_TIMEOUT, | ||
| 86 | CFG_PM_CTRL_FLAGS, | ||
| 87 | CFG_PM_KEEP_ALIVE_IN_BEACONS, | ||
| 88 | CFG_BT_ON_THRESHOLD, | ||
| 89 | CFG_RTS_THRESHOLD, | ||
| 90 | CFG_CTS_TO_SELF, | ||
| 91 | CFG_COEX_MODE, | ||
| 92 | CFG_WIRELESS_MODE, | ||
| 93 | CFG_ASSOCIATION_TIMEOUT, | ||
| 94 | CFG_ROAM_TIMEOUT, | ||
| 95 | CFG_CAPABILITY_SUPPORTED_RATES, | ||
| 96 | CFG_SCAN_ALLOWED_UNASSOC_FLAGS, | ||
| 97 | CFG_SCAN_ALLOWED_MAIN_ASSOC_FLAGS, | ||
| 98 | CFG_SCAN_ALLOWED_PAN_ASSOC_FLAGS, | ||
| 99 | CFG_SCAN_INTERNAL_PERIODIC_ENABLED, | ||
| 100 | CFG_SCAN_IMM_INTERNAL_PERIODIC_SCAN_ON_INIT, | ||
| 101 | CFG_SCAN_DEFAULT_PERIODIC_FREQ_SEC, | ||
| 102 | CFG_SCAN_NUM_PASSIVE_CHAN_PER_PARTIAL_SCAN, | ||
| 103 | CFG_TLC_SUPPORTED_TX_HT_RATES, | ||
| 104 | CFG_TLC_SUPPORTED_TX_RATES, | ||
| 105 | CFG_TLC_SPATIAL_STREAM_SUPPORTED, | ||
| 106 | CFG_TLC_RETRY_PER_RATE, | ||
| 107 | CFG_TLC_RETRY_PER_HT_RATE, | ||
| 108 | CFG_TLC_FIXED_MCS, | ||
| 109 | CFG_TLC_CONTROL_FLAGS, | ||
| 110 | CFG_TLC_SR_MIN_FAIL, | ||
| 111 | CFG_TLC_SR_MIN_PASS, | ||
| 112 | CFG_TLC_HT_STAY_IN_COL_PASS_THRESH, | ||
| 113 | CFG_TLC_HT_STAY_IN_COL_FAIL_THRESH, | ||
| 114 | CFG_TLC_LEGACY_STAY_IN_COL_PASS_THRESH, | ||
| 115 | CFG_TLC_LEGACY_STAY_IN_COL_FAIL_THRESH, | ||
| 116 | CFG_TLC_HT_FLUSH_STATS_PACKETS, | ||
| 117 | CFG_TLC_LEGACY_FLUSH_STATS_PACKETS, | ||
| 118 | CFG_TLC_LEGACY_FLUSH_STATS_MS, | ||
| 119 | CFG_TLC_HT_FLUSH_STATS_MS, | ||
| 120 | CFG_TLC_STAY_IN_COL_TIME_OUT, | ||
| 121 | CFG_TLC_AGG_SHORT_LIM, | ||
| 122 | CFG_TLC_AGG_LONG_LIM, | ||
| 123 | CFG_TLC_HT_SR_NO_DECREASE, | ||
| 124 | CFG_TLC_LEGACY_SR_NO_DECREASE, | ||
| 125 | CFG_TLC_SR_FORCE_DECREASE, | ||
| 126 | CFG_TLC_SR_ALLOW_INCREASE, | ||
| 127 | CFG_TLC_AGG_SET_LONG, | ||
| 128 | CFG_TLC_AUTO_AGGREGATION, | ||
| 129 | CFG_TLC_AGG_THRESHOLD, | ||
| 130 | CFG_TLC_TID_LOAD_THRESHOLD, | ||
| 131 | CFG_TLC_BLOCK_ACK_TIMEOUT, | ||
| 132 | CFG_TLC_NO_BA_COUNTED_AS_ONE, | ||
| 133 | CFG_TLC_NUM_BA_STREAMS_ALLOWED, | ||
| 134 | CFG_TLC_NUM_BA_STREAMS_PRESENT, | ||
| 135 | CFG_TLC_RENEW_ADDBA_DELAY, | ||
| 136 | CFG_TLC_NUM_OF_MULTISEC_TO_COUN_LOAD, | ||
| 137 | CFG_TLC_IS_STABLE_IN_HT, | ||
| 138 | CFG_TLC_SR_SIC_1ST_FAIL, | ||
| 139 | CFG_TLC_SR_SIC_1ST_PASS, | ||
| 140 | CFG_TLC_SR_SIC_TOTAL_FAIL, | ||
| 141 | CFG_TLC_SR_SIC_TOTAL_PASS, | ||
| 142 | CFG_RLC_CHAIN_CTRL, | ||
| 143 | CFG_TRK_TABLE_OP_MODE, | ||
| 144 | CFG_TRK_TABLE_RSSI_THRESHOLD, | ||
| 145 | CFG_TX_PWR_TARGET, /* Used By xVT */ | ||
| 146 | CFG_TX_PWR_LIMIT_USR, | ||
| 147 | CFG_TX_PWR_LIMIT_BSS, /* 11d limit */ | ||
| 148 | CFG_TX_PWR_LIMIT_BSS_CONSTRAINT, /* 11h constraint */ | ||
| 149 | CFG_TX_PWR_MODE, | ||
| 150 | CFG_MLME_DBG_NOTIF_BLOCK, | ||
| 151 | CFG_BT_OFF_BECONS_INTERVALS, | ||
| 152 | CFG_BT_FRAG_DURATION, | ||
| 153 | CFG_ACTIVE_CHAINS, | ||
| 154 | CFG_CALIB_CTRL, | ||
| 155 | CFG_CAPABILITY_SUPPORTED_HT_RATES, | ||
| 156 | CFG_HT_MAC_PARAM_INFO, | ||
| 157 | CFG_MIMO_PS_MODE, | ||
| 158 | CFG_HT_DEFAULT_CAPABILIES_INFO, | ||
| 159 | CFG_LED_SC_RESOLUTION_FACTOR, | ||
| 160 | CFG_PTAM_ENERGY_CCK_DET_DEFAULT, | ||
| 161 | CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_DEFAULT, | ||
| 162 | CFG_PTAM_CORR40_4_TH_ADD_MIN_DEFAULT, | ||
| 163 | CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_DEFAULT, | ||
| 164 | CFG_PTAM_CORR32_4_TH_ADD_MIN_DEFAULT, | ||
| 165 | CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_DEFAULT, | ||
| 166 | CFG_PTAM_CORR32_1_TH_ADD_MIN_DEFAULT, | ||
| 167 | CFG_PTAM_ENERGY_CCK_DET_MIN_VAL, | ||
| 168 | CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MIN_VAL, | ||
| 169 | CFG_PTAM_CORR40_4_TH_ADD_MIN_MIN_VAL, | ||
| 170 | CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MIN_VAL, | ||
| 171 | CFG_PTAM_CORR32_4_TH_ADD_MIN_MIN_VAL, | ||
| 172 | CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MIN_VAL, | ||
| 173 | CFG_PTAM_CORR32_1_TH_ADD_MIN_MIN_VAL, | ||
| 174 | CFG_PTAM_ENERGY_CCK_DET_MAX_VAL, | ||
| 175 | CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_MAX_VAL, | ||
| 176 | CFG_PTAM_CORR40_4_TH_ADD_MIN_MAX_VAL, | ||
| 177 | CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_MAX_VAL, | ||
| 178 | CFG_PTAM_CORR32_4_TH_ADD_MIN_MAX_VAL, | ||
| 179 | CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_MAX_VAL, | ||
| 180 | CFG_PTAM_CORR32_1_TH_ADD_MIN_MAX_VAL, | ||
| 181 | CFG_PTAM_ENERGY_CCK_DET_STEP_VAL, | ||
| 182 | CFG_PTAM_CORR40_4_TH_ADD_MIN_MRC_STEP_VAL, | ||
| 183 | CFG_PTAM_CORR40_4_TH_ADD_MIN_STEP_VAL, | ||
| 184 | CFG_PTAM_CORR32_4_TH_ADD_MIN_MRC_STEP_VAL, | ||
| 185 | CFG_PTAM_CORR32_4_TH_ADD_MIN_STEP_VAL, | ||
| 186 | CFG_PTAM_CORR32_1_TH_ADD_MIN_MRC_STEP_VAL, | ||
| 187 | CFG_PTAM_CORR32_1_TH_ADD_MIN_STEP_VAL, | ||
| 188 | CFG_PTAM_LINK_SENS_FA_OFDM_MAX, | ||
| 189 | CFG_PTAM_LINK_SENS_FA_OFDM_MIN, | ||
| 190 | CFG_PTAM_LINK_SENS_FA_CCK_MAX, | ||
| 191 | CFG_PTAM_LINK_SENS_FA_CCK_MIN, | ||
| 192 | CFG_PTAM_LINK_SENS_NRG_DIFF, | ||
| 193 | CFG_PTAM_LINK_SENS_NRG_MARGIN, | ||
| 194 | CFG_PTAM_LINK_SENS_MAX_NUMBER_OF_TIMES_IN_CCK_NO_FA, | ||
| 195 | CFG_PTAM_LINK_SENS_AUTO_CORR_MAX_TH_CCK, | ||
| 196 | CFG_AGG_MGG_TID_LOAD_ADDBA_THRESHOLD, | ||
| 197 | CFG_AGG_MGG_TID_LOAD_DELBA_THRESHOLD, | ||
| 198 | CFG_AGG_MGG_ADDBA_BUF_SIZE, | ||
| 199 | CFG_AGG_MGG_ADDBA_INACTIVE_TIMEOUT, | ||
| 200 | CFG_AGG_MGG_ADDBA_DEBUG_FLAGS, | ||
| 201 | CFG_SCAN_PERIODIC_RSSI_HIGH_THRESHOLD, | ||
| 202 | CFG_SCAN_PERIODIC_COEF_RSSI_HIGH, | ||
| 203 | CFG_11D_ENABLED, | ||
| 204 | CFG_11H_FEATURE_FLAGS, | ||
| 205 | |||
| 206 | /* <-- LAST --> */ | ||
| 207 | CFG_TBL_FIX_LAST | ||
| 208 | }; | ||
| 209 | |||
| 210 | /* variable size table */ | ||
| 211 | enum { | ||
| 212 | CFG_NET_ADDR = 0, | ||
| 213 | CFG_LED_PATTERN_TABLE, | ||
| 214 | |||
| 215 | /* <-- LAST --> */ | ||
| 216 | CFG_TBL_VAR_LAST | ||
| 217 | }; | ||
| 218 | |||
| 219 | struct iwm_umac_cmd_set_param_fix { | ||
| 220 | __le16 tbl; | ||
| 221 | __le16 key; | ||
| 222 | __le32 value; | ||
| 223 | } __packed; | ||
| 224 | |||
| 225 | struct iwm_umac_cmd_set_param_var { | ||
| 226 | __le16 tbl; | ||
| 227 | __le16 key; | ||
| 228 | __le16 len; | ||
| 229 | __le16 reserved; | ||
| 230 | } __packed; | ||
| 231 | |||
| 232 | struct iwm_umac_cmd_get_param { | ||
| 233 | __le16 tbl; | ||
| 234 | __le16 key; | ||
| 235 | } __packed; | ||
| 236 | |||
| 237 | struct iwm_umac_cmd_get_param_resp { | ||
| 238 | __le16 tbl; | ||
| 239 | __le16 key; | ||
| 240 | __le16 len; | ||
| 241 | __le16 reserved; | ||
| 242 | } __packed; | ||
| 243 | |||
| 244 | struct iwm_umac_cmd_eeprom_proxy_hdr { | ||
| 245 | __le32 type; | ||
| 246 | __le32 offset; | ||
| 247 | __le32 len; | ||
| 248 | } __packed; | ||
| 249 | |||
| 250 | struct iwm_umac_cmd_eeprom_proxy { | ||
| 251 | struct iwm_umac_cmd_eeprom_proxy_hdr hdr; | ||
| 252 | u8 buf[0]; | ||
| 253 | } __packed; | ||
| 254 | |||
| 255 | #define IWM_UMAC_CMD_EEPROM_TYPE_READ 0x1 | ||
| 256 | #define IWM_UMAC_CMD_EEPROM_TYPE_WRITE 0x2 | ||
| 257 | |||
| 258 | #define UMAC_CHANNEL_FLAG_VALID BIT(0) | ||
| 259 | #define UMAC_CHANNEL_FLAG_IBSS BIT(1) | ||
| 260 | #define UMAC_CHANNEL_FLAG_ACTIVE BIT(3) | ||
| 261 | #define UMAC_CHANNEL_FLAG_RADAR BIT(4) | ||
| 262 | #define UMAC_CHANNEL_FLAG_DFS BIT(7) | ||
| 263 | |||
| 264 | struct iwm_umac_channel_info { | ||
| 265 | u8 band; | ||
| 266 | u8 type; | ||
| 267 | u8 reserved; | ||
| 268 | u8 flags; | ||
| 269 | __le32 channels_mask; | ||
| 270 | } __packed; | ||
| 271 | |||
| 272 | struct iwm_umac_cmd_get_channel_list { | ||
| 273 | __le16 count; | ||
| 274 | __le16 reserved; | ||
| 275 | struct iwm_umac_channel_info ch[0]; | ||
| 276 | } __packed; | ||
| 277 | |||
| 278 | |||
| 279 | /* UMAC WiFi interface commands */ | ||
| 280 | |||
| 281 | /* Coexistence mode */ | ||
| 282 | #define COEX_MODE_SA 0x1 | ||
| 283 | #define COEX_MODE_XOR 0x2 | ||
| 284 | #define COEX_MODE_CM 0x3 | ||
| 285 | #define COEX_MODE_MAX 0x4 | ||
| 286 | |||
| 287 | /* Wireless mode */ | ||
| 288 | #define WIRELESS_MODE_11A 0x1 | ||
| 289 | #define WIRELESS_MODE_11G 0x2 | ||
| 290 | #define WIRELESS_MODE_11N 0x4 | ||
| 291 | |||
| 292 | #define UMAC_PROFILE_EX_IE_REQUIRED 0x1 | ||
| 293 | #define UMAC_PROFILE_QOS_ALLOWED 0x2 | ||
| 294 | |||
| 295 | /* Scanning */ | ||
| 296 | #define UMAC_WIFI_IF_PROBE_OPTION_MAX 10 | ||
| 297 | |||
| 298 | #define UMAC_WIFI_IF_SCAN_TYPE_USER 0x0 | ||
| 299 | #define UMAC_WIFI_IF_SCAN_TYPE_UMAC_RESERVED 0x1 | ||
| 300 | #define UMAC_WIFI_IF_SCAN_TYPE_HOST_PERIODIC 0x2 | ||
| 301 | #define UMAC_WIFI_IF_SCAN_TYPE_MAX 0x3 | ||
| 302 | |||
| 303 | struct iwm_umac_ssid { | ||
| 304 | u8 ssid_len; | ||
| 305 | u8 ssid[IEEE80211_MAX_SSID_LEN]; | ||
| 306 | u8 reserved[3]; | ||
| 307 | } __packed; | ||
| 308 | |||
| 309 | struct iwm_umac_cmd_scan_request { | ||
| 310 | struct iwm_umac_wifi_if hdr; | ||
| 311 | __le32 type; /* UMAC_WIFI_IF_SCAN_TYPE_* */ | ||
| 312 | u8 ssid_num; | ||
| 313 | u8 seq_num; | ||
| 314 | u8 timeout; /* In seconds */ | ||
| 315 | u8 reserved; | ||
| 316 | struct iwm_umac_ssid ssids[UMAC_WIFI_IF_PROBE_OPTION_MAX]; | ||
| 317 | } __packed; | ||
| 318 | |||
| 319 | #define UMAC_CIPHER_TYPE_NONE 0xFF | ||
| 320 | #define UMAC_CIPHER_TYPE_USE_GROUPCAST 0x00 | ||
| 321 | #define UMAC_CIPHER_TYPE_WEP_40 0x01 | ||
| 322 | #define UMAC_CIPHER_TYPE_WEP_104 0x02 | ||
| 323 | #define UMAC_CIPHER_TYPE_TKIP 0x04 | ||
| 324 | #define UMAC_CIPHER_TYPE_CCMP 0x08 | ||
| 325 | |||
| 326 | /* Supported authentication types - bitmap */ | ||
| 327 | #define UMAC_AUTH_TYPE_OPEN 0x00 | ||
| 328 | #define UMAC_AUTH_TYPE_LEGACY_PSK 0x01 | ||
| 329 | #define UMAC_AUTH_TYPE_8021X 0x02 | ||
| 330 | #define UMAC_AUTH_TYPE_RSNA_PSK 0x04 | ||
| 331 | |||
| 332 | /* iwm_umac_security.flag is WPA supported -- bits[0:0] */ | ||
| 333 | #define UMAC_SEC_FLG_WPA_ON_POS 0 | ||
| 334 | #define UMAC_SEC_FLG_WPA_ON_SEED 1 | ||
| 335 | #define UMAC_SEC_FLG_WPA_ON_MSK (UMAC_SEC_FLG_WPA_ON_SEED << \ | ||
| 336 | UMAC_SEC_FLG_WPA_ON_POS) | ||
| 337 | |||
| 338 | /* iwm_umac_security.flag is WPA2 supported -- bits [1:1] */ | ||
| 339 | #define UMAC_SEC_FLG_RSNA_ON_POS 1 | ||
| 340 | #define UMAC_SEC_FLG_RSNA_ON_SEED 1 | ||
| 341 | #define UMAC_SEC_FLG_RSNA_ON_MSK (UMAC_SEC_FLG_RSNA_ON_SEED << \ | ||
| 342 | UMAC_SEC_FLG_RSNA_ON_POS) | ||
| 343 | |||
| 344 | /* iwm_umac_security.flag is WSC mode on -- bits [2:2] */ | ||
| 345 | #define UMAC_SEC_FLG_WSC_ON_POS 2 | ||
| 346 | #define UMAC_SEC_FLG_WSC_ON_SEED 1 | ||
| 347 | #define UMAC_SEC_FLG_WSC_ON_MSK (UMAC_SEC_FLG_WSC_ON_SEED << \ | ||
| 348 | UMAC_SEC_FLG_WSC_ON_POS) | ||
| 349 | |||
| 350 | |||
| 351 | /* Legacy profile can use only WEP40 and WEP104 for encryption and | ||
| 352 | * OPEN or PSK for authentication */ | ||
| 353 | #define UMAC_SEC_FLG_LEGACY_PROFILE 0 | ||
| 354 | |||
| 355 | struct iwm_umac_security { | ||
| 356 | u8 auth_type; | ||
| 357 | u8 ucast_cipher; | ||
| 358 | u8 mcast_cipher; | ||
| 359 | u8 flags; | ||
| 360 | } __packed; | ||
| 361 | |||
| 362 | struct iwm_umac_ibss { | ||
| 363 | u8 beacon_interval; /* in millisecond */ | ||
| 364 | u8 atim; /* in millisecond */ | ||
| 365 | s8 join_only; | ||
| 366 | u8 band; | ||
| 367 | u8 channel; | ||
| 368 | u8 reserved[3]; | ||
| 369 | } __packed; | ||
| 370 | |||
| 371 | #define UMAC_MODE_BSS 0 | ||
| 372 | #define UMAC_MODE_IBSS 1 | ||
| 373 | |||
| 374 | #define UMAC_BSSID_MAX 4 | ||
| 375 | |||
| 376 | struct iwm_umac_profile { | ||
| 377 | struct iwm_umac_wifi_if hdr; | ||
| 378 | __le32 mode; | ||
| 379 | struct iwm_umac_ssid ssid; | ||
| 380 | u8 bssid[UMAC_BSSID_MAX][ETH_ALEN]; | ||
| 381 | struct iwm_umac_security sec; | ||
| 382 | struct iwm_umac_ibss ibss; | ||
| 383 | __le32 channel_2ghz; | ||
| 384 | __le32 channel_5ghz; | ||
| 385 | __le16 flags; | ||
| 386 | u8 wireless_mode; | ||
| 387 | u8 bss_num; | ||
| 388 | } __packed; | ||
| 389 | |||
| 390 | struct iwm_umac_invalidate_profile { | ||
| 391 | struct iwm_umac_wifi_if hdr; | ||
| 392 | u8 reason; | ||
| 393 | u8 reserved[3]; | ||
| 394 | } __packed; | ||
| 395 | |||
| 396 | /* Encryption key commands */ | ||
| 397 | struct iwm_umac_key_wep40 { | ||
| 398 | struct iwm_umac_wifi_if hdr; | ||
| 399 | struct iwm_umac_key_hdr key_hdr; | ||
| 400 | u8 key[WLAN_KEY_LEN_WEP40]; | ||
| 401 | u8 static_key; | ||
| 402 | u8 reserved[2]; | ||
| 403 | } __packed; | ||
| 404 | |||
| 405 | struct iwm_umac_key_wep104 { | ||
| 406 | struct iwm_umac_wifi_if hdr; | ||
| 407 | struct iwm_umac_key_hdr key_hdr; | ||
| 408 | u8 key[WLAN_KEY_LEN_WEP104]; | ||
| 409 | u8 static_key; | ||
| 410 | u8 reserved[2]; | ||
| 411 | } __packed; | ||
| 412 | |||
| 413 | #define IWM_TKIP_KEY_SIZE 16 | ||
| 414 | #define IWM_TKIP_MIC_SIZE 8 | ||
| 415 | struct iwm_umac_key_tkip { | ||
| 416 | struct iwm_umac_wifi_if hdr; | ||
| 417 | struct iwm_umac_key_hdr key_hdr; | ||
| 418 | u8 iv_count[6]; | ||
| 419 | u8 reserved[2]; | ||
| 420 | u8 tkip_key[IWM_TKIP_KEY_SIZE]; | ||
| 421 | u8 mic_rx_key[IWM_TKIP_MIC_SIZE]; | ||
| 422 | u8 mic_tx_key[IWM_TKIP_MIC_SIZE]; | ||
| 423 | } __packed; | ||
| 424 | |||
| 425 | struct iwm_umac_key_ccmp { | ||
| 426 | struct iwm_umac_wifi_if hdr; | ||
| 427 | struct iwm_umac_key_hdr key_hdr; | ||
| 428 | u8 iv_count[6]; | ||
| 429 | u8 reserved[2]; | ||
| 430 | u8 key[WLAN_KEY_LEN_CCMP]; | ||
| 431 | } __packed; | ||
| 432 | |||
| 433 | struct iwm_umac_key_remove { | ||
| 434 | struct iwm_umac_wifi_if hdr; | ||
| 435 | struct iwm_umac_key_hdr key_hdr; | ||
| 436 | } __packed; | ||
| 437 | |||
| 438 | struct iwm_umac_tx_key_id { | ||
| 439 | struct iwm_umac_wifi_if hdr; | ||
| 440 | u8 key_idx; | ||
| 441 | u8 reserved[3]; | ||
| 442 | } __packed; | ||
| 443 | |||
| 444 | struct iwm_umac_pwr_trigger { | ||
| 445 | struct iwm_umac_wifi_if hdr; | ||
| 446 | __le32 reseved; | ||
| 447 | } __packed; | ||
| 448 | |||
| 449 | struct iwm_umac_cmd_stats_req { | ||
| 450 | __le32 flags; | ||
| 451 | } __packed; | ||
| 452 | |||
| 453 | struct iwm_umac_cmd_stop_resume_tx { | ||
| 454 | u8 flags; | ||
| 455 | u8 sta_id; | ||
| 456 | __le16 stop_resume_tid_msk; | ||
| 457 | __le16 last_seq_num[IWM_UMAC_TID_NR]; | ||
| 458 | u16 reserved; | ||
| 459 | } __packed; | ||
| 460 | |||
| 461 | #define IWM_CMD_PMKID_ADD 1 | ||
| 462 | #define IWM_CMD_PMKID_DEL 2 | ||
| 463 | #define IWM_CMD_PMKID_FLUSH 3 | ||
| 464 | |||
| 465 | struct iwm_umac_pmkid_update { | ||
| 466 | struct iwm_umac_wifi_if hdr; | ||
| 467 | __le32 command; | ||
| 468 | u8 bssid[ETH_ALEN]; | ||
| 469 | __le16 reserved; | ||
| 470 | u8 pmkid[WLAN_PMKID_LEN]; | ||
| 471 | } __packed; | ||
| 472 | |||
| 473 | /* LMAC commands */ | ||
| 474 | int iwm_read_mac(struct iwm_priv *iwm, u8 *mac); | ||
| 475 | int iwm_send_prio_table(struct iwm_priv *iwm); | ||
| 476 | int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested); | ||
| 477 | int iwm_send_periodic_calib_cfg(struct iwm_priv *iwm, u8 calib_requested); | ||
| 478 | int iwm_send_calib_results(struct iwm_priv *iwm); | ||
| 479 | int iwm_store_rxiq_calib_result(struct iwm_priv *iwm); | ||
| 480 | int iwm_send_ct_kill_cfg(struct iwm_priv *iwm, u8 entry, u8 exit); | ||
| 481 | |||
| 482 | /* UMAC commands */ | ||
| 483 | int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, | ||
| 484 | bool resp); | ||
| 485 | int iwm_send_umac_reset(struct iwm_priv *iwm, __le32 reset_flags, bool resp); | ||
| 486 | int iwm_umac_set_config_fix(struct iwm_priv *iwm, u16 tbl, u16 key, u32 value); | ||
| 487 | int iwm_umac_set_config_var(struct iwm_priv *iwm, u16 key, | ||
| 488 | void *payload, u16 payload_size); | ||
| 489 | int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags); | ||
| 490 | int iwm_send_mlme_profile(struct iwm_priv *iwm); | ||
| 491 | int __iwm_invalidate_mlme_profile(struct iwm_priv *iwm); | ||
| 492 | int iwm_invalidate_mlme_profile(struct iwm_priv *iwm); | ||
| 493 | int iwm_send_packet(struct iwm_priv *iwm, struct sk_buff *skb, int pool_id); | ||
| 494 | int iwm_set_tx_key(struct iwm_priv *iwm, u8 key_idx); | ||
| 495 | int iwm_set_key(struct iwm_priv *iwm, bool remove, struct iwm_key *key); | ||
| 496 | int iwm_tx_power_trigger(struct iwm_priv *iwm); | ||
| 497 | int iwm_send_umac_stats_req(struct iwm_priv *iwm, u32 flags); | ||
| 498 | int iwm_send_umac_channel_list(struct iwm_priv *iwm); | ||
| 499 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, | ||
| 500 | int ssid_num); | ||
| 501 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len); | ||
| 502 | int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, | ||
| 503 | struct iwm_umac_notif_stop_resume_tx *ntf); | ||
| 504 | int iwm_send_pmkid_update(struct iwm_priv *iwm, | ||
| 505 | struct cfg80211_pmksa *pmksa, u32 command); | ||
| 506 | |||
| 507 | /* UDMA commands */ | ||
| 508 | int iwm_target_reset(struct iwm_priv *iwm); | ||
| 509 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/debug.h b/drivers/net/wireless/iwmc3200wifi/debug.h new file mode 100644 index 00000000000..a0c13a49ab3 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/debug.h | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> | ||
| 5 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 6 | * Zhu Yi <yi.zhu@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #ifndef __IWM_DEBUG_H__ | ||
| 25 | #define __IWM_DEBUG_H__ | ||
| 26 | |||
| 27 | #define IWM_ERR(p, f, a...) dev_err(iwm_to_dev(p), f, ## a) | ||
| 28 | #define IWM_WARN(p, f, a...) dev_warn(iwm_to_dev(p), f, ## a) | ||
| 29 | #define IWM_INFO(p, f, a...) dev_info(iwm_to_dev(p), f, ## a) | ||
| 30 | #define IWM_CRIT(p, f, a...) dev_crit(iwm_to_dev(p), f, ## a) | ||
| 31 | |||
| 32 | #ifdef CONFIG_IWM_DEBUG | ||
| 33 | |||
| 34 | #define IWM_DEBUG_MODULE(i, level, module, f, a...) \ | ||
| 35 | do { \ | ||
| 36 | if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\ | ||
| 37 | dev_printk(KERN_INFO, (iwm_to_dev(i)), \ | ||
| 38 | "%s " f, __func__ , ## a); \ | ||
| 39 | } while (0) | ||
| 40 | |||
| 41 | #define IWM_HEXDUMP(i, level, module, pref, buf, len) \ | ||
| 42 | do { \ | ||
| 43 | if (unlikely(i->dbg.dbg_module[IWM_DM_##module] >= (IWM_DL_##level)))\ | ||
| 44 | print_hex_dump(KERN_INFO, pref, DUMP_PREFIX_OFFSET, \ | ||
| 45 | 16, 1, buf, len, 1); \ | ||
| 46 | } while (0) | ||
| 47 | |||
| 48 | #else | ||
| 49 | |||
| 50 | #define IWM_DEBUG_MODULE(i, level, module, f, a...) | ||
| 51 | #define IWM_HEXDUMP(i, level, module, pref, buf, len) | ||
| 52 | |||
| 53 | #endif /* CONFIG_IWM_DEBUG */ | ||
| 54 | |||
| 55 | /* Debug modules */ | ||
| 56 | enum iwm_debug_module_id { | ||
| 57 | IWM_DM_BOOT = 0, | ||
| 58 | IWM_DM_FW, | ||
| 59 | IWM_DM_SDIO, | ||
| 60 | IWM_DM_NTF, | ||
| 61 | IWM_DM_RX, | ||
| 62 | IWM_DM_TX, | ||
| 63 | IWM_DM_MLME, | ||
| 64 | IWM_DM_CMD, | ||
| 65 | IWM_DM_WEXT, | ||
| 66 | __IWM_DM_NR, | ||
| 67 | }; | ||
| 68 | #define IWM_DM_DEFAULT 0 | ||
| 69 | |||
| 70 | #define IWM_DBG_BOOT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, BOOT, f, ## a) | ||
| 71 | #define IWM_DBG_FW(i, l, f, a...) IWM_DEBUG_MODULE(i, l, FW, f, ## a) | ||
| 72 | #define IWM_DBG_SDIO(i, l, f, a...) IWM_DEBUG_MODULE(i, l, SDIO, f, ## a) | ||
| 73 | #define IWM_DBG_NTF(i, l, f, a...) IWM_DEBUG_MODULE(i, l, NTF, f, ## a) | ||
| 74 | #define IWM_DBG_RX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, RX, f, ## a) | ||
| 75 | #define IWM_DBG_TX(i, l, f, a...) IWM_DEBUG_MODULE(i, l, TX, f, ## a) | ||
| 76 | #define IWM_DBG_MLME(i, l, f, a...) IWM_DEBUG_MODULE(i, l, MLME, f, ## a) | ||
| 77 | #define IWM_DBG_CMD(i, l, f, a...) IWM_DEBUG_MODULE(i, l, CMD, f, ## a) | ||
| 78 | #define IWM_DBG_WEXT(i, l, f, a...) IWM_DEBUG_MODULE(i, l, WEXT, f, ## a) | ||
| 79 | |||
| 80 | /* Debug levels */ | ||
| 81 | enum iwm_debug_level { | ||
| 82 | IWM_DL_NONE = 0, | ||
| 83 | IWM_DL_ERR, | ||
| 84 | IWM_DL_WARN, | ||
| 85 | IWM_DL_INFO, | ||
| 86 | IWM_DL_DBG, | ||
| 87 | }; | ||
| 88 | #define IWM_DL_DEFAULT IWM_DL_ERR | ||
| 89 | |||
| 90 | struct iwm_debugfs { | ||
| 91 | struct iwm_priv *iwm; | ||
| 92 | struct dentry *rootdir; | ||
| 93 | struct dentry *devdir; | ||
| 94 | struct dentry *dbgdir; | ||
| 95 | struct dentry *txdir; | ||
| 96 | struct dentry *rxdir; | ||
| 97 | struct dentry *busdir; | ||
| 98 | |||
| 99 | u32 dbg_level; | ||
| 100 | struct dentry *dbg_level_dentry; | ||
| 101 | |||
| 102 | unsigned long dbg_modules; | ||
| 103 | struct dentry *dbg_modules_dentry; | ||
| 104 | |||
| 105 | u8 dbg_module[__IWM_DM_NR]; | ||
| 106 | struct dentry *dbg_module_dentries[__IWM_DM_NR]; | ||
| 107 | |||
| 108 | struct dentry *txq_dentry; | ||
| 109 | struct dentry *tx_credit_dentry; | ||
| 110 | struct dentry *rx_ticket_dentry; | ||
| 111 | |||
| 112 | struct dentry *fw_err_dentry; | ||
| 113 | }; | ||
| 114 | |||
| 115 | #ifdef CONFIG_IWM_DEBUG | ||
| 116 | void iwm_debugfs_init(struct iwm_priv *iwm); | ||
| 117 | void iwm_debugfs_exit(struct iwm_priv *iwm); | ||
| 118 | #else | ||
| 119 | static inline void iwm_debugfs_init(struct iwm_priv *iwm) {} | ||
| 120 | static inline void iwm_debugfs_exit(struct iwm_priv *iwm) {} | ||
| 121 | #endif | ||
| 122 | |||
| 123 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c new file mode 100644 index 00000000000..0a0cc9667cd --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c | |||
| @@ -0,0 +1,493 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> | ||
| 5 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 6 | * Zhu Yi <yi.zhu@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | #include <linux/slab.h> | ||
| 25 | #include <linux/kernel.h> | ||
| 26 | #include <linux/bitops.h> | ||
| 27 | #include <linux/debugfs.h> | ||
| 28 | |||
| 29 | #include "iwm.h" | ||
| 30 | #include "bus.h" | ||
| 31 | #include "rx.h" | ||
| 32 | #include "debug.h" | ||
| 33 | |||
| 34 | static struct { | ||
| 35 | u8 id; | ||
| 36 | char *name; | ||
| 37 | } iwm_debug_module[__IWM_DM_NR] = { | ||
| 38 | {IWM_DM_BOOT, "boot"}, | ||
| 39 | {IWM_DM_FW, "fw"}, | ||
| 40 | {IWM_DM_SDIO, "sdio"}, | ||
| 41 | {IWM_DM_NTF, "ntf"}, | ||
| 42 | {IWM_DM_RX, "rx"}, | ||
| 43 | {IWM_DM_TX, "tx"}, | ||
| 44 | {IWM_DM_MLME, "mlme"}, | ||
| 45 | {IWM_DM_CMD, "cmd"}, | ||
| 46 | {IWM_DM_WEXT, "wext"}, | ||
| 47 | }; | ||
| 48 | |||
| 49 | #define add_dbg_module(dbg, name, id, initlevel) \ | ||
| 50 | do { \ | ||
| 51 | dbg.dbg_module[id] = (initlevel); \ | ||
| 52 | dbg.dbg_module_dentries[id] = \ | ||
| 53 | debugfs_create_x8(name, 0600, \ | ||
| 54 | dbg.dbgdir, \ | ||
| 55 | &(dbg.dbg_module[id])); \ | ||
| 56 | } while (0) | ||
| 57 | |||
| 58 | static int iwm_debugfs_u32_read(void *data, u64 *val) | ||
| 59 | { | ||
| 60 | struct iwm_priv *iwm = data; | ||
| 61 | |||
| 62 | *val = iwm->dbg.dbg_level; | ||
| 63 | return 0; | ||
| 64 | } | ||
| 65 | |||
| 66 | static int iwm_debugfs_dbg_level_write(void *data, u64 val) | ||
| 67 | { | ||
| 68 | struct iwm_priv *iwm = data; | ||
| 69 | int i; | ||
| 70 | |||
| 71 | iwm->dbg.dbg_level = val; | ||
| 72 | |||
| 73 | for (i = 0; i < __IWM_DM_NR; i++) | ||
| 74 | iwm->dbg.dbg_module[i] = val; | ||
| 75 | |||
| 76 | return 0; | ||
| 77 | } | ||
| 78 | DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_level, | ||
| 79 | iwm_debugfs_u32_read, iwm_debugfs_dbg_level_write, | ||
| 80 | "%llu\n"); | ||
| 81 | |||
| 82 | static int iwm_debugfs_dbg_modules_write(void *data, u64 val) | ||
| 83 | { | ||
| 84 | struct iwm_priv *iwm = data; | ||
| 85 | int i, bit; | ||
| 86 | |||
| 87 | iwm->dbg.dbg_modules = val; | ||
| 88 | |||
| 89 | for (i = 0; i < __IWM_DM_NR; i++) | ||
| 90 | iwm->dbg.dbg_module[i] = 0; | ||
| 91 | |||
| 92 | for_each_set_bit(bit, &iwm->dbg.dbg_modules, __IWM_DM_NR) | ||
| 93 | iwm->dbg.dbg_module[bit] = iwm->dbg.dbg_level; | ||
| 94 | |||
| 95 | return 0; | ||
| 96 | } | ||
| 97 | DEFINE_SIMPLE_ATTRIBUTE(fops_iwm_dbg_modules, | ||
| 98 | iwm_debugfs_u32_read, iwm_debugfs_dbg_modules_write, | ||
| 99 | "%llu\n"); | ||
| 100 | |||
| 101 | static int iwm_generic_open(struct inode *inode, struct file *filp) | ||
| 102 | { | ||
| 103 | filp->private_data = inode->i_private; | ||
| 104 | return 0; | ||
| 105 | } | ||
| 106 | |||
| 107 | |||
| 108 | static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer, | ||
| 109 | size_t count, loff_t *ppos) | ||
| 110 | { | ||
| 111 | struct iwm_priv *iwm = filp->private_data; | ||
| 112 | char *buf; | ||
| 113 | int i, buf_len = 4096; | ||
| 114 | size_t len = 0; | ||
| 115 | ssize_t ret; | ||
| 116 | |||
| 117 | if (*ppos != 0) | ||
| 118 | return 0; | ||
| 119 | if (count < sizeof(buf)) | ||
| 120 | return -ENOSPC; | ||
| 121 | |||
| 122 | buf = kzalloc(buf_len, GFP_KERNEL); | ||
| 123 | if (!buf) | ||
| 124 | return -ENOMEM; | ||
| 125 | |||
| 126 | for (i = 0; i < IWM_TX_QUEUES; i++) { | ||
| 127 | struct iwm_tx_queue *txq = &iwm->txq[i]; | ||
| 128 | struct sk_buff *skb; | ||
| 129 | int j; | ||
| 130 | unsigned long flags; | ||
| 131 | |||
| 132 | spin_lock_irqsave(&txq->queue.lock, flags); | ||
| 133 | |||
| 134 | skb = (struct sk_buff *)&txq->queue; | ||
| 135 | |||
| 136 | len += snprintf(buf + len, buf_len - len, "TXQ #%d\n", i); | ||
| 137 | len += snprintf(buf + len, buf_len - len, "\tStopped: %d\n", | ||
| 138 | __netif_subqueue_stopped(iwm_to_ndev(iwm), | ||
| 139 | txq->id)); | ||
| 140 | len += snprintf(buf + len, buf_len - len, "\tConcat count:%d\n", | ||
| 141 | txq->concat_count); | ||
| 142 | len += snprintf(buf + len, buf_len - len, "\tQueue len: %d\n", | ||
| 143 | skb_queue_len(&txq->queue)); | ||
| 144 | for (j = 0; j < skb_queue_len(&txq->queue); j++) { | ||
| 145 | struct iwm_tx_info *tx_info; | ||
| 146 | |||
| 147 | skb = skb->next; | ||
| 148 | tx_info = skb_to_tx_info(skb); | ||
| 149 | |||
| 150 | len += snprintf(buf + len, buf_len - len, | ||
| 151 | "\tSKB #%d\n", j); | ||
| 152 | len += snprintf(buf + len, buf_len - len, | ||
| 153 | "\t\tsta: %d\n", tx_info->sta); | ||
| 154 | len += snprintf(buf + len, buf_len - len, | ||
| 155 | "\t\tcolor: %d\n", tx_info->color); | ||
| 156 | len += snprintf(buf + len, buf_len - len, | ||
| 157 | "\t\ttid: %d\n", tx_info->tid); | ||
| 158 | } | ||
| 159 | |||
| 160 | spin_unlock_irqrestore(&txq->queue.lock, flags); | ||
| 161 | |||
| 162 | spin_lock_irqsave(&txq->stopped_queue.lock, flags); | ||
| 163 | |||
| 164 | len += snprintf(buf + len, buf_len - len, | ||
| 165 | "\tStopped Queue len: %d\n", | ||
| 166 | skb_queue_len(&txq->stopped_queue)); | ||
| 167 | for (j = 0; j < skb_queue_len(&txq->stopped_queue); j++) { | ||
| 168 | struct iwm_tx_info *tx_info; | ||
| 169 | |||
| 170 | skb = skb->next; | ||
| 171 | tx_info = skb_to_tx_info(skb); | ||
| 172 | |||
| 173 | len += snprintf(buf + len, buf_len - len, | ||
| 174 | "\tSKB #%d\n", j); | ||
| 175 | len += snprintf(buf + len, buf_len - len, | ||
| 176 | "\t\tsta: %d\n", tx_info->sta); | ||
| 177 | len += snprintf(buf + len, buf_len - len, | ||
| 178 | "\t\tcolor: %d\n", tx_info->color); | ||
| 179 | len += snprintf(buf + len, buf_len - len, | ||
| 180 | "\t\ttid: %d\n", tx_info->tid); | ||
| 181 | } | ||
| 182 | |||
| 183 | spin_unlock_irqrestore(&txq->stopped_queue.lock, flags); | ||
| 184 | } | ||
| 185 | |||
| 186 | ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); | ||
| 187 | kfree(buf); | ||
| 188 | |||
| 189 | return ret; | ||
| 190 | } | ||
| 191 | |||
| 192 | static ssize_t iwm_debugfs_tx_credit_read(struct file *filp, | ||
| 193 | char __user *buffer, | ||
| 194 | size_t count, loff_t *ppos) | ||
| 195 | { | ||
| 196 | struct iwm_priv *iwm = filp->private_data; | ||
| 197 | struct iwm_tx_credit *credit = &iwm->tx_credit; | ||
| 198 | char *buf; | ||
| 199 | int i, buf_len = 4096; | ||
| 200 | size_t len = 0; | ||
| 201 | ssize_t ret; | ||
| 202 | |||
| 203 | if (*ppos != 0) | ||
| 204 | return 0; | ||
| 205 | if (count < sizeof(buf)) | ||
| 206 | return -ENOSPC; | ||
| 207 | |||
| 208 | buf = kzalloc(buf_len, GFP_KERNEL); | ||
| 209 | if (!buf) | ||
| 210 | return -ENOMEM; | ||
| 211 | |||
| 212 | len += snprintf(buf + len, buf_len - len, | ||
| 213 | "NR pools: %d\n", credit->pool_nr); | ||
| 214 | len += snprintf(buf + len, buf_len - len, | ||
| 215 | "pools map: 0x%lx\n", credit->full_pools_map); | ||
| 216 | |||
| 217 | len += snprintf(buf + len, buf_len - len, "\n### POOLS ###\n"); | ||
| 218 | for (i = 0; i < IWM_MACS_OUT_GROUPS; i++) { | ||
| 219 | len += snprintf(buf + len, buf_len - len, | ||
| 220 | "pools entry #%d\n", i); | ||
| 221 | len += snprintf(buf + len, buf_len - len, | ||
| 222 | "\tid: %d\n", | ||
| 223 | credit->pools[i].id); | ||
| 224 | len += snprintf(buf + len, buf_len - len, | ||
| 225 | "\tsid: %d\n", | ||
| 226 | credit->pools[i].sid); | ||
| 227 | len += snprintf(buf + len, buf_len - len, | ||
| 228 | "\tmin_pages: %d\n", | ||
| 229 | credit->pools[i].min_pages); | ||
| 230 | len += snprintf(buf + len, buf_len - len, | ||
| 231 | "\tmax_pages: %d\n", | ||
| 232 | credit->pools[i].max_pages); | ||
| 233 | len += snprintf(buf + len, buf_len - len, | ||
| 234 | "\talloc_pages: %d\n", | ||
| 235 | credit->pools[i].alloc_pages); | ||
| 236 | len += snprintf(buf + len, buf_len - len, | ||
| 237 | "\tfreed_pages: %d\n", | ||
| 238 | credit->pools[i].total_freed_pages); | ||
| 239 | } | ||
| 240 | |||
| 241 | len += snprintf(buf + len, buf_len - len, "\n### SPOOLS ###\n"); | ||
| 242 | for (i = 0; i < IWM_MACS_OUT_SGROUPS; i++) { | ||
| 243 | len += snprintf(buf + len, buf_len - len, | ||
| 244 | "spools entry #%d\n", i); | ||
| 245 | len += snprintf(buf + len, buf_len - len, | ||
| 246 | "\tid: %d\n", | ||
| 247 | credit->spools[i].id); | ||
| 248 | len += snprintf(buf + len, buf_len - len, | ||
| 249 | "\tmax_pages: %d\n", | ||
| 250 | credit->spools[i].max_pages); | ||
| 251 | len += snprintf(buf + len, buf_len - len, | ||
| 252 | "\talloc_pages: %d\n", | ||
| 253 | credit->spools[i].alloc_pages); | ||
| 254 | |||
| 255 | } | ||
| 256 | |||
| 257 | ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); | ||
| 258 | kfree(buf); | ||
| 259 | |||
| 260 | return ret; | ||
| 261 | } | ||
| 262 | |||
| 263 | static ssize_t iwm_debugfs_rx_ticket_read(struct file *filp, | ||
| 264 | char __user *buffer, | ||
| 265 | size_t count, loff_t *ppos) | ||
| 266 | { | ||
| 267 | struct iwm_priv *iwm = filp->private_data; | ||
| 268 | struct iwm_rx_ticket_node *ticket; | ||
| 269 | char *buf; | ||
| 270 | int buf_len = 4096, i; | ||
| 271 | size_t len = 0; | ||
| 272 | ssize_t ret; | ||
| 273 | |||
| 274 | if (*ppos != 0) | ||
| 275 | return 0; | ||
| 276 | if (count < sizeof(buf)) | ||
| 277 | return -ENOSPC; | ||
| 278 | |||
| 279 | buf = kzalloc(buf_len, GFP_KERNEL); | ||
| 280 | if (!buf) | ||
| 281 | return -ENOMEM; | ||
| 282 | |||
| 283 | spin_lock(&iwm->ticket_lock); | ||
| 284 | list_for_each_entry(ticket, &iwm->rx_tickets, node) { | ||
| 285 | len += snprintf(buf + len, buf_len - len, "Ticket #%d\n", | ||
| 286 | ticket->ticket->id); | ||
| 287 | len += snprintf(buf + len, buf_len - len, "\taction: 0x%x\n", | ||
| 288 | ticket->ticket->action); | ||
| 289 | len += snprintf(buf + len, buf_len - len, "\tflags: 0x%x\n", | ||
| 290 | ticket->ticket->flags); | ||
| 291 | } | ||
| 292 | spin_unlock(&iwm->ticket_lock); | ||
| 293 | |||
| 294 | for (i = 0; i < IWM_RX_ID_HASH; i++) { | ||
| 295 | struct iwm_rx_packet *packet; | ||
| 296 | struct list_head *pkt_list = &iwm->rx_packets[i]; | ||
| 297 | |||
| 298 | if (!list_empty(pkt_list)) { | ||
| 299 | len += snprintf(buf + len, buf_len - len, | ||
| 300 | "Packet hash #%d\n", i); | ||
| 301 | spin_lock(&iwm->packet_lock[i]); | ||
| 302 | list_for_each_entry(packet, pkt_list, node) { | ||
| 303 | len += snprintf(buf + len, buf_len - len, | ||
| 304 | "\tPacket id: %d\n", | ||
| 305 | packet->id); | ||
| 306 | len += snprintf(buf + len, buf_len - len, | ||
| 307 | "\tPacket length: %lu\n", | ||
| 308 | packet->pkt_size); | ||
| 309 | } | ||
| 310 | spin_unlock(&iwm->packet_lock[i]); | ||
| 311 | } | ||
| 312 | } | ||
| 313 | |||
| 314 | ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); | ||
| 315 | kfree(buf); | ||
| 316 | |||
| 317 | return ret; | ||
| 318 | } | ||
| 319 | |||
| 320 | static ssize_t iwm_debugfs_fw_err_read(struct file *filp, | ||
| 321 | char __user *buffer, | ||
| 322 | size_t count, loff_t *ppos) | ||
| 323 | { | ||
| 324 | |||
| 325 | struct iwm_priv *iwm = filp->private_data; | ||
| 326 | char buf[512]; | ||
| 327 | int buf_len = 512; | ||
| 328 | size_t len = 0; | ||
| 329 | |||
| 330 | if (*ppos != 0) | ||
| 331 | return 0; | ||
| 332 | if (count < sizeof(buf)) | ||
| 333 | return -ENOSPC; | ||
| 334 | |||
| 335 | if (!iwm->last_fw_err) | ||
| 336 | return -ENOMEM; | ||
| 337 | |||
| 338 | if (iwm->last_fw_err->line_num == 0) | ||
| 339 | goto out; | ||
| 340 | |||
| 341 | len += snprintf(buf + len, buf_len - len, "%cMAC FW ERROR:\n", | ||
| 342 | (le32_to_cpu(iwm->last_fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) | ||
| 343 | ? 'L' : 'U'); | ||
| 344 | len += snprintf(buf + len, buf_len - len, | ||
| 345 | "\tCategory: %d\n", | ||
| 346 | le32_to_cpu(iwm->last_fw_err->category)); | ||
| 347 | |||
| 348 | len += snprintf(buf + len, buf_len - len, | ||
| 349 | "\tStatus: 0x%x\n", | ||
| 350 | le32_to_cpu(iwm->last_fw_err->status)); | ||
| 351 | |||
| 352 | len += snprintf(buf + len, buf_len - len, | ||
| 353 | "\tPC: 0x%x\n", | ||
| 354 | le32_to_cpu(iwm->last_fw_err->pc)); | ||
| 355 | |||
| 356 | len += snprintf(buf + len, buf_len - len, | ||
| 357 | "\tblink1: %d\n", | ||
| 358 | le32_to_cpu(iwm->last_fw_err->blink1)); | ||
| 359 | |||
| 360 | len += snprintf(buf + len, buf_len - len, | ||
| 361 | "\tblink2: %d\n", | ||
| 362 | le32_to_cpu(iwm->last_fw_err->blink2)); | ||
| 363 | |||
| 364 | len += snprintf(buf + len, buf_len - len, | ||
| 365 | "\tilink1: %d\n", | ||
| 366 | le32_to_cpu(iwm->last_fw_err->ilink1)); | ||
| 367 | |||
| 368 | len += snprintf(buf + len, buf_len - len, | ||
| 369 | "\tilink2: %d\n", | ||
| 370 | le32_to_cpu(iwm->last_fw_err->ilink2)); | ||
| 371 | |||
| 372 | len += snprintf(buf + len, buf_len - len, | ||
| 373 | "\tData1: 0x%x\n", | ||
| 374 | le32_to_cpu(iwm->last_fw_err->data1)); | ||
| 375 | |||
| 376 | len += snprintf(buf + len, buf_len - len, | ||
| 377 | "\tData2: 0x%x\n", | ||
| 378 | le32_to_cpu(iwm->last_fw_err->data2)); | ||
| 379 | |||
| 380 | len += snprintf(buf + len, buf_len - len, | ||
| 381 | "\tLine number: %d\n", | ||
| 382 | le32_to_cpu(iwm->last_fw_err->line_num)); | ||
| 383 | |||
| 384 | len += snprintf(buf + len, buf_len - len, | ||
| 385 | "\tUMAC status: 0x%x\n", | ||
| 386 | le32_to_cpu(iwm->last_fw_err->umac_status)); | ||
| 387 | |||
| 388 | len += snprintf(buf + len, buf_len - len, | ||
| 389 | "\tLMAC status: 0x%x\n", | ||
| 390 | le32_to_cpu(iwm->last_fw_err->lmac_status)); | ||
| 391 | |||
| 392 | len += snprintf(buf + len, buf_len - len, | ||
| 393 | "\tSDIO status: 0x%x\n", | ||
| 394 | le32_to_cpu(iwm->last_fw_err->sdio_status)); | ||
| 395 | |||
| 396 | out: | ||
| 397 | |||
| 398 | return simple_read_from_buffer(buffer, len, ppos, buf, buf_len); | ||
| 399 | } | ||
| 400 | |||
| 401 | static const struct file_operations iwm_debugfs_txq_fops = { | ||
| 402 | .owner = THIS_MODULE, | ||
| 403 | .open = iwm_generic_open, | ||
| 404 | .read = iwm_debugfs_txq_read, | ||
| 405 | .llseek = default_llseek, | ||
| 406 | }; | ||
| 407 | |||
| 408 | static const struct file_operations iwm_debugfs_tx_credit_fops = { | ||
| 409 | .owner = THIS_MODULE, | ||
| 410 | .open = iwm_generic_open, | ||
| 411 | .read = iwm_debugfs_tx_credit_read, | ||
| 412 | .llseek = default_llseek, | ||
| 413 | }; | ||
| 414 | |||
| 415 | static const struct file_operations iwm_debugfs_rx_ticket_fops = { | ||
| 416 | .owner = THIS_MODULE, | ||
| 417 | .open = iwm_generic_open, | ||
| 418 | .read = iwm_debugfs_rx_ticket_read, | ||
| 419 | .llseek = default_llseek, | ||
| 420 | }; | ||
| 421 | |||
| 422 | static const struct file_operations iwm_debugfs_fw_err_fops = { | ||
| 423 | .owner = THIS_MODULE, | ||
| 424 | .open = iwm_generic_open, | ||
| 425 | .read = iwm_debugfs_fw_err_read, | ||
| 426 | .llseek = default_llseek, | ||
| 427 | }; | ||
| 428 | |||
| 429 | void iwm_debugfs_init(struct iwm_priv *iwm) | ||
| 430 | { | ||
| 431 | int i; | ||
| 432 | |||
| 433 | iwm->dbg.rootdir = debugfs_create_dir(KBUILD_MODNAME, NULL); | ||
| 434 | iwm->dbg.devdir = debugfs_create_dir(wiphy_name(iwm_to_wiphy(iwm)), | ||
| 435 | iwm->dbg.rootdir); | ||
| 436 | iwm->dbg.dbgdir = debugfs_create_dir("debug", iwm->dbg.devdir); | ||
| 437 | iwm->dbg.rxdir = debugfs_create_dir("rx", iwm->dbg.devdir); | ||
| 438 | iwm->dbg.txdir = debugfs_create_dir("tx", iwm->dbg.devdir); | ||
| 439 | iwm->dbg.busdir = debugfs_create_dir("bus", iwm->dbg.devdir); | ||
| 440 | if (iwm->bus_ops->debugfs_init) | ||
| 441 | iwm->bus_ops->debugfs_init(iwm, iwm->dbg.busdir); | ||
| 442 | |||
| 443 | iwm->dbg.dbg_level = IWM_DL_NONE; | ||
| 444 | iwm->dbg.dbg_level_dentry = | ||
| 445 | debugfs_create_file("level", 0200, iwm->dbg.dbgdir, iwm, | ||
| 446 | &fops_iwm_dbg_level); | ||
| 447 | |||
| 448 | iwm->dbg.dbg_modules = IWM_DM_DEFAULT; | ||
| 449 | iwm->dbg.dbg_modules_dentry = | ||
| 450 | debugfs_create_file("modules", 0200, iwm->dbg.dbgdir, iwm, | ||
| 451 | &fops_iwm_dbg_modules); | ||
| 452 | |||
| 453 | for (i = 0; i < __IWM_DM_NR; i++) | ||
| 454 | add_dbg_module(iwm->dbg, iwm_debug_module[i].name, | ||
| 455 | iwm_debug_module[i].id, IWM_DL_DEFAULT); | ||
| 456 | |||
| 457 | iwm->dbg.txq_dentry = debugfs_create_file("queues", 0200, | ||
| 458 | iwm->dbg.txdir, iwm, | ||
| 459 | &iwm_debugfs_txq_fops); | ||
| 460 | iwm->dbg.tx_credit_dentry = debugfs_create_file("credits", 0200, | ||
| 461 | iwm->dbg.txdir, iwm, | ||
| 462 | &iwm_debugfs_tx_credit_fops); | ||
| 463 | iwm->dbg.rx_ticket_dentry = debugfs_create_file("tickets", 0200, | ||
| 464 | iwm->dbg.rxdir, iwm, | ||
| 465 | &iwm_debugfs_rx_ticket_fops); | ||
| 466 | iwm->dbg.fw_err_dentry = debugfs_create_file("last_fw_err", 0200, | ||
| 467 | iwm->dbg.dbgdir, iwm, | ||
| 468 | &iwm_debugfs_fw_err_fops); | ||
| 469 | } | ||
| 470 | |||
| 471 | void iwm_debugfs_exit(struct iwm_priv *iwm) | ||
| 472 | { | ||
| 473 | int i; | ||
| 474 | |||
| 475 | for (i = 0; i < __IWM_DM_NR; i++) | ||
| 476 | debugfs_remove(iwm->dbg.dbg_module_dentries[i]); | ||
| 477 | |||
| 478 | debugfs_remove(iwm->dbg.dbg_modules_dentry); | ||
| 479 | debugfs_remove(iwm->dbg.dbg_level_dentry); | ||
| 480 | debugfs_remove(iwm->dbg.txq_dentry); | ||
| 481 | debugfs_remove(iwm->dbg.tx_credit_dentry); | ||
| 482 | debugfs_remove(iwm->dbg.rx_ticket_dentry); | ||
| 483 | debugfs_remove(iwm->dbg.fw_err_dentry); | ||
| 484 | if (iwm->bus_ops->debugfs_exit) | ||
| 485 | iwm->bus_ops->debugfs_exit(iwm); | ||
| 486 | |||
| 487 | debugfs_remove(iwm->dbg.busdir); | ||
| 488 | debugfs_remove(iwm->dbg.dbgdir); | ||
| 489 | debugfs_remove(iwm->dbg.txdir); | ||
| 490 | debugfs_remove(iwm->dbg.rxdir); | ||
| 491 | debugfs_remove(iwm->dbg.devdir); | ||
| 492 | debugfs_remove(iwm->dbg.rootdir); | ||
| 493 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.c b/drivers/net/wireless/iwmc3200wifi/eeprom.c new file mode 100644 index 00000000000..e80e776b74f --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/eeprom.c | |||
| @@ -0,0 +1,234 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #include <linux/kernel.h> | ||
| 40 | #include <linux/slab.h> | ||
| 41 | |||
| 42 | #include "iwm.h" | ||
| 43 | #include "umac.h" | ||
| 44 | #include "commands.h" | ||
| 45 | #include "eeprom.h" | ||
| 46 | |||
| 47 | static struct iwm_eeprom_entry eeprom_map[] = { | ||
| 48 | [IWM_EEPROM_SIG] = | ||
| 49 | {"Signature", IWM_EEPROM_SIG_OFF, IWM_EEPROM_SIG_LEN}, | ||
| 50 | |||
| 51 | [IWM_EEPROM_VERSION] = | ||
| 52 | {"Version", IWM_EEPROM_VERSION_OFF, IWM_EEPROM_VERSION_LEN}, | ||
| 53 | |||
| 54 | [IWM_EEPROM_OEM_HW_VERSION] = | ||
| 55 | {"OEM HW version", IWM_EEPROM_OEM_HW_VERSION_OFF, | ||
| 56 | IWM_EEPROM_OEM_HW_VERSION_LEN}, | ||
| 57 | |||
| 58 | [IWM_EEPROM_MAC_VERSION] = | ||
| 59 | {"MAC version", IWM_EEPROM_MAC_VERSION_OFF, IWM_EEPROM_MAC_VERSION_LEN}, | ||
| 60 | |||
| 61 | [IWM_EEPROM_CARD_ID] = | ||
| 62 | {"Card ID", IWM_EEPROM_CARD_ID_OFF, IWM_EEPROM_CARD_ID_LEN}, | ||
| 63 | |||
| 64 | [IWM_EEPROM_RADIO_CONF] = | ||
| 65 | {"Radio config", IWM_EEPROM_RADIO_CONF_OFF, IWM_EEPROM_RADIO_CONF_LEN}, | ||
| 66 | |||
| 67 | [IWM_EEPROM_SKU_CAP] = | ||
| 68 | {"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN}, | ||
| 69 | |||
| 70 | [IWM_EEPROM_FAT_CHANNELS_CAP] = | ||
| 71 | {"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF, | ||
| 72 | IWM_EEPROM_FAT_CHANNELS_CAP_LEN}, | ||
| 73 | |||
| 74 | [IWM_EEPROM_CALIB_RXIQ_OFFSET] = | ||
| 75 | {"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN}, | ||
| 76 | |||
| 77 | [IWM_EEPROM_CALIB_RXIQ] = | ||
| 78 | {"Calib RX IQ", 0, IWM_EEPROM_CALIB_RXIQ_LEN}, | ||
| 79 | }; | ||
| 80 | |||
| 81 | |||
| 82 | static int iwm_eeprom_read(struct iwm_priv *iwm, u8 eeprom_id) | ||
| 83 | { | ||
| 84 | int ret; | ||
| 85 | u32 entry_size, chunk_size, data_offset = 0, addr_offset = 0; | ||
| 86 | u32 addr; | ||
| 87 | struct iwm_udma_wifi_cmd udma_cmd; | ||
| 88 | struct iwm_umac_cmd umac_cmd; | ||
| 89 | struct iwm_umac_cmd_eeprom_proxy eeprom_cmd; | ||
| 90 | |||
| 91 | if (eeprom_id > (IWM_EEPROM_LAST - 1)) | ||
| 92 | return -EINVAL; | ||
| 93 | |||
| 94 | entry_size = eeprom_map[eeprom_id].length; | ||
| 95 | |||
| 96 | if (eeprom_id >= IWM_EEPROM_INDIRECT_DATA) { | ||
| 97 | /* indirect data */ | ||
| 98 | u32 off_id = eeprom_id - IWM_EEPROM_INDIRECT_DATA + | ||
| 99 | IWM_EEPROM_INDIRECT_OFFSET; | ||
| 100 | |||
| 101 | eeprom_map[eeprom_id].offset = | ||
| 102 | *(u16 *)(iwm->eeprom + eeprom_map[off_id].offset) << 1; | ||
| 103 | } | ||
| 104 | |||
| 105 | addr = eeprom_map[eeprom_id].offset; | ||
| 106 | |||
| 107 | udma_cmd.eop = 1; | ||
| 108 | udma_cmd.credit_group = 0x4; | ||
| 109 | udma_cmd.ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD; | ||
| 110 | udma_cmd.lmac_offset = 0; | ||
| 111 | |||
| 112 | umac_cmd.id = UMAC_CMD_OPCODE_EEPROM_PROXY; | ||
| 113 | umac_cmd.resp = 1; | ||
| 114 | |||
| 115 | while (entry_size > 0) { | ||
| 116 | chunk_size = min_t(u32, entry_size, IWM_MAX_EEPROM_DATA_LEN); | ||
| 117 | |||
| 118 | eeprom_cmd.hdr.type = | ||
| 119 | cpu_to_le32(IWM_UMAC_CMD_EEPROM_TYPE_READ); | ||
| 120 | eeprom_cmd.hdr.offset = cpu_to_le32(addr + addr_offset); | ||
| 121 | eeprom_cmd.hdr.len = cpu_to_le32(chunk_size); | ||
| 122 | |||
| 123 | ret = iwm_hal_send_umac_cmd(iwm, &udma_cmd, | ||
| 124 | &umac_cmd, &eeprom_cmd, | ||
| 125 | sizeof(struct iwm_umac_cmd_eeprom_proxy)); | ||
| 126 | if (ret < 0) { | ||
| 127 | IWM_ERR(iwm, "Couldn't read eeprom\n"); | ||
| 128 | return ret; | ||
| 129 | } | ||
| 130 | |||
| 131 | ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_EEPROM_PROXY, | ||
| 132 | IWM_SRC_UMAC, 2*HZ); | ||
| 133 | if (ret < 0) { | ||
| 134 | IWM_ERR(iwm, "Did not get any eeprom answer\n"); | ||
| 135 | return ret; | ||
| 136 | } | ||
| 137 | |||
| 138 | data_offset += chunk_size; | ||
| 139 | addr_offset += chunk_size; | ||
| 140 | entry_size -= chunk_size; | ||
| 141 | } | ||
| 142 | |||
| 143 | return 0; | ||
| 144 | } | ||
| 145 | |||
| 146 | u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id) | ||
| 147 | { | ||
| 148 | if (!iwm->eeprom) | ||
| 149 | return ERR_PTR(-ENODEV); | ||
| 150 | |||
| 151 | return iwm->eeprom + eeprom_map[eeprom_id].offset; | ||
| 152 | } | ||
| 153 | |||
| 154 | int iwm_eeprom_fat_channels(struct iwm_priv *iwm) | ||
| 155 | { | ||
| 156 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 157 | struct ieee80211_supported_band *band; | ||
| 158 | u16 *channels, i; | ||
| 159 | |||
| 160 | channels = (u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_FAT_CHANNELS_CAP); | ||
| 161 | if (IS_ERR(channels)) | ||
| 162 | return PTR_ERR(channels); | ||
| 163 | |||
| 164 | band = wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
| 165 | band->ht_cap.ht_supported = true; | ||
| 166 | |||
| 167 | for (i = 0; i < IWM_EEPROM_FAT_CHANNELS_24; i++) | ||
| 168 | if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) | ||
| 169 | band->ht_cap.ht_supported = false; | ||
| 170 | |||
| 171 | band = wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
| 172 | band->ht_cap.ht_supported = true; | ||
| 173 | for (i = IWM_EEPROM_FAT_CHANNELS_24; i < IWM_EEPROM_FAT_CHANNELS; i++) | ||
| 174 | if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) | ||
| 175 | band->ht_cap.ht_supported = false; | ||
| 176 | |||
| 177 | return 0; | ||
| 178 | } | ||
| 179 | |||
| 180 | u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm) | ||
| 181 | { | ||
| 182 | u16 sku_cap; | ||
| 183 | u32 wireless_mode = 0; | ||
| 184 | |||
| 185 | sku_cap = *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP)); | ||
| 186 | |||
| 187 | if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_24GHZ) | ||
| 188 | wireless_mode |= WIRELESS_MODE_11G; | ||
| 189 | |||
| 190 | if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_52GHZ) | ||
| 191 | wireless_mode |= WIRELESS_MODE_11A; | ||
| 192 | |||
| 193 | if (sku_cap & IWM_EEPROM_SKU_CAP_11N_ENABLE) | ||
| 194 | wireless_mode |= WIRELESS_MODE_11N; | ||
| 195 | |||
| 196 | return wireless_mode; | ||
| 197 | } | ||
| 198 | |||
| 199 | |||
| 200 | int iwm_eeprom_init(struct iwm_priv *iwm) | ||
| 201 | { | ||
| 202 | int i, ret = 0; | ||
| 203 | char name[32]; | ||
| 204 | |||
| 205 | iwm->eeprom = kzalloc(IWM_EEPROM_LEN, GFP_KERNEL); | ||
| 206 | if (!iwm->eeprom) | ||
| 207 | return -ENOMEM; | ||
| 208 | |||
| 209 | for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) { | ||
| 210 | ret = iwm_eeprom_read(iwm, i); | ||
| 211 | if (ret < 0) { | ||
| 212 | IWM_ERR(iwm, "Couldn't read eeprom entry #%d: %s\n", | ||
| 213 | i, eeprom_map[i].name); | ||
| 214 | break; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | IWM_DBG_BOOT(iwm, DBG, "EEPROM dump:\n"); | ||
| 219 | for (i = IWM_EEPROM_FIRST; i < IWM_EEPROM_LAST; i++) { | ||
| 220 | memset(name, 0, 32); | ||
| 221 | sprintf(name, "%s: ", eeprom_map[i].name); | ||
| 222 | |||
| 223 | IWM_HEXDUMP(iwm, DBG, BOOT, name, | ||
| 224 | iwm->eeprom + eeprom_map[i].offset, | ||
| 225 | eeprom_map[i].length); | ||
| 226 | } | ||
| 227 | |||
| 228 | return ret; | ||
| 229 | } | ||
| 230 | |||
| 231 | void iwm_eeprom_exit(struct iwm_priv *iwm) | ||
| 232 | { | ||
| 233 | kfree(iwm->eeprom); | ||
| 234 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.h b/drivers/net/wireless/iwmc3200wifi/eeprom.h new file mode 100644 index 00000000000..4e3a3fdab0d --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/eeprom.h | |||
| @@ -0,0 +1,127 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_EEPROM_H__ | ||
| 40 | #define __IWM_EEPROM_H__ | ||
| 41 | |||
| 42 | enum { | ||
| 43 | IWM_EEPROM_SIG = 0, | ||
| 44 | IWM_EEPROM_FIRST = IWM_EEPROM_SIG, | ||
| 45 | IWM_EEPROM_VERSION, | ||
| 46 | IWM_EEPROM_OEM_HW_VERSION, | ||
| 47 | IWM_EEPROM_MAC_VERSION, | ||
| 48 | IWM_EEPROM_CARD_ID, | ||
| 49 | IWM_EEPROM_RADIO_CONF, | ||
| 50 | IWM_EEPROM_SKU_CAP, | ||
| 51 | IWM_EEPROM_FAT_CHANNELS_CAP, | ||
| 52 | |||
| 53 | IWM_EEPROM_INDIRECT_OFFSET, | ||
| 54 | IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET, | ||
| 55 | |||
| 56 | IWM_EEPROM_INDIRECT_DATA, | ||
| 57 | IWM_EEPROM_CALIB_RXIQ = IWM_EEPROM_INDIRECT_DATA, | ||
| 58 | |||
| 59 | IWM_EEPROM_LAST, | ||
| 60 | }; | ||
| 61 | |||
| 62 | #define IWM_EEPROM_SIG_OFF 0x00 | ||
| 63 | #define IWM_EEPROM_VERSION_OFF (0x54 << 1) | ||
| 64 | #define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1) | ||
| 65 | #define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1) | ||
| 66 | #define IWM_EEPROM_CARD_ID_OFF (0x5d << 1) | ||
| 67 | #define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1) | ||
| 68 | #define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1) | ||
| 69 | #define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1) | ||
| 70 | #define IWM_EEPROM_FAT_CHANNELS_CAP_OFF (0xde << 1) | ||
| 71 | |||
| 72 | #define IWM_EEPROM_SIG_LEN 4 | ||
| 73 | #define IWM_EEPROM_VERSION_LEN 2 | ||
| 74 | #define IWM_EEPROM_OEM_HW_VERSION_LEN 2 | ||
| 75 | #define IWM_EEPROM_MAC_VERSION_LEN 1 | ||
| 76 | #define IWM_EEPROM_CARD_ID_LEN 2 | ||
| 77 | #define IWM_EEPROM_RADIO_CONF_LEN 2 | ||
| 78 | #define IWM_EEPROM_SKU_CAP_LEN 2 | ||
| 79 | #define IWM_EEPROM_FAT_CHANNELS_CAP_LEN 40 | ||
| 80 | #define IWM_EEPROM_INDIRECT_LEN 2 | ||
| 81 | |||
| 82 | #define IWM_MAX_EEPROM_DATA_LEN 240 | ||
| 83 | #define IWM_EEPROM_LEN 0x800 | ||
| 84 | |||
| 85 | #define IWM_EEPROM_MIN_ALLOWED_VERSION 0x0610 | ||
| 86 | #define IWM_EEPROM_MAX_ALLOWED_VERSION 0x0700 | ||
| 87 | #define IWM_EEPROM_CURRENT_VERSION 0x0612 | ||
| 88 | |||
| 89 | #define IWM_EEPROM_SKU_CAP_BAND_24GHZ (1 << 4) | ||
| 90 | #define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) | ||
| 91 | #define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6) | ||
| 92 | |||
| 93 | #define IWM_EEPROM_FAT_CHANNELS 20 | ||
| 94 | /* 2.4 gHz FAT primary channels: 1, 2, 3, 4, 5, 6, 7, 8, 9 */ | ||
| 95 | #define IWM_EEPROM_FAT_CHANNELS_24 9 | ||
| 96 | /* 5.2 gHz FAT primary channels: 36,44,52,60,100,108,116,124,132,149,157 */ | ||
| 97 | #define IWM_EEPROM_FAT_CHANNELS_52 11 | ||
| 98 | |||
| 99 | #define IWM_EEPROM_FAT_CHANNEL_ENABLED (1 << 0) | ||
| 100 | |||
| 101 | enum { | ||
| 102 | IWM_EEPROM_CALIB_CAL_HDR, | ||
| 103 | IWM_EEPROM_CALIB_TX_POWER, | ||
| 104 | IWM_EEPROM_CALIB_XTAL, | ||
| 105 | IWM_EEPROM_CALIB_TEMPERATURE, | ||
| 106 | IWM_EEPROM_CALIB_RX_BB_FILTER, | ||
| 107 | IWM_EEPROM_CALIB_RX_IQ, | ||
| 108 | IWM_EEPROM_CALIB_MAX, | ||
| 109 | }; | ||
| 110 | |||
| 111 | #define IWM_EEPROM_CALIB_RXIQ_OFF (IWM_EEPROM_CALIB_CONFIG_OFF + \ | ||
| 112 | (IWM_EEPROM_CALIB_RX_IQ << 1)) | ||
| 113 | #define IWM_EEPROM_CALIB_RXIQ_LEN sizeof(struct iwm_lmac_calib_rxiq) | ||
| 114 | |||
| 115 | struct iwm_eeprom_entry { | ||
| 116 | char *name; | ||
| 117 | u32 offset; | ||
| 118 | u32 length; | ||
| 119 | }; | ||
| 120 | |||
| 121 | int iwm_eeprom_init(struct iwm_priv *iwm); | ||
| 122 | void iwm_eeprom_exit(struct iwm_priv *iwm); | ||
| 123 | u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id); | ||
| 124 | int iwm_eeprom_fat_channels(struct iwm_priv *iwm); | ||
| 125 | u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm); | ||
| 126 | |||
| 127 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.c b/drivers/net/wireless/iwmc3200wifi/fw.c new file mode 100644 index 00000000000..6f1afe6bbc8 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/fw.c | |||
| @@ -0,0 +1,416 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #include <linux/kernel.h> | ||
| 40 | #include <linux/firmware.h> | ||
| 41 | |||
| 42 | #include "iwm.h" | ||
| 43 | #include "bus.h" | ||
| 44 | #include "hal.h" | ||
| 45 | #include "umac.h" | ||
| 46 | #include "debug.h" | ||
| 47 | #include "fw.h" | ||
| 48 | #include "commands.h" | ||
| 49 | |||
| 50 | static const char fw_barker[] = "*WESTOPFORNOONE*"; | ||
| 51 | |||
| 52 | /* | ||
| 53 | * @op_code: Op code we're looking for. | ||
| 54 | * @index: There can be several instances of the same opcode within | ||
| 55 | * the firmware. Index specifies which one we're looking for. | ||
| 56 | */ | ||
| 57 | static int iwm_fw_op_offset(struct iwm_priv *iwm, const struct firmware *fw, | ||
| 58 | u16 op_code, u32 index) | ||
| 59 | { | ||
| 60 | int offset = -EINVAL, fw_offset; | ||
| 61 | u32 op_index = 0; | ||
| 62 | const u8 *fw_ptr; | ||
| 63 | struct iwm_fw_hdr_rec *rec; | ||
| 64 | |||
| 65 | fw_offset = 0; | ||
| 66 | fw_ptr = fw->data; | ||
| 67 | |||
| 68 | /* We first need to look for the firmware barker */ | ||
| 69 | if (memcmp(fw_ptr, fw_barker, IWM_HDR_BARKER_LEN)) { | ||
| 70 | IWM_ERR(iwm, "No barker string in this FW\n"); | ||
| 71 | return -EINVAL; | ||
| 72 | } | ||
| 73 | |||
| 74 | if (fw->size < IWM_HDR_LEN) { | ||
| 75 | IWM_ERR(iwm, "FW is too small (%zu)\n", fw->size); | ||
| 76 | return -EINVAL; | ||
| 77 | } | ||
| 78 | |||
| 79 | fw_offset += IWM_HDR_BARKER_LEN; | ||
| 80 | |||
| 81 | while (fw_offset < fw->size) { | ||
| 82 | rec = (struct iwm_fw_hdr_rec *)(fw_ptr + fw_offset); | ||
| 83 | |||
| 84 | IWM_DBG_FW(iwm, DBG, "FW: op_code: 0x%x, len: %d @ 0x%x\n", | ||
| 85 | rec->op_code, rec->len, fw_offset); | ||
| 86 | |||
| 87 | if (rec->op_code == IWM_HDR_REC_OP_INVALID) { | ||
| 88 | IWM_DBG_FW(iwm, DBG, "Reached INVALID op code\n"); | ||
| 89 | break; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (rec->op_code == op_code) { | ||
| 93 | if (op_index == index) { | ||
| 94 | fw_offset += sizeof(struct iwm_fw_hdr_rec); | ||
| 95 | offset = fw_offset; | ||
| 96 | goto out; | ||
| 97 | } | ||
| 98 | op_index++; | ||
| 99 | } | ||
| 100 | |||
| 101 | fw_offset += sizeof(struct iwm_fw_hdr_rec) + rec->len; | ||
| 102 | } | ||
| 103 | |||
| 104 | out: | ||
| 105 | return offset; | ||
| 106 | } | ||
| 107 | |||
| 108 | static int iwm_load_firmware_chunk(struct iwm_priv *iwm, | ||
| 109 | const struct firmware *fw, | ||
| 110 | struct iwm_fw_img_desc *img_desc) | ||
| 111 | { | ||
| 112 | struct iwm_udma_nonwifi_cmd target_cmd; | ||
| 113 | u32 chunk_size; | ||
| 114 | const u8 *chunk_ptr; | ||
| 115 | int ret = 0; | ||
| 116 | |||
| 117 | IWM_DBG_FW(iwm, INFO, "Loading FW chunk: %d bytes @ 0x%x\n", | ||
| 118 | img_desc->length, img_desc->address); | ||
| 119 | |||
| 120 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; | ||
| 121 | target_cmd.handle_by_hw = 1; | ||
| 122 | target_cmd.op2 = 0; | ||
| 123 | target_cmd.resp = 0; | ||
| 124 | target_cmd.eop = 1; | ||
| 125 | |||
| 126 | chunk_size = img_desc->length; | ||
| 127 | chunk_ptr = fw->data + img_desc->offset; | ||
| 128 | |||
| 129 | while (chunk_size > 0) { | ||
| 130 | u32 tmp_chunk_size; | ||
| 131 | |||
| 132 | tmp_chunk_size = min_t(u32, chunk_size, | ||
| 133 | IWM_MAX_NONWIFI_CMD_BUFF_SIZE); | ||
| 134 | |||
| 135 | target_cmd.addr = cpu_to_le32(img_desc->address + | ||
| 136 | (chunk_ptr - fw->data - img_desc->offset)); | ||
| 137 | target_cmd.op1_sz = cpu_to_le32(tmp_chunk_size); | ||
| 138 | |||
| 139 | IWM_DBG_FW(iwm, DBG, "\t%d bytes @ 0x%x\n", | ||
| 140 | tmp_chunk_size, target_cmd.addr); | ||
| 141 | |||
| 142 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, chunk_ptr); | ||
| 143 | if (ret < 0) { | ||
| 144 | IWM_ERR(iwm, "Couldn't load FW chunk\n"); | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | |||
| 148 | chunk_size -= tmp_chunk_size; | ||
| 149 | chunk_ptr += tmp_chunk_size; | ||
| 150 | } | ||
| 151 | |||
| 152 | return ret; | ||
| 153 | } | ||
| 154 | /* | ||
| 155 | * To load a fw image to the target, we basically go through the | ||
| 156 | * fw, looking for OP_MEM_DESC records. Once we found one, we | ||
| 157 | * pass it to iwm_load_firmware_chunk(). | ||
| 158 | * The OP_MEM_DESC records contain the actuall memory chunk to be | ||
| 159 | * sent, but also the destination address. | ||
| 160 | */ | ||
| 161 | static int iwm_load_img(struct iwm_priv *iwm, const char *img_name) | ||
| 162 | { | ||
| 163 | const struct firmware *fw; | ||
| 164 | struct iwm_fw_img_desc *img_desc; | ||
| 165 | struct iwm_fw_img_ver *ver; | ||
| 166 | int ret = 0, fw_offset; | ||
| 167 | u32 opcode_idx = 0, build_date; | ||
| 168 | char *build_tag; | ||
| 169 | |||
| 170 | ret = request_firmware(&fw, img_name, iwm_to_dev(iwm)); | ||
| 171 | if (ret) { | ||
| 172 | IWM_ERR(iwm, "Request firmware failed"); | ||
| 173 | return ret; | ||
| 174 | } | ||
| 175 | |||
| 176 | IWM_DBG_FW(iwm, INFO, "Start to load FW %s\n", img_name); | ||
| 177 | |||
| 178 | while (1) { | ||
| 179 | fw_offset = iwm_fw_op_offset(iwm, fw, | ||
| 180 | IWM_HDR_REC_OP_MEM_DESC, | ||
| 181 | opcode_idx); | ||
| 182 | if (fw_offset < 0) | ||
| 183 | break; | ||
| 184 | |||
| 185 | img_desc = (struct iwm_fw_img_desc *)(fw->data + fw_offset); | ||
| 186 | ret = iwm_load_firmware_chunk(iwm, fw, img_desc); | ||
| 187 | if (ret < 0) | ||
| 188 | goto err_release_fw; | ||
| 189 | opcode_idx++; | ||
| 190 | } | ||
| 191 | |||
| 192 | /* Read firmware version */ | ||
| 193 | fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_SW_VER, 0); | ||
| 194 | if (fw_offset < 0) | ||
| 195 | goto err_release_fw; | ||
| 196 | |||
| 197 | ver = (struct iwm_fw_img_ver *)(fw->data + fw_offset); | ||
| 198 | |||
| 199 | /* Read build tag */ | ||
| 200 | fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_TAG, 0); | ||
| 201 | if (fw_offset < 0) | ||
| 202 | goto err_release_fw; | ||
| 203 | |||
| 204 | build_tag = (char *)(fw->data + fw_offset); | ||
| 205 | |||
| 206 | /* Read build date */ | ||
| 207 | fw_offset = iwm_fw_op_offset(iwm, fw, IWM_HDR_REC_OP_BUILD_DATE, 0); | ||
| 208 | if (fw_offset < 0) | ||
| 209 | goto err_release_fw; | ||
| 210 | |||
| 211 | build_date = *(u32 *)(fw->data + fw_offset); | ||
| 212 | |||
| 213 | IWM_INFO(iwm, "%s:\n", img_name); | ||
| 214 | IWM_INFO(iwm, "\tVersion: %02X.%02X\n", ver->major, ver->minor); | ||
| 215 | IWM_INFO(iwm, "\tBuild tag: %s\n", build_tag); | ||
| 216 | IWM_INFO(iwm, "\tBuild date: %x-%x-%x\n", | ||
| 217 | IWM_BUILD_YEAR(build_date), IWM_BUILD_MONTH(build_date), | ||
| 218 | IWM_BUILD_DAY(build_date)); | ||
| 219 | |||
| 220 | if (!strcmp(img_name, iwm->bus_ops->umac_name)) | ||
| 221 | sprintf(iwm->umac_version, "%02X.%02X", | ||
| 222 | ver->major, ver->minor); | ||
| 223 | |||
| 224 | if (!strcmp(img_name, iwm->bus_ops->lmac_name)) | ||
| 225 | sprintf(iwm->lmac_version, "%02X.%02X", | ||
| 226 | ver->major, ver->minor); | ||
| 227 | |||
| 228 | err_release_fw: | ||
| 229 | release_firmware(fw); | ||
| 230 | |||
| 231 | return ret; | ||
| 232 | } | ||
| 233 | |||
| 234 | static int iwm_load_umac(struct iwm_priv *iwm) | ||
| 235 | { | ||
| 236 | struct iwm_udma_nonwifi_cmd target_cmd; | ||
| 237 | int ret; | ||
| 238 | |||
| 239 | ret = iwm_load_img(iwm, iwm->bus_ops->umac_name); | ||
| 240 | if (ret < 0) | ||
| 241 | return ret; | ||
| 242 | |||
| 243 | /* We've loaded the UMAC, we can tell the target to jump there */ | ||
| 244 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_JUMP; | ||
| 245 | target_cmd.addr = cpu_to_le32(UMAC_MU_FW_INST_DATA_12_ADDR); | ||
| 246 | target_cmd.op1_sz = 0; | ||
| 247 | target_cmd.op2 = 0; | ||
| 248 | target_cmd.handle_by_hw = 0; | ||
| 249 | target_cmd.resp = 1 ; | ||
| 250 | target_cmd.eop = 1; | ||
| 251 | |||
| 252 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); | ||
| 253 | if (ret < 0) | ||
| 254 | IWM_ERR(iwm, "Couldn't send JMP command\n"); | ||
| 255 | |||
| 256 | return ret; | ||
| 257 | } | ||
| 258 | |||
| 259 | static int iwm_load_lmac(struct iwm_priv *iwm, const char *img_name) | ||
| 260 | { | ||
| 261 | int ret; | ||
| 262 | |||
| 263 | ret = iwm_load_img(iwm, img_name); | ||
| 264 | if (ret < 0) | ||
| 265 | return ret; | ||
| 266 | |||
| 267 | return iwm_send_umac_reset(iwm, | ||
| 268 | cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_CLK_EN), 0); | ||
| 269 | } | ||
| 270 | |||
| 271 | static int iwm_init_calib(struct iwm_priv *iwm, unsigned long cfg_bitmap, | ||
| 272 | unsigned long expected_bitmap, u8 rx_iq_cmd) | ||
| 273 | { | ||
| 274 | /* Read RX IQ calibration result from EEPROM */ | ||
| 275 | if (test_bit(rx_iq_cmd, &cfg_bitmap)) { | ||
| 276 | iwm_store_rxiq_calib_result(iwm); | ||
| 277 | set_bit(PHY_CALIBRATE_RX_IQ_CMD, &iwm->calib_done_map); | ||
| 278 | } | ||
| 279 | |||
| 280 | iwm_send_prio_table(iwm); | ||
| 281 | iwm_send_init_calib_cfg(iwm, cfg_bitmap); | ||
| 282 | |||
| 283 | while (iwm->calib_done_map != expected_bitmap) { | ||
| 284 | if (iwm_notif_handle(iwm, CALIBRATION_RES_NOTIFICATION, | ||
| 285 | IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT)) { | ||
| 286 | IWM_DBG_FW(iwm, DBG, "Initial calibration timeout\n"); | ||
| 287 | return -ETIMEDOUT; | ||
| 288 | } | ||
| 289 | |||
| 290 | IWM_DBG_FW(iwm, DBG, "Got calibration result. calib_done_map: " | ||
| 291 | "0x%lx, expected calibrations: 0x%lx\n", | ||
| 292 | iwm->calib_done_map, expected_bitmap); | ||
| 293 | } | ||
| 294 | |||
| 295 | return 0; | ||
| 296 | } | ||
| 297 | |||
| 298 | /* | ||
| 299 | * We currently have to load 3 FWs: | ||
| 300 | * 1) The UMAC (Upper MAC). | ||
| 301 | * 2) The calibration LMAC (Lower MAC). | ||
| 302 | * We then send the calibration init command, so that the device can | ||
| 303 | * run a first calibration round. | ||
| 304 | * 3) The operational LMAC, which replaces the calibration one when it's | ||
| 305 | * done with the first calibration round. | ||
| 306 | * | ||
| 307 | * Once those 3 FWs have been loaded, we send the periodic calibration | ||
| 308 | * command, and then the device is available for regular 802.11 operations. | ||
| 309 | */ | ||
| 310 | int iwm_load_fw(struct iwm_priv *iwm) | ||
| 311 | { | ||
| 312 | unsigned long init_calib_map, periodic_calib_map; | ||
| 313 | unsigned long expected_calib_map; | ||
| 314 | int ret; | ||
| 315 | |||
| 316 | /* We first start downloading the UMAC */ | ||
| 317 | ret = iwm_load_umac(iwm); | ||
| 318 | if (ret < 0) { | ||
| 319 | IWM_ERR(iwm, "UMAC loading failed\n"); | ||
| 320 | return ret; | ||
| 321 | } | ||
| 322 | |||
| 323 | /* Handle UMAC_ALIVE notification */ | ||
| 324 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_ALIVE, IWM_SRC_UMAC, | ||
| 325 | WAIT_NOTIF_TIMEOUT); | ||
| 326 | if (ret) { | ||
| 327 | IWM_ERR(iwm, "Handle UMAC_ALIVE failed: %d\n", ret); | ||
| 328 | return ret; | ||
| 329 | } | ||
| 330 | |||
| 331 | /* UMAC is alive, we can download the calibration LMAC */ | ||
| 332 | ret = iwm_load_lmac(iwm, iwm->bus_ops->calib_lmac_name); | ||
| 333 | if (ret) { | ||
| 334 | IWM_ERR(iwm, "Calibration LMAC loading failed\n"); | ||
| 335 | return ret; | ||
| 336 | } | ||
| 337 | |||
| 338 | /* Handle UMAC_INIT_COMPLETE notification */ | ||
| 339 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE, | ||
| 340 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
| 341 | if (ret) { | ||
| 342 | IWM_ERR(iwm, "Handle INIT_COMPLETE failed for calibration " | ||
| 343 | "LMAC: %d\n", ret); | ||
| 344 | return ret; | ||
| 345 | } | ||
| 346 | |||
| 347 | /* Read EEPROM data */ | ||
| 348 | ret = iwm_eeprom_init(iwm); | ||
| 349 | if (ret < 0) { | ||
| 350 | IWM_ERR(iwm, "Couldn't init eeprom array\n"); | ||
| 351 | return ret; | ||
| 352 | } | ||
| 353 | |||
| 354 | init_calib_map = iwm->conf.calib_map & IWM_CALIB_MAP_INIT_MSK; | ||
| 355 | expected_calib_map = iwm->conf.expected_calib_map & | ||
| 356 | IWM_CALIB_MAP_INIT_MSK; | ||
| 357 | periodic_calib_map = IWM_CALIB_MAP_PER_LMAC(iwm->conf.calib_map); | ||
| 358 | |||
| 359 | ret = iwm_init_calib(iwm, init_calib_map, expected_calib_map, | ||
| 360 | CALIB_CFG_RX_IQ_IDX); | ||
| 361 | if (ret < 0) { | ||
| 362 | /* Let's try the old way */ | ||
| 363 | ret = iwm_init_calib(iwm, expected_calib_map, | ||
| 364 | expected_calib_map, | ||
| 365 | PHY_CALIBRATE_RX_IQ_CMD); | ||
| 366 | if (ret < 0) { | ||
| 367 | IWM_ERR(iwm, "Calibration result timeout\n"); | ||
| 368 | goto out; | ||
| 369 | } | ||
| 370 | } | ||
| 371 | |||
| 372 | /* Handle LMAC CALIBRATION_COMPLETE notification */ | ||
| 373 | ret = iwm_notif_handle(iwm, CALIBRATION_COMPLETE_NOTIFICATION, | ||
| 374 | IWM_SRC_LMAC, WAIT_NOTIF_TIMEOUT); | ||
| 375 | if (ret) { | ||
| 376 | IWM_ERR(iwm, "Wait for CALIBRATION_COMPLETE timeout\n"); | ||
| 377 | goto out; | ||
| 378 | } | ||
| 379 | |||
| 380 | IWM_INFO(iwm, "LMAC calibration done: 0x%lx\n", iwm->calib_done_map); | ||
| 381 | |||
| 382 | iwm_send_umac_reset(iwm, cpu_to_le32(UMAC_RST_CTRL_FLG_LARC_RESET), 1); | ||
| 383 | |||
| 384 | ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_RESET, IWM_SRC_UMAC, | ||
| 385 | WAIT_NOTIF_TIMEOUT); | ||
| 386 | if (ret) { | ||
| 387 | IWM_ERR(iwm, "Wait for UMAC RESET timeout\n"); | ||
| 388 | goto out; | ||
| 389 | } | ||
| 390 | |||
| 391 | /* Download the operational LMAC */ | ||
| 392 | ret = iwm_load_lmac(iwm, iwm->bus_ops->lmac_name); | ||
| 393 | if (ret) { | ||
| 394 | IWM_ERR(iwm, "LMAC loading failed\n"); | ||
| 395 | goto out; | ||
| 396 | } | ||
| 397 | |||
| 398 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_INIT_COMPLETE, | ||
| 399 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
| 400 | if (ret) { | ||
| 401 | IWM_ERR(iwm, "Handle INIT_COMPLETE failed for LMAC: %d\n", ret); | ||
| 402 | goto out; | ||
| 403 | } | ||
| 404 | |||
| 405 | iwm_send_prio_table(iwm); | ||
| 406 | iwm_send_calib_results(iwm); | ||
| 407 | iwm_send_periodic_calib_cfg(iwm, periodic_calib_map); | ||
| 408 | iwm_send_ct_kill_cfg(iwm, iwm->conf.ct_kill_entry, | ||
| 409 | iwm->conf.ct_kill_exit); | ||
| 410 | |||
| 411 | return 0; | ||
| 412 | |||
| 413 | out: | ||
| 414 | iwm_eeprom_exit(iwm); | ||
| 415 | return ret; | ||
| 416 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/fw.h b/drivers/net/wireless/iwmc3200wifi/fw.h new file mode 100644 index 00000000000..c70a3b40dad --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/fw.h | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_FW_H__ | ||
| 40 | #define __IWM_FW_H__ | ||
| 41 | |||
| 42 | /** | ||
| 43 | * struct iwm_fw_hdr_rec - An iwm firmware image is a | ||
| 44 | * concatenation of various records. Each of them is | ||
| 45 | * defined by an ID (aka op code), a length, and the | ||
| 46 | * actual data. | ||
| 47 | * @op_code: The record ID, see IWM_HDR_REC_OP_* | ||
| 48 | * | ||
| 49 | * @len: The record payload length | ||
| 50 | * | ||
| 51 | * @buf: The record payload | ||
| 52 | */ | ||
| 53 | struct iwm_fw_hdr_rec { | ||
| 54 | u16 op_code; | ||
| 55 | u16 len; | ||
| 56 | u8 buf[0]; | ||
| 57 | }; | ||
| 58 | |||
| 59 | /* Header's definitions */ | ||
| 60 | #define IWM_HDR_LEN (512) | ||
| 61 | #define IWM_HDR_BARKER_LEN (16) | ||
| 62 | |||
| 63 | /* Header's opcodes */ | ||
| 64 | #define IWM_HDR_REC_OP_INVALID (0x00) | ||
| 65 | #define IWM_HDR_REC_OP_BUILD_DATE (0x01) | ||
| 66 | #define IWM_HDR_REC_OP_BUILD_TAG (0x02) | ||
| 67 | #define IWM_HDR_REC_OP_SW_VER (0x03) | ||
| 68 | #define IWM_HDR_REC_OP_HW_SKU (0x04) | ||
| 69 | #define IWM_HDR_REC_OP_BUILD_OPT (0x05) | ||
| 70 | #define IWM_HDR_REC_OP_MEM_DESC (0x06) | ||
| 71 | #define IWM_HDR_REC_USERDEFS (0x07) | ||
| 72 | |||
| 73 | /* Header's records length (in bytes) */ | ||
| 74 | #define IWM_HDR_REC_LEN_BUILD_DATE (4) | ||
| 75 | #define IWM_HDR_REC_LEN_BUILD_TAG (64) | ||
| 76 | #define IWM_HDR_REC_LEN_SW_VER (4) | ||
| 77 | #define IWM_HDR_REC_LEN_HW_SKU (4) | ||
| 78 | #define IWM_HDR_REC_LEN_BUILD_OPT (4) | ||
| 79 | #define IWM_HDR_REC_LEN_MEM_DESC (12) | ||
| 80 | #define IWM_HDR_REC_LEN_USERDEF (64) | ||
| 81 | |||
| 82 | #define IWM_BUILD_YEAR(date) ((date >> 16) & 0xffff) | ||
| 83 | #define IWM_BUILD_MONTH(date) ((date >> 8) & 0xff) | ||
| 84 | #define IWM_BUILD_DAY(date) (date & 0xff) | ||
| 85 | |||
| 86 | struct iwm_fw_img_desc { | ||
| 87 | u32 offset; | ||
| 88 | u32 address; | ||
| 89 | u32 length; | ||
| 90 | }; | ||
| 91 | |||
| 92 | struct iwm_fw_img_ver { | ||
| 93 | u8 minor; | ||
| 94 | u8 major; | ||
| 95 | u16 reserved; | ||
| 96 | }; | ||
| 97 | |||
| 98 | int iwm_load_fw(struct iwm_priv *iwm); | ||
| 99 | |||
| 100 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/hal.c b/drivers/net/wireless/iwmc3200wifi/hal.c new file mode 100644 index 00000000000..1cabcb39643 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/hal.c | |||
| @@ -0,0 +1,470 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | /* | ||
| 40 | * Hardware Abstraction Layer for iwm. | ||
| 41 | * | ||
| 42 | * This file mostly defines an abstraction API for | ||
| 43 | * sending various commands to the target. | ||
| 44 | * | ||
| 45 | * We have 2 types of commands: wifi and non-wifi ones. | ||
| 46 | * | ||
| 47 | * - wifi commands: | ||
| 48 | * They are used for sending LMAC and UMAC commands, | ||
| 49 | * and thus are the most commonly used ones. | ||
| 50 | * There are 2 different wifi command types, the regular | ||
| 51 | * one and the LMAC one. The former is used to send | ||
| 52 | * UMAC commands (see UMAC_CMD_OPCODE_* from umac.h) | ||
| 53 | * while the latter is used for sending commands to the | ||
| 54 | * LMAC. If you look at LMAC commands you'll se that they | ||
| 55 | * are actually regular iwlwifi target commands encapsulated | ||
| 56 | * into a special UMAC command called UMAC passthrough. | ||
| 57 | * This is due to the fact the host talks exclusively | ||
| 58 | * to the UMAC and so there needs to be a special UMAC | ||
| 59 | * command for talking to the LMAC. | ||
| 60 | * This is how a wifi command is laid out: | ||
| 61 | * ------------------------ | ||
| 62 | * | iwm_udma_out_wifi_hdr | | ||
| 63 | * ------------------------ | ||
| 64 | * | SW meta_data (32 bits) | | ||
| 65 | * ------------------------ | ||
| 66 | * | iwm_dev_cmd_hdr | | ||
| 67 | * ------------------------ | ||
| 68 | * | payload | | ||
| 69 | * | .... | | ||
| 70 | * | ||
| 71 | * - non-wifi, or general commands: | ||
| 72 | * Those commands are handled by the device's bootrom, | ||
| 73 | * and are typically sent when the UMAC and the LMAC | ||
| 74 | * are not yet available. | ||
| 75 | * * This is how a non-wifi command is laid out: | ||
| 76 | * --------------------------- | ||
| 77 | * | iwm_udma_out_nonwifi_hdr | | ||
| 78 | * --------------------------- | ||
| 79 | * | payload | | ||
| 80 | * | .... | | ||
| 81 | |||
| 82 | * | ||
| 83 | * All the commands start with a UDMA header, which is | ||
| 84 | * basically a 32 bits field. The 4 LSB there define | ||
| 85 | * an opcode that allows the target to differentiate | ||
| 86 | * between wifi (opcode is 0xf) and non-wifi commands | ||
| 87 | * (opcode is [0..0xe]). | ||
| 88 | * | ||
| 89 | * When a command (wifi or non-wifi) is supposed to receive | ||
| 90 | * an answer, we queue the command buffer. When we do receive | ||
| 91 | * a command response from the UMAC, we go through the list | ||
| 92 | * of pending command, and pass both the command and the answer | ||
| 93 | * to the rx handler. Each command is sent with a unique | ||
| 94 | * sequence id, and the answer is sent with the same one. This | ||
| 95 | * is how we're supposed to match an answer with its command. | ||
| 96 | * See rx.c:iwm_rx_handle_[non]wifi() and iwm_get_pending_[non]wifi() | ||
| 97 | * for the implementation details. | ||
| 98 | */ | ||
| 99 | #include <linux/kernel.h> | ||
| 100 | #include <linux/netdevice.h> | ||
| 101 | #include <linux/slab.h> | ||
| 102 | |||
| 103 | #include "iwm.h" | ||
| 104 | #include "bus.h" | ||
| 105 | #include "hal.h" | ||
| 106 | #include "umac.h" | ||
| 107 | #include "debug.h" | ||
| 108 | #include "trace.h" | ||
| 109 | |||
| 110 | static int iwm_nonwifi_cmd_init(struct iwm_priv *iwm, | ||
| 111 | struct iwm_nonwifi_cmd *cmd, | ||
| 112 | struct iwm_udma_nonwifi_cmd *udma_cmd) | ||
| 113 | { | ||
| 114 | INIT_LIST_HEAD(&cmd->pending); | ||
| 115 | |||
| 116 | spin_lock(&iwm->cmd_lock); | ||
| 117 | |||
| 118 | cmd->resp_received = 0; | ||
| 119 | |||
| 120 | cmd->seq_num = iwm->nonwifi_seq_num; | ||
| 121 | udma_cmd->seq_num = cpu_to_le16(cmd->seq_num); | ||
| 122 | |||
| 123 | iwm->nonwifi_seq_num++; | ||
| 124 | iwm->nonwifi_seq_num %= UMAC_NONWIFI_SEQ_NUM_MAX; | ||
| 125 | |||
| 126 | if (udma_cmd->resp) | ||
| 127 | list_add_tail(&cmd->pending, &iwm->nonwifi_pending_cmd); | ||
| 128 | |||
| 129 | spin_unlock(&iwm->cmd_lock); | ||
| 130 | |||
| 131 | cmd->buf.start = cmd->buf.payload; | ||
| 132 | cmd->buf.len = 0; | ||
| 133 | |||
| 134 | memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd)); | ||
| 135 | |||
| 136 | return cmd->seq_num; | ||
| 137 | } | ||
| 138 | |||
| 139 | u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm) | ||
| 140 | { | ||
| 141 | u16 seq_num = iwm->wifi_seq_num; | ||
| 142 | |||
| 143 | iwm->wifi_seq_num++; | ||
| 144 | iwm->wifi_seq_num %= UMAC_WIFI_SEQ_NUM_MAX; | ||
| 145 | |||
| 146 | return seq_num; | ||
| 147 | } | ||
| 148 | |||
| 149 | static void iwm_wifi_cmd_init(struct iwm_priv *iwm, | ||
| 150 | struct iwm_wifi_cmd *cmd, | ||
| 151 | struct iwm_udma_wifi_cmd *udma_cmd, | ||
| 152 | struct iwm_umac_cmd *umac_cmd, | ||
| 153 | struct iwm_lmac_cmd *lmac_cmd, | ||
| 154 | u16 payload_size) | ||
| 155 | { | ||
| 156 | INIT_LIST_HEAD(&cmd->pending); | ||
| 157 | |||
| 158 | spin_lock(&iwm->cmd_lock); | ||
| 159 | |||
| 160 | cmd->seq_num = iwm_alloc_wifi_cmd_seq(iwm); | ||
| 161 | umac_cmd->seq_num = cpu_to_le16(cmd->seq_num); | ||
| 162 | |||
| 163 | if (umac_cmd->resp) | ||
| 164 | list_add_tail(&cmd->pending, &iwm->wifi_pending_cmd); | ||
| 165 | |||
| 166 | spin_unlock(&iwm->cmd_lock); | ||
| 167 | |||
| 168 | cmd->buf.start = cmd->buf.payload; | ||
| 169 | cmd->buf.len = 0; | ||
| 170 | |||
| 171 | if (lmac_cmd) { | ||
| 172 | cmd->buf.start -= sizeof(struct iwm_lmac_hdr); | ||
| 173 | |||
| 174 | lmac_cmd->seq_num = cpu_to_le16(cmd->seq_num); | ||
| 175 | lmac_cmd->count = cpu_to_le16(payload_size); | ||
| 176 | |||
| 177 | memcpy(&cmd->lmac_cmd, lmac_cmd, sizeof(*lmac_cmd)); | ||
| 178 | |||
| 179 | umac_cmd->count = cpu_to_le16(sizeof(struct iwm_lmac_hdr)); | ||
| 180 | } else | ||
| 181 | umac_cmd->count = 0; | ||
| 182 | |||
| 183 | umac_cmd->count = cpu_to_le16(payload_size + | ||
| 184 | le16_to_cpu(umac_cmd->count)); | ||
| 185 | udma_cmd->count = cpu_to_le16(sizeof(struct iwm_umac_fw_cmd_hdr) + | ||
| 186 | le16_to_cpu(umac_cmd->count)); | ||
| 187 | |||
| 188 | memcpy(&cmd->udma_cmd, udma_cmd, sizeof(*udma_cmd)); | ||
| 189 | memcpy(&cmd->umac_cmd, umac_cmd, sizeof(*umac_cmd)); | ||
| 190 | } | ||
| 191 | |||
| 192 | void iwm_cmd_flush(struct iwm_priv *iwm) | ||
| 193 | { | ||
| 194 | struct iwm_wifi_cmd *wcmd, *wnext; | ||
| 195 | struct iwm_nonwifi_cmd *nwcmd, *nwnext; | ||
| 196 | |||
| 197 | list_for_each_entry_safe(wcmd, wnext, &iwm->wifi_pending_cmd, pending) { | ||
| 198 | list_del(&wcmd->pending); | ||
| 199 | kfree(wcmd); | ||
| 200 | } | ||
| 201 | |||
| 202 | list_for_each_entry_safe(nwcmd, nwnext, &iwm->nonwifi_pending_cmd, | ||
| 203 | pending) { | ||
| 204 | list_del(&nwcmd->pending); | ||
| 205 | kfree(nwcmd); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, u16 seq_num) | ||
| 210 | { | ||
| 211 | struct iwm_wifi_cmd *cmd; | ||
| 212 | |||
| 213 | list_for_each_entry(cmd, &iwm->wifi_pending_cmd, pending) | ||
| 214 | if (cmd->seq_num == seq_num) { | ||
| 215 | list_del(&cmd->pending); | ||
| 216 | return cmd; | ||
| 217 | } | ||
| 218 | |||
| 219 | return NULL; | ||
| 220 | } | ||
| 221 | |||
| 222 | struct iwm_nonwifi_cmd *iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, | ||
| 223 | u8 seq_num, u8 cmd_opcode) | ||
| 224 | { | ||
| 225 | struct iwm_nonwifi_cmd *cmd; | ||
| 226 | |||
| 227 | list_for_each_entry(cmd, &iwm->nonwifi_pending_cmd, pending) | ||
| 228 | if ((cmd->seq_num == seq_num) && | ||
| 229 | (cmd->udma_cmd.opcode == cmd_opcode) && | ||
| 230 | (cmd->resp_received)) { | ||
| 231 | list_del(&cmd->pending); | ||
| 232 | return cmd; | ||
| 233 | } | ||
| 234 | |||
| 235 | return NULL; | ||
| 236 | } | ||
| 237 | |||
| 238 | static void iwm_build_udma_nonwifi_hdr(struct iwm_priv *iwm, | ||
| 239 | struct iwm_udma_out_nonwifi_hdr *hdr, | ||
| 240 | struct iwm_udma_nonwifi_cmd *cmd) | ||
| 241 | { | ||
| 242 | memset(hdr, 0, sizeof(*hdr)); | ||
| 243 | |||
| 244 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, cmd->opcode); | ||
| 245 | SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP, cmd->resp); | ||
| 246 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, 1); | ||
| 247 | SET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW, | ||
| 248 | cmd->handle_by_hw); | ||
| 249 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE); | ||
| 250 | SET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM, | ||
| 251 | le16_to_cpu(cmd->seq_num)); | ||
| 252 | |||
| 253 | hdr->addr = cmd->addr; | ||
| 254 | hdr->op1_sz = cmd->op1_sz; | ||
| 255 | hdr->op2 = cmd->op2; | ||
| 256 | } | ||
| 257 | |||
| 258 | static int iwm_send_udma_nonwifi_cmd(struct iwm_priv *iwm, | ||
| 259 | struct iwm_nonwifi_cmd *cmd) | ||
| 260 | { | ||
| 261 | struct iwm_udma_out_nonwifi_hdr *udma_hdr; | ||
| 262 | struct iwm_nonwifi_cmd_buff *buf; | ||
| 263 | struct iwm_udma_nonwifi_cmd *udma_cmd = &cmd->udma_cmd; | ||
| 264 | |||
| 265 | buf = &cmd->buf; | ||
| 266 | |||
| 267 | buf->start -= sizeof(struct iwm_umac_nonwifi_out_hdr); | ||
| 268 | buf->len += sizeof(struct iwm_umac_nonwifi_out_hdr); | ||
| 269 | |||
| 270 | udma_hdr = (struct iwm_udma_out_nonwifi_hdr *)(buf->start); | ||
| 271 | |||
| 272 | iwm_build_udma_nonwifi_hdr(iwm, udma_hdr, udma_cmd); | ||
| 273 | |||
| 274 | IWM_DBG_CMD(iwm, DBG, | ||
| 275 | "Send UDMA nonwifi cmd: opcode = 0x%x, resp = 0x%x, " | ||
| 276 | "hw = 0x%x, seqnum = %d, addr = 0x%x, op1_sz = 0x%x, " | ||
| 277 | "op2 = 0x%x\n", udma_cmd->opcode, udma_cmd->resp, | ||
| 278 | udma_cmd->handle_by_hw, cmd->seq_num, udma_cmd->addr, | ||
| 279 | udma_cmd->op1_sz, udma_cmd->op2); | ||
| 280 | |||
| 281 | trace_iwm_tx_nonwifi_cmd(iwm, udma_hdr); | ||
| 282 | return iwm_bus_send_chunk(iwm, buf->start, buf->len); | ||
| 283 | } | ||
| 284 | |||
| 285 | void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop) | ||
| 286 | { | ||
| 287 | struct iwm_udma_out_wifi_hdr *hdr = (struct iwm_udma_out_wifi_hdr *)buf; | ||
| 288 | |||
| 289 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, eop); | ||
| 290 | } | ||
| 291 | |||
| 292 | void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm, | ||
| 293 | struct iwm_udma_out_wifi_hdr *hdr, | ||
| 294 | struct iwm_udma_wifi_cmd *cmd) | ||
| 295 | { | ||
| 296 | memset(hdr, 0, sizeof(*hdr)); | ||
| 297 | |||
| 298 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE, UMAC_HDI_OUT_OPCODE_WIFI); | ||
| 299 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT, cmd->eop); | ||
| 300 | SET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_SIGNATURE, UMAC_HDI_OUT_SIGNATURE); | ||
| 301 | |||
| 302 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_BYTE_COUNT, | ||
| 303 | le16_to_cpu(cmd->count)); | ||
| 304 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_CREDIT_GRP, cmd->credit_group); | ||
| 305 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_RATID, cmd->ra_tid); | ||
| 306 | SET_VAL32(hdr->meta_data, UMAC_HDI_OUT_LMAC_OFFSET, cmd->lmac_offset); | ||
| 307 | } | ||
| 308 | |||
| 309 | void iwm_build_umac_hdr(struct iwm_priv *iwm, | ||
| 310 | struct iwm_umac_fw_cmd_hdr *hdr, | ||
| 311 | struct iwm_umac_cmd *cmd) | ||
| 312 | { | ||
| 313 | memset(hdr, 0, sizeof(*hdr)); | ||
| 314 | |||
| 315 | SET_VAL32(hdr->meta_data, UMAC_FW_CMD_BYTE_COUNT, | ||
| 316 | le16_to_cpu(cmd->count)); | ||
| 317 | SET_VAL32(hdr->meta_data, UMAC_FW_CMD_TX_STA_COLOR, cmd->color); | ||
| 318 | SET_VAL8(hdr->cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ, cmd->resp); | ||
| 319 | |||
| 320 | hdr->cmd.cmd = cmd->id; | ||
| 321 | hdr->cmd.seq_num = cmd->seq_num; | ||
| 322 | } | ||
| 323 | |||
| 324 | static int iwm_send_udma_wifi_cmd(struct iwm_priv *iwm, | ||
| 325 | struct iwm_wifi_cmd *cmd) | ||
| 326 | { | ||
| 327 | struct iwm_umac_wifi_out_hdr *umac_hdr; | ||
| 328 | struct iwm_wifi_cmd_buff *buf; | ||
| 329 | struct iwm_udma_wifi_cmd *udma_cmd = &cmd->udma_cmd; | ||
| 330 | struct iwm_umac_cmd *umac_cmd = &cmd->umac_cmd; | ||
| 331 | int ret; | ||
| 332 | |||
| 333 | buf = &cmd->buf; | ||
| 334 | |||
| 335 | buf->start -= sizeof(struct iwm_umac_wifi_out_hdr); | ||
| 336 | buf->len += sizeof(struct iwm_umac_wifi_out_hdr); | ||
| 337 | |||
| 338 | umac_hdr = (struct iwm_umac_wifi_out_hdr *)(buf->start); | ||
| 339 | |||
| 340 | iwm_build_udma_wifi_hdr(iwm, &umac_hdr->hw_hdr, udma_cmd); | ||
| 341 | iwm_build_umac_hdr(iwm, &umac_hdr->sw_hdr, umac_cmd); | ||
| 342 | |||
| 343 | IWM_DBG_CMD(iwm, DBG, | ||
| 344 | "Send UDMA wifi cmd: opcode = 0x%x, UMAC opcode = 0x%x, " | ||
| 345 | "eop = 0x%x, count = 0x%x, credit_group = 0x%x, " | ||
| 346 | "ra_tid = 0x%x, lmac_offset = 0x%x, seqnum = %d\n", | ||
| 347 | UMAC_HDI_OUT_OPCODE_WIFI, umac_cmd->id, | ||
| 348 | udma_cmd->eop, udma_cmd->count, udma_cmd->credit_group, | ||
| 349 | udma_cmd->ra_tid, udma_cmd->lmac_offset, cmd->seq_num); | ||
| 350 | |||
| 351 | if (umac_cmd->id == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH) | ||
| 352 | IWM_DBG_CMD(iwm, DBG, "\tLMAC opcode: 0x%x\n", | ||
| 353 | cmd->lmac_cmd.id); | ||
| 354 | |||
| 355 | ret = iwm_tx_credit_alloc(iwm, udma_cmd->credit_group, buf->len); | ||
| 356 | |||
| 357 | /* We keep sending UMAC reset regardless of the command credits. | ||
| 358 | * The UMAC is supposed to be reset anyway and the Tx credits are | ||
| 359 | * reinitialized afterwards. If we are lucky, the reset could | ||
| 360 | * still be done even though we have run out of credits for the | ||
| 361 | * command pool at this moment.*/ | ||
| 362 | if (ret && (umac_cmd->id != UMAC_CMD_OPCODE_RESET)) { | ||
| 363 | IWM_DBG_TX(iwm, DBG, "Failed to alloc tx credit for cmd %d\n", | ||
| 364 | umac_cmd->id); | ||
| 365 | return ret; | ||
| 366 | } | ||
| 367 | |||
| 368 | trace_iwm_tx_wifi_cmd(iwm, umac_hdr); | ||
| 369 | return iwm_bus_send_chunk(iwm, buf->start, buf->len); | ||
| 370 | } | ||
| 371 | |||
| 372 | /* target_cmd a.k.a udma_nonwifi_cmd can be sent when UMAC is not available */ | ||
| 373 | int iwm_hal_send_target_cmd(struct iwm_priv *iwm, | ||
| 374 | struct iwm_udma_nonwifi_cmd *udma_cmd, | ||
| 375 | const void *payload) | ||
| 376 | { | ||
| 377 | struct iwm_nonwifi_cmd *cmd; | ||
| 378 | int ret, seq_num; | ||
| 379 | |||
| 380 | cmd = kzalloc(sizeof(struct iwm_nonwifi_cmd), GFP_KERNEL); | ||
| 381 | if (!cmd) { | ||
| 382 | IWM_ERR(iwm, "Couldn't alloc memory for hal cmd\n"); | ||
| 383 | return -ENOMEM; | ||
| 384 | } | ||
| 385 | |||
| 386 | seq_num = iwm_nonwifi_cmd_init(iwm, cmd, udma_cmd); | ||
| 387 | |||
| 388 | if (cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE || | ||
| 389 | cmd->udma_cmd.opcode == UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT) { | ||
| 390 | cmd->buf.len = le32_to_cpu(cmd->udma_cmd.op1_sz); | ||
| 391 | memcpy(&cmd->buf.payload, payload, cmd->buf.len); | ||
| 392 | } | ||
| 393 | |||
| 394 | ret = iwm_send_udma_nonwifi_cmd(iwm, cmd); | ||
| 395 | |||
| 396 | if (!udma_cmd->resp) | ||
| 397 | kfree(cmd); | ||
| 398 | |||
| 399 | if (ret < 0) | ||
| 400 | return ret; | ||
| 401 | |||
| 402 | return seq_num; | ||
| 403 | } | ||
| 404 | |||
| 405 | static void iwm_build_lmac_hdr(struct iwm_priv *iwm, struct iwm_lmac_hdr *hdr, | ||
| 406 | struct iwm_lmac_cmd *cmd) | ||
| 407 | { | ||
| 408 | memset(hdr, 0, sizeof(*hdr)); | ||
| 409 | |||
| 410 | hdr->id = cmd->id; | ||
| 411 | hdr->flags = 0; /* Is this ever used? */ | ||
| 412 | hdr->seq_num = cmd->seq_num; | ||
| 413 | } | ||
| 414 | |||
| 415 | /* | ||
| 416 | * iwm_hal_send_host_cmd(): sends commands to the UMAC or the LMAC. | ||
| 417 | * Sending command to the LMAC is equivalent to sending a | ||
| 418 | * regular UMAC command with the LMAC passthrough or the LMAC | ||
| 419 | * wrapper UMAC command IDs. | ||
| 420 | */ | ||
| 421 | int iwm_hal_send_host_cmd(struct iwm_priv *iwm, | ||
| 422 | struct iwm_udma_wifi_cmd *udma_cmd, | ||
| 423 | struct iwm_umac_cmd *umac_cmd, | ||
| 424 | struct iwm_lmac_cmd *lmac_cmd, | ||
| 425 | const void *payload, u16 payload_size) | ||
| 426 | { | ||
| 427 | struct iwm_wifi_cmd *cmd; | ||
| 428 | struct iwm_lmac_hdr *hdr; | ||
| 429 | int lmac_hdr_len = 0; | ||
| 430 | int ret; | ||
| 431 | |||
| 432 | cmd = kzalloc(sizeof(struct iwm_wifi_cmd), GFP_KERNEL); | ||
| 433 | if (!cmd) { | ||
| 434 | IWM_ERR(iwm, "Couldn't alloc memory for wifi hal cmd\n"); | ||
| 435 | return -ENOMEM; | ||
| 436 | } | ||
| 437 | |||
| 438 | iwm_wifi_cmd_init(iwm, cmd, udma_cmd, umac_cmd, lmac_cmd, payload_size); | ||
| 439 | |||
| 440 | if (lmac_cmd) { | ||
| 441 | hdr = (struct iwm_lmac_hdr *)(cmd->buf.start); | ||
| 442 | |||
| 443 | iwm_build_lmac_hdr(iwm, hdr, &cmd->lmac_cmd); | ||
| 444 | lmac_hdr_len = sizeof(struct iwm_lmac_hdr); | ||
| 445 | } | ||
| 446 | |||
| 447 | memcpy(cmd->buf.payload, payload, payload_size); | ||
| 448 | cmd->buf.len = le16_to_cpu(umac_cmd->count); | ||
| 449 | |||
| 450 | ret = iwm_send_udma_wifi_cmd(iwm, cmd); | ||
| 451 | |||
| 452 | /* We free the cmd if we're not expecting any response */ | ||
| 453 | if (!umac_cmd->resp) | ||
| 454 | kfree(cmd); | ||
| 455 | return ret; | ||
| 456 | } | ||
| 457 | |||
| 458 | /* | ||
| 459 | * iwm_hal_send_umac_cmd(): This is a special case for | ||
| 460 | * iwm_hal_send_host_cmd() to send direct UMAC cmd (without | ||
| 461 | * LMAC involved). | ||
| 462 | */ | ||
| 463 | int iwm_hal_send_umac_cmd(struct iwm_priv *iwm, | ||
| 464 | struct iwm_udma_wifi_cmd *udma_cmd, | ||
| 465 | struct iwm_umac_cmd *umac_cmd, | ||
| 466 | const void *payload, u16 payload_size) | ||
| 467 | { | ||
| 468 | return iwm_hal_send_host_cmd(iwm, udma_cmd, umac_cmd, NULL, | ||
| 469 | payload, payload_size); | ||
| 470 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/hal.h b/drivers/net/wireless/iwmc3200wifi/hal.h new file mode 100644 index 00000000000..c20936d9b6b --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/hal.h | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef _IWM_HAL_H_ | ||
| 40 | #define _IWM_HAL_H_ | ||
| 41 | |||
| 42 | #include "umac.h" | ||
| 43 | |||
| 44 | #define GET_VAL8(s, name) ((s >> name##_POS) & name##_SEED) | ||
| 45 | #define GET_VAL16(s, name) ((le16_to_cpu(s) >> name##_POS) & name##_SEED) | ||
| 46 | #define GET_VAL32(s, name) ((le32_to_cpu(s) >> name##_POS) & name##_SEED) | ||
| 47 | |||
| 48 | #define SET_VAL8(s, name, val) \ | ||
| 49 | do { \ | ||
| 50 | s = (s & ~(name##_SEED << name##_POS)) | \ | ||
| 51 | ((val & name##_SEED) << name##_POS); \ | ||
| 52 | } while (0) | ||
| 53 | |||
| 54 | #define SET_VAL16(s, name, val) \ | ||
| 55 | do { \ | ||
| 56 | s = cpu_to_le16((le16_to_cpu(s) & ~(name##_SEED << name##_POS)) | \ | ||
| 57 | ((val & name##_SEED) << name##_POS)); \ | ||
| 58 | } while (0) | ||
| 59 | |||
| 60 | #define SET_VAL32(s, name, val) \ | ||
| 61 | do { \ | ||
| 62 | s = cpu_to_le32((le32_to_cpu(s) & ~(name##_SEED << name##_POS)) | \ | ||
| 63 | ((val & name##_SEED) << name##_POS)); \ | ||
| 64 | } while (0) | ||
| 65 | |||
| 66 | |||
| 67 | #define UDMA_UMAC_INIT { .eop = 1, \ | ||
| 68 | .credit_group = 0x4, \ | ||
| 69 | .ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \ | ||
| 70 | .lmac_offset = 0 } | ||
| 71 | #define UDMA_LMAC_INIT { .eop = 1, \ | ||
| 72 | .credit_group = 0x4, \ | ||
| 73 | .ra_tid = UMAC_HDI_ACT_TBL_IDX_HOST_CMD, \ | ||
| 74 | .lmac_offset = 4 } | ||
| 75 | |||
| 76 | |||
| 77 | /* UDMA IN OP CODE -- cmd bits [3:0] */ | ||
| 78 | #define UDMA_HDI_IN_NW_CMD_OPCODE_POS 0 | ||
| 79 | #define UDMA_HDI_IN_NW_CMD_OPCODE_SEED 0xF | ||
| 80 | |||
| 81 | #define UDMA_IN_OPCODE_GENERAL_RESP 0x0 | ||
| 82 | #define UDMA_IN_OPCODE_READ_RESP 0x1 | ||
| 83 | #define UDMA_IN_OPCODE_WRITE_RESP 0x2 | ||
| 84 | #define UDMA_IN_OPCODE_PERS_WRITE_RESP 0x5 | ||
| 85 | #define UDMA_IN_OPCODE_PERS_READ_RESP 0x6 | ||
| 86 | #define UDMA_IN_OPCODE_RD_MDFY_WR_RESP 0x7 | ||
| 87 | #define UDMA_IN_OPCODE_EP_MNGMT_MSG 0x8 | ||
| 88 | #define UDMA_IN_OPCODE_CRDT_CHNG_MSG 0x9 | ||
| 89 | #define UDMA_IN_OPCODE_CNTRL_DATABASE_MSG 0xA | ||
| 90 | #define UDMA_IN_OPCODE_SW_MSG 0xB | ||
| 91 | #define UDMA_IN_OPCODE_WIFI 0xF | ||
| 92 | #define UDMA_IN_OPCODE_WIFI_LMAC 0x1F | ||
| 93 | #define UDMA_IN_OPCODE_WIFI_UMAC 0x2F | ||
| 94 | |||
| 95 | /* HW API: udma_hdi_nonwifi API (OUT and IN) */ | ||
| 96 | |||
| 97 | /* iwm_udma_nonwifi_cmd request response -- bits [9:9] */ | ||
| 98 | #define UDMA_HDI_OUT_NW_CMD_RESP_POS 9 | ||
| 99 | #define UDMA_HDI_OUT_NW_CMD_RESP_SEED 0x1 | ||
| 100 | |||
| 101 | /* iwm_udma_nonwifi_cmd handle by HW -- bits [11:11] */ | ||
| 102 | #define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_POS 11 | ||
| 103 | #define UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW_SEED 0x1 | ||
| 104 | |||
| 105 | /* iwm_udma_nonwifi_cmd sequence-number -- bits [12:15] */ | ||
| 106 | #define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_POS 12 | ||
| 107 | #define UDMA_HDI_OUT_NW_CMD_SEQ_NUM_SEED 0xF | ||
| 108 | |||
| 109 | /* UDMA IN Non-WIFI HW sequence number -- bits [12:15] */ | ||
| 110 | #define UDMA_IN_NW_HW_SEQ_NUM_POS 12 | ||
| 111 | #define UDMA_IN_NW_HW_SEQ_NUM_SEED 0xF | ||
| 112 | |||
| 113 | /* UDMA IN Non-WIFI HW signature -- bits [16:31] */ | ||
| 114 | #define UDMA_IN_NW_HW_SIG_POS 16 | ||
| 115 | #define UDMA_IN_NW_HW_SIG_SEED 0xFFFF | ||
| 116 | |||
| 117 | /* fixed signature */ | ||
| 118 | #define UDMA_IN_NW_HW_SIG 0xCBBC | ||
| 119 | |||
| 120 | /* UDMA IN Non-WIFI HW block length -- bits [32:35] */ | ||
| 121 | #define UDMA_IN_NW_HW_LENGTH_SEED 0xF | ||
| 122 | #define UDMA_IN_NW_HW_LENGTH_POS 32 | ||
| 123 | |||
| 124 | /* End of HW API: udma_hdi_nonwifi API (OUT and IN) */ | ||
| 125 | |||
| 126 | #define IWM_SDIO_FW_MAX_CHUNK_SIZE 2032 | ||
| 127 | #define IWM_MAX_WIFI_HEADERS_SIZE 32 | ||
| 128 | #define IWM_MAX_NONWIFI_HEADERS_SIZE 16 | ||
| 129 | #define IWM_MAX_NONWIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \ | ||
| 130 | IWM_MAX_NONWIFI_HEADERS_SIZE) | ||
| 131 | #define IWM_MAX_WIFI_CMD_BUFF_SIZE (IWM_SDIO_FW_MAX_CHUNK_SIZE - \ | ||
| 132 | IWM_MAX_WIFI_HEADERS_SIZE) | ||
| 133 | |||
| 134 | #define IWM_HAL_CONCATENATE_BUF_SIZE (32 * 1024) | ||
| 135 | |||
| 136 | struct iwm_wifi_cmd_buff { | ||
| 137 | u16 len; | ||
| 138 | u8 *start; | ||
| 139 | u8 hdr[IWM_MAX_WIFI_HEADERS_SIZE]; | ||
| 140 | u8 payload[IWM_MAX_WIFI_CMD_BUFF_SIZE]; | ||
| 141 | }; | ||
| 142 | |||
| 143 | struct iwm_nonwifi_cmd_buff { | ||
| 144 | u16 len; | ||
| 145 | u8 *start; | ||
| 146 | u8 hdr[IWM_MAX_NONWIFI_HEADERS_SIZE]; | ||
| 147 | u8 payload[IWM_MAX_NONWIFI_CMD_BUFF_SIZE]; | ||
| 148 | }; | ||
| 149 | |||
| 150 | struct iwm_udma_nonwifi_cmd { | ||
| 151 | u8 opcode; | ||
| 152 | u8 eop; | ||
| 153 | u8 resp; | ||
| 154 | u8 handle_by_hw; | ||
| 155 | __le32 addr; | ||
| 156 | __le32 op1_sz; | ||
| 157 | __le32 op2; | ||
| 158 | __le16 seq_num; | ||
| 159 | }; | ||
| 160 | |||
| 161 | struct iwm_udma_wifi_cmd { | ||
| 162 | __le16 count; | ||
| 163 | u8 eop; | ||
| 164 | u8 credit_group; | ||
| 165 | u8 ra_tid; | ||
| 166 | u8 lmac_offset; | ||
| 167 | }; | ||
| 168 | |||
| 169 | struct iwm_umac_cmd { | ||
| 170 | u8 id; | ||
| 171 | __le16 count; | ||
| 172 | u8 resp; | ||
| 173 | __le16 seq_num; | ||
| 174 | u8 color; | ||
| 175 | }; | ||
| 176 | |||
| 177 | struct iwm_lmac_cmd { | ||
| 178 | u8 id; | ||
| 179 | __le16 count; | ||
| 180 | u8 resp; | ||
| 181 | __le16 seq_num; | ||
| 182 | }; | ||
| 183 | |||
| 184 | struct iwm_nonwifi_cmd { | ||
| 185 | u16 seq_num; | ||
| 186 | bool resp_received; | ||
| 187 | struct list_head pending; | ||
| 188 | struct iwm_udma_nonwifi_cmd udma_cmd; | ||
| 189 | struct iwm_umac_cmd umac_cmd; | ||
| 190 | struct iwm_lmac_cmd lmac_cmd; | ||
| 191 | struct iwm_nonwifi_cmd_buff buf; | ||
| 192 | u32 flags; | ||
| 193 | }; | ||
| 194 | |||
| 195 | struct iwm_wifi_cmd { | ||
| 196 | u16 seq_num; | ||
| 197 | struct list_head pending; | ||
| 198 | struct iwm_udma_wifi_cmd udma_cmd; | ||
| 199 | struct iwm_umac_cmd umac_cmd; | ||
| 200 | struct iwm_lmac_cmd lmac_cmd; | ||
| 201 | struct iwm_wifi_cmd_buff buf; | ||
| 202 | u32 flags; | ||
| 203 | }; | ||
| 204 | |||
| 205 | void iwm_cmd_flush(struct iwm_priv *iwm); | ||
| 206 | |||
| 207 | struct iwm_wifi_cmd *iwm_get_pending_wifi_cmd(struct iwm_priv *iwm, | ||
| 208 | u16 seq_num); | ||
| 209 | struct iwm_nonwifi_cmd *iwm_get_pending_nonwifi_cmd(struct iwm_priv *iwm, | ||
| 210 | u8 seq_num, u8 cmd_opcode); | ||
| 211 | |||
| 212 | |||
| 213 | int iwm_hal_send_target_cmd(struct iwm_priv *iwm, | ||
| 214 | struct iwm_udma_nonwifi_cmd *ucmd, | ||
| 215 | const void *payload); | ||
| 216 | |||
| 217 | int iwm_hal_send_host_cmd(struct iwm_priv *iwm, | ||
| 218 | struct iwm_udma_wifi_cmd *udma_cmd, | ||
| 219 | struct iwm_umac_cmd *umac_cmd, | ||
| 220 | struct iwm_lmac_cmd *lmac_cmd, | ||
| 221 | const void *payload, u16 payload_size); | ||
| 222 | |||
| 223 | int iwm_hal_send_umac_cmd(struct iwm_priv *iwm, | ||
| 224 | struct iwm_udma_wifi_cmd *udma_cmd, | ||
| 225 | struct iwm_umac_cmd *umac_cmd, | ||
| 226 | const void *payload, u16 payload_size); | ||
| 227 | |||
| 228 | u16 iwm_alloc_wifi_cmd_seq(struct iwm_priv *iwm); | ||
| 229 | |||
| 230 | void iwm_udma_wifi_hdr_set_eop(struct iwm_priv *iwm, u8 *buf, u8 eop); | ||
| 231 | void iwm_build_udma_wifi_hdr(struct iwm_priv *iwm, | ||
| 232 | struct iwm_udma_out_wifi_hdr *hdr, | ||
| 233 | struct iwm_udma_wifi_cmd *cmd); | ||
| 234 | void iwm_build_umac_hdr(struct iwm_priv *iwm, | ||
| 235 | struct iwm_umac_fw_cmd_hdr *hdr, | ||
| 236 | struct iwm_umac_cmd *cmd); | ||
| 237 | #endif /* _IWM_HAL_H_ */ | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h new file mode 100644 index 00000000000..51d7efa15ae --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h | |||
| @@ -0,0 +1,367 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_H__ | ||
| 40 | #define __IWM_H__ | ||
| 41 | |||
| 42 | #include <linux/netdevice.h> | ||
| 43 | #include <linux/wireless.h> | ||
| 44 | #include <net/cfg80211.h> | ||
| 45 | |||
| 46 | #include "debug.h" | ||
| 47 | #include "hal.h" | ||
| 48 | #include "umac.h" | ||
| 49 | #include "lmac.h" | ||
| 50 | #include "eeprom.h" | ||
| 51 | #include "trace.h" | ||
| 52 | |||
| 53 | #define IWM_COPYRIGHT "Copyright(c) 2009 Intel Corporation" | ||
| 54 | #define IWM_AUTHOR "<ilw@linux.intel.com>" | ||
| 55 | |||
| 56 | #define IWM_SRC_LMAC UMAC_HDI_IN_SOURCE_FHRX | ||
| 57 | #define IWM_SRC_UDMA UMAC_HDI_IN_SOURCE_UDMA | ||
| 58 | #define IWM_SRC_UMAC UMAC_HDI_IN_SOURCE_FW | ||
| 59 | #define IWM_SRC_NUM 3 | ||
| 60 | |||
| 61 | #define IWM_POWER_INDEX_MIN 0 | ||
| 62 | #define IWM_POWER_INDEX_MAX 5 | ||
| 63 | #define IWM_POWER_INDEX_DEFAULT 3 | ||
| 64 | |||
| 65 | struct iwm_conf { | ||
| 66 | u32 sdio_ior_timeout; | ||
| 67 | unsigned long calib_map; | ||
| 68 | unsigned long expected_calib_map; | ||
| 69 | u8 ct_kill_entry; | ||
| 70 | u8 ct_kill_exit; | ||
| 71 | bool reset_on_fatal_err; | ||
| 72 | bool auto_connect; | ||
| 73 | bool wimax_not_present; | ||
| 74 | bool enable_qos; | ||
| 75 | u32 mode; | ||
| 76 | |||
| 77 | u32 power_index; | ||
| 78 | u32 frag_threshold; | ||
| 79 | u32 rts_threshold; | ||
| 80 | bool cts_to_self; | ||
| 81 | |||
| 82 | u32 assoc_timeout; | ||
| 83 | u32 roam_timeout; | ||
| 84 | u32 wireless_mode; | ||
| 85 | |||
| 86 | u8 ibss_band; | ||
| 87 | u8 ibss_channel; | ||
| 88 | |||
| 89 | u8 mac_addr[ETH_ALEN]; | ||
| 90 | }; | ||
| 91 | |||
| 92 | enum { | ||
| 93 | COEX_MODE_SA = 1, | ||
| 94 | COEX_MODE_XOR, | ||
| 95 | COEX_MODE_CM, | ||
| 96 | COEX_MODE_MAX, | ||
| 97 | }; | ||
| 98 | |||
| 99 | struct iwm_if_ops; | ||
| 100 | struct iwm_wifi_cmd; | ||
| 101 | |||
| 102 | struct pool_entry { | ||
| 103 | int id; /* group id */ | ||
| 104 | int sid; /* super group id */ | ||
| 105 | int min_pages; /* min capacity in pages */ | ||
| 106 | int max_pages; /* max capacity in pages */ | ||
| 107 | int alloc_pages; /* allocated # of pages. incresed by driver */ | ||
| 108 | int total_freed_pages; /* total freed # of pages. incresed by UMAC */ | ||
| 109 | }; | ||
| 110 | |||
| 111 | struct spool_entry { | ||
| 112 | int id; | ||
| 113 | int max_pages; | ||
| 114 | int alloc_pages; | ||
| 115 | }; | ||
| 116 | |||
| 117 | struct iwm_tx_credit { | ||
| 118 | spinlock_t lock; | ||
| 119 | int pool_nr; | ||
| 120 | unsigned long full_pools_map; /* bitmap for # of filled tx pools */ | ||
| 121 | struct pool_entry pools[IWM_MACS_OUT_GROUPS]; | ||
| 122 | struct spool_entry spools[IWM_MACS_OUT_SGROUPS]; | ||
| 123 | }; | ||
| 124 | |||
| 125 | struct iwm_notif { | ||
| 126 | struct list_head pending; | ||
| 127 | u32 cmd_id; | ||
| 128 | void *cmd; | ||
| 129 | u8 src; | ||
| 130 | void *buf; | ||
| 131 | unsigned long buf_size; | ||
| 132 | }; | ||
| 133 | |||
| 134 | struct iwm_tid_info { | ||
| 135 | __le16 last_seq_num; | ||
| 136 | bool stopped; | ||
| 137 | struct mutex mutex; | ||
| 138 | }; | ||
| 139 | |||
| 140 | struct iwm_sta_info { | ||
| 141 | u8 addr[ETH_ALEN]; | ||
| 142 | bool valid; | ||
| 143 | bool qos; | ||
| 144 | u8 color; | ||
| 145 | struct iwm_tid_info tid_info[IWM_UMAC_TID_NR]; | ||
| 146 | }; | ||
| 147 | |||
| 148 | struct iwm_tx_info { | ||
| 149 | u8 sta; | ||
| 150 | u8 color; | ||
| 151 | u8 tid; | ||
| 152 | }; | ||
| 153 | |||
| 154 | struct iwm_rx_info { | ||
| 155 | unsigned long rx_size; | ||
| 156 | unsigned long rx_buf_size; | ||
| 157 | }; | ||
| 158 | |||
| 159 | #define IWM_NUM_KEYS 4 | ||
| 160 | |||
| 161 | struct iwm_umac_key_hdr { | ||
| 162 | u8 mac[ETH_ALEN]; | ||
| 163 | u8 key_idx; | ||
| 164 | u8 multicast; /* BCast encrypt & BCast decrypt of frames FROM mac */ | ||
| 165 | } __packed; | ||
| 166 | |||
| 167 | struct iwm_key { | ||
| 168 | struct iwm_umac_key_hdr hdr; | ||
| 169 | u32 cipher; | ||
| 170 | u8 key[WLAN_MAX_KEY_LEN]; | ||
| 171 | u8 seq[IW_ENCODE_SEQ_MAX_SIZE]; | ||
| 172 | int key_len; | ||
| 173 | int seq_len; | ||
| 174 | }; | ||
| 175 | |||
| 176 | #define IWM_RX_ID_HASH 0xff | ||
| 177 | #define IWM_RX_ID_GET_HASH(id) ((id) % IWM_RX_ID_HASH) | ||
| 178 | |||
| 179 | #define IWM_STA_TABLE_NUM 16 | ||
| 180 | #define IWM_TX_LIST_SIZE 64 | ||
| 181 | #define IWM_RX_LIST_SIZE 256 | ||
| 182 | |||
| 183 | #define IWM_SCAN_ID_MAX 0xff | ||
| 184 | |||
| 185 | #define IWM_STATUS_READY 0 | ||
| 186 | #define IWM_STATUS_SCANNING 1 | ||
| 187 | #define IWM_STATUS_SCAN_ABORTING 2 | ||
| 188 | #define IWM_STATUS_SME_CONNECTING 3 | ||
| 189 | #define IWM_STATUS_ASSOCIATED 4 | ||
| 190 | #define IWM_STATUS_RESETTING 5 | ||
| 191 | |||
| 192 | struct iwm_tx_queue { | ||
| 193 | int id; | ||
| 194 | struct sk_buff_head queue; | ||
| 195 | struct sk_buff_head stopped_queue; | ||
| 196 | spinlock_t lock; | ||
| 197 | struct workqueue_struct *wq; | ||
| 198 | struct work_struct worker; | ||
| 199 | u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE]; | ||
| 200 | int concat_count; | ||
| 201 | u8 *concat_ptr; | ||
| 202 | }; | ||
| 203 | |||
| 204 | /* Queues 0 ~ 3 for AC data, 5 for iPAN */ | ||
| 205 | #define IWM_TX_QUEUES 5 | ||
| 206 | #define IWM_TX_DATA_QUEUES 4 | ||
| 207 | #define IWM_TX_CMD_QUEUE 4 | ||
| 208 | |||
| 209 | struct iwm_bss_info { | ||
| 210 | struct list_head node; | ||
| 211 | struct cfg80211_bss *cfg_bss; | ||
| 212 | struct iwm_umac_notif_bss_info *bss; | ||
| 213 | }; | ||
| 214 | |||
| 215 | typedef int (*iwm_handler)(struct iwm_priv *priv, u8 *buf, | ||
| 216 | unsigned long buf_size, struct iwm_wifi_cmd *cmd); | ||
| 217 | |||
| 218 | #define IWM_WATCHDOG_PERIOD (6 * HZ) | ||
| 219 | |||
| 220 | struct iwm_priv { | ||
| 221 | struct wireless_dev *wdev; | ||
| 222 | struct iwm_if_ops *bus_ops; | ||
| 223 | |||
| 224 | struct iwm_conf conf; | ||
| 225 | |||
| 226 | unsigned long status; | ||
| 227 | |||
| 228 | struct list_head pending_notif; | ||
| 229 | wait_queue_head_t notif_queue; | ||
| 230 | |||
| 231 | wait_queue_head_t nonwifi_queue; | ||
| 232 | |||
| 233 | unsigned long calib_done_map; | ||
| 234 | struct { | ||
| 235 | u8 *buf; | ||
| 236 | u32 size; | ||
| 237 | } calib_res[CALIBRATION_CMD_NUM]; | ||
| 238 | |||
| 239 | struct iwm_umac_profile *umac_profile; | ||
| 240 | bool umac_profile_active; | ||
| 241 | |||
| 242 | u8 bssid[ETH_ALEN]; | ||
| 243 | u8 channel; | ||
| 244 | u16 rate; | ||
| 245 | u32 txpower; | ||
| 246 | |||
| 247 | struct iwm_sta_info sta_table[IWM_STA_TABLE_NUM]; | ||
| 248 | struct list_head bss_list; | ||
| 249 | |||
| 250 | void (*nonwifi_rx_handlers[UMAC_HDI_IN_OPCODE_NONWIFI_MAX]) | ||
| 251 | (struct iwm_priv *priv, u8 *buf, unsigned long buf_size); | ||
| 252 | |||
| 253 | const iwm_handler *umac_handlers; | ||
| 254 | const iwm_handler *lmac_handlers; | ||
| 255 | DECLARE_BITMAP(lmac_handler_map, LMAC_COMMAND_ID_NUM); | ||
| 256 | DECLARE_BITMAP(umac_handler_map, LMAC_COMMAND_ID_NUM); | ||
| 257 | DECLARE_BITMAP(udma_handler_map, LMAC_COMMAND_ID_NUM); | ||
| 258 | |||
| 259 | struct list_head wifi_pending_cmd; | ||
| 260 | struct list_head nonwifi_pending_cmd; | ||
| 261 | u16 wifi_seq_num; | ||
| 262 | u8 nonwifi_seq_num; | ||
| 263 | spinlock_t cmd_lock; | ||
| 264 | |||
| 265 | u32 core_enabled; | ||
| 266 | |||
| 267 | u8 scan_id; | ||
| 268 | struct cfg80211_scan_request *scan_request; | ||
| 269 | |||
| 270 | struct sk_buff_head rx_list; | ||
| 271 | struct list_head rx_tickets; | ||
| 272 | spinlock_t ticket_lock; | ||
| 273 | struct list_head rx_packets[IWM_RX_ID_HASH]; | ||
| 274 | spinlock_t packet_lock[IWM_RX_ID_HASH]; | ||
| 275 | struct workqueue_struct *rx_wq; | ||
| 276 | struct work_struct rx_worker; | ||
| 277 | |||
| 278 | struct iwm_tx_credit tx_credit; | ||
| 279 | struct iwm_tx_queue txq[IWM_TX_QUEUES]; | ||
| 280 | |||
| 281 | struct iwm_key keys[IWM_NUM_KEYS]; | ||
| 282 | s8 default_key; | ||
| 283 | |||
| 284 | DECLARE_BITMAP(wifi_ntfy, WIFI_IF_NTFY_MAX); | ||
| 285 | wait_queue_head_t wifi_ntfy_queue; | ||
| 286 | |||
| 287 | wait_queue_head_t mlme_queue; | ||
| 288 | |||
| 289 | struct iw_statistics wstats; | ||
| 290 | struct delayed_work stats_request; | ||
| 291 | struct delayed_work disconnect; | ||
| 292 | struct delayed_work ct_kill_delay; | ||
| 293 | |||
| 294 | struct iwm_debugfs dbg; | ||
| 295 | |||
| 296 | u8 *eeprom; | ||
| 297 | struct timer_list watchdog; | ||
| 298 | struct work_struct reset_worker; | ||
| 299 | struct work_struct auth_retry_worker; | ||
| 300 | struct mutex mutex; | ||
| 301 | |||
| 302 | u8 *req_ie; | ||
| 303 | int req_ie_len; | ||
| 304 | u8 *resp_ie; | ||
| 305 | int resp_ie_len; | ||
| 306 | |||
| 307 | struct iwm_fw_error_hdr *last_fw_err; | ||
| 308 | char umac_version[8]; | ||
| 309 | char lmac_version[8]; | ||
| 310 | |||
| 311 | char private[0] __attribute__((__aligned__(NETDEV_ALIGN))); | ||
| 312 | }; | ||
| 313 | |||
| 314 | static inline void *iwm_private(struct iwm_priv *iwm) | ||
| 315 | { | ||
| 316 | BUG_ON(!iwm); | ||
| 317 | return &iwm->private; | ||
| 318 | } | ||
| 319 | |||
| 320 | #define hw_to_iwm(h) (h->iwm) | ||
| 321 | #define iwm_to_dev(i) (wiphy_dev(i->wdev->wiphy)) | ||
| 322 | #define iwm_to_wiphy(i) (i->wdev->wiphy) | ||
| 323 | #define wiphy_to_iwm(w) (struct iwm_priv *)(wiphy_priv(w)) | ||
| 324 | #define iwm_to_wdev(i) (i->wdev) | ||
| 325 | #define wdev_to_iwm(w) (struct iwm_priv *)(wdev_priv(w)) | ||
| 326 | #define iwm_to_ndev(i) (i->wdev->netdev) | ||
| 327 | #define ndev_to_iwm(n) (wdev_to_iwm(n->ieee80211_ptr)) | ||
| 328 | #define skb_to_rx_info(s) ((struct iwm_rx_info *)(s->cb)) | ||
| 329 | #define skb_to_tx_info(s) ((struct iwm_tx_info *)s->cb) | ||
| 330 | |||
| 331 | void *iwm_if_alloc(int sizeof_bus, struct device *dev, | ||
| 332 | struct iwm_if_ops *if_ops); | ||
| 333 | void iwm_if_free(struct iwm_priv *iwm); | ||
| 334 | int iwm_if_add(struct iwm_priv *iwm); | ||
| 335 | void iwm_if_remove(struct iwm_priv *iwm); | ||
| 336 | int iwm_mode_to_nl80211_iftype(int mode); | ||
| 337 | int iwm_priv_init(struct iwm_priv *iwm); | ||
| 338 | void iwm_priv_deinit(struct iwm_priv *iwm); | ||
| 339 | void iwm_reset(struct iwm_priv *iwm); | ||
| 340 | void iwm_resetting(struct iwm_priv *iwm); | ||
| 341 | void iwm_tx_credit_init_pools(struct iwm_priv *iwm, | ||
| 342 | struct iwm_umac_notif_alive *alive); | ||
| 343 | int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb); | ||
| 344 | int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd, | ||
| 345 | u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size); | ||
| 346 | int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout); | ||
| 347 | void iwm_init_default_profile(struct iwm_priv *iwm, | ||
| 348 | struct iwm_umac_profile *profile); | ||
| 349 | void iwm_link_on(struct iwm_priv *iwm); | ||
| 350 | void iwm_link_off(struct iwm_priv *iwm); | ||
| 351 | int iwm_up(struct iwm_priv *iwm); | ||
| 352 | int iwm_down(struct iwm_priv *iwm); | ||
| 353 | |||
| 354 | /* TX API */ | ||
| 355 | int iwm_tid_to_queue(u16 tid); | ||
| 356 | void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages); | ||
| 357 | void iwm_tx_worker(struct work_struct *work); | ||
| 358 | int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev); | ||
| 359 | |||
| 360 | /* RX API */ | ||
| 361 | void iwm_rx_setup_handlers(struct iwm_priv *iwm); | ||
| 362 | int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size); | ||
| 363 | int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, | ||
| 364 | struct iwm_wifi_cmd *cmd); | ||
| 365 | void iwm_rx_free(struct iwm_priv *iwm); | ||
| 366 | |||
| 367 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/lmac.h b/drivers/net/wireless/iwmc3200wifi/lmac.h new file mode 100644 index 00000000000..5ddcdf8c70c --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/lmac.h | |||
| @@ -0,0 +1,484 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_LMAC_H__ | ||
| 40 | #define __IWM_LMAC_H__ | ||
| 41 | |||
| 42 | struct iwm_lmac_hdr { | ||
| 43 | u8 id; | ||
| 44 | u8 flags; | ||
| 45 | __le16 seq_num; | ||
| 46 | } __packed; | ||
| 47 | |||
| 48 | /* LMAC commands */ | ||
| 49 | #define CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK 0x1 | ||
| 50 | |||
| 51 | struct iwm_lmac_cal_cfg_elt { | ||
| 52 | __le32 enable; /* 1 means LMAC needs to do something */ | ||
| 53 | __le32 start; /* 1 to start calibration, 0 to stop */ | ||
| 54 | __le32 send_res; /* 1 for sending back results */ | ||
| 55 | __le32 apply_res; /* 1 for applying calibration results to HW */ | ||
| 56 | __le32 reserved; | ||
| 57 | } __packed; | ||
| 58 | |||
| 59 | struct iwm_lmac_cal_cfg_status { | ||
| 60 | struct iwm_lmac_cal_cfg_elt init; | ||
| 61 | struct iwm_lmac_cal_cfg_elt periodic; | ||
| 62 | __le32 flags; /* CALIB_CFG_FLAG_SEND_COMPLETE_NTFY_AFTER_MSK */ | ||
| 63 | } __packed; | ||
| 64 | |||
| 65 | struct iwm_lmac_cal_cfg_cmd { | ||
| 66 | struct iwm_lmac_cal_cfg_status ucode_cfg; | ||
| 67 | struct iwm_lmac_cal_cfg_status driver_cfg; | ||
| 68 | __le32 reserved; | ||
| 69 | } __packed; | ||
| 70 | |||
| 71 | struct iwm_lmac_cal_cfg_resp { | ||
| 72 | __le32 status; | ||
| 73 | } __packed; | ||
| 74 | |||
| 75 | #define IWM_CARD_STATE_SW_HW_ENABLED 0x00 | ||
| 76 | #define IWM_CARD_STATE_HW_DISABLED 0x01 | ||
| 77 | #define IWM_CARD_STATE_SW_DISABLED 0x02 | ||
| 78 | #define IWM_CARD_STATE_CTKILL_DISABLED 0x04 | ||
| 79 | #define IWM_CARD_STATE_IS_RXON 0x10 | ||
| 80 | |||
| 81 | struct iwm_lmac_card_state { | ||
| 82 | __le32 flags; | ||
| 83 | } __packed; | ||
| 84 | |||
| 85 | /** | ||
| 86 | * COEX_PRIORITY_TABLE_CMD | ||
| 87 | * | ||
| 88 | * Priority entry for each state | ||
| 89 | * Will keep two tables, for STA and WIPAN | ||
| 90 | */ | ||
| 91 | enum { | ||
| 92 | /* UN-ASSOCIATION PART */ | ||
| 93 | COEX_UNASSOC_IDLE = 0, | ||
| 94 | COEX_UNASSOC_MANUAL_SCAN, | ||
| 95 | COEX_UNASSOC_AUTO_SCAN, | ||
| 96 | |||
| 97 | /* CALIBRATION */ | ||
| 98 | COEX_CALIBRATION, | ||
| 99 | COEX_PERIODIC_CALIBRATION, | ||
| 100 | |||
| 101 | /* CONNECTION */ | ||
| 102 | COEX_CONNECTION_ESTAB, | ||
| 103 | |||
| 104 | /* ASSOCIATION PART */ | ||
| 105 | COEX_ASSOCIATED_IDLE, | ||
| 106 | COEX_ASSOC_MANUAL_SCAN, | ||
| 107 | COEX_ASSOC_AUTO_SCAN, | ||
| 108 | COEX_ASSOC_ACTIVE_LEVEL, | ||
| 109 | |||
| 110 | /* RF ON/OFF */ | ||
| 111 | COEX_RF_ON, | ||
| 112 | COEX_RF_OFF, | ||
| 113 | COEX_STAND_ALONE_DEBUG, | ||
| 114 | |||
| 115 | /* IPNN */ | ||
| 116 | COEX_IPAN_ASSOC_LEVEL, | ||
| 117 | |||
| 118 | /* RESERVED */ | ||
| 119 | COEX_RSRVD1, | ||
| 120 | COEX_RSRVD2, | ||
| 121 | |||
| 122 | COEX_EVENTS_NUM | ||
| 123 | }; | ||
| 124 | |||
| 125 | #define COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK 0x1 | ||
| 126 | #define COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK 0x2 | ||
| 127 | #define COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK 0x4 | ||
| 128 | |||
| 129 | struct coex_event { | ||
| 130 | u8 req_prio; | ||
| 131 | u8 win_med_prio; | ||
| 132 | u8 reserved; | ||
| 133 | u8 flags; | ||
| 134 | } __packed; | ||
| 135 | |||
| 136 | #define COEX_FLAGS_STA_TABLE_VALID_MSK 0x1 | ||
| 137 | #define COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK 0x4 | ||
| 138 | #define COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK 0x8 | ||
| 139 | #define COEX_FLAGS_COEX_ENABLE_MSK 0x80 | ||
| 140 | |||
| 141 | struct iwm_coex_prio_table_cmd { | ||
| 142 | u8 flags; | ||
| 143 | u8 reserved[3]; | ||
| 144 | struct coex_event sta_prio[COEX_EVENTS_NUM]; | ||
| 145 | } __packed; | ||
| 146 | |||
| 147 | /* Coexistence definitions | ||
| 148 | * | ||
| 149 | * Constants to fill in the Priorities' Tables | ||
| 150 | * RP - Requested Priority | ||
| 151 | * WP - Win Medium Priority: priority assigned when the contention has been won | ||
| 152 | * FLAGS - Combination of COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK and | ||
| 153 | * COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | ||
| 154 | */ | ||
| 155 | |||
| 156 | #define COEX_UNASSOC_IDLE_FLAGS 0 | ||
| 157 | #define COEX_UNASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 158 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK) | ||
| 159 | #define COEX_UNASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 160 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK) | ||
| 161 | #define COEX_CALIBRATION_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 162 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK) | ||
| 163 | #define COEX_PERIODIC_CALIBRATION_FLAGS 0 | ||
| 164 | /* COEX_CONNECTION_ESTAB: we need DELAY_MEDIUM_FREE_NTFY to let WiMAX | ||
| 165 | * disconnect from network. */ | ||
| 166 | #define COEX_CONNECTION_ESTAB_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 167 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \ | ||
| 168 | COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK) | ||
| 169 | #define COEX_ASSOCIATED_IDLE_FLAGS 0 | ||
| 170 | #define COEX_ASSOC_MANUAL_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 171 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK) | ||
| 172 | #define COEX_ASSOC_AUTO_SCAN_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 173 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK) | ||
| 174 | #define COEX_ASSOC_ACTIVE_LEVEL_FLAGS 0 | ||
| 175 | #define COEX_RF_ON_FLAGS 0 | ||
| 176 | #define COEX_RF_OFF_FLAGS 0 | ||
| 177 | #define COEX_STAND_ALONE_DEBUG_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 178 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK) | ||
| 179 | #define COEX_IPAN_ASSOC_LEVEL_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 180 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \ | ||
| 181 | COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK) | ||
| 182 | #define COEX_RSRVD1_FLAGS 0 | ||
| 183 | #define COEX_RSRVD2_FLAGS 0 | ||
| 184 | /* XOR_RF_ON is the event wrapping all radio ownership. We need | ||
| 185 | * DELAY_MEDIUM_FREE_NTFY to let WiMAX disconnect from network. */ | ||
| 186 | #define COEX_XOR_RF_ON_FLAGS (COEX_EVT_FLAG_MEDIUM_FREE_NTFY_MSK | \ | ||
| 187 | COEX_EVT_FLAG_MEDIUM_ACTV_NTFY_MSK | \ | ||
| 188 | COEX_EVT_FLAG_DELAY_MEDIUM_FREE_NTFY_MSK) | ||
| 189 | |||
| 190 | /* CT kill config command */ | ||
| 191 | struct iwm_ct_kill_cfg_cmd { | ||
| 192 | u32 exit_threshold; | ||
| 193 | u32 reserved; | ||
| 194 | u32 entry_threshold; | ||
| 195 | } __packed; | ||
| 196 | |||
| 197 | |||
| 198 | /* LMAC OP CODES */ | ||
| 199 | #define REPLY_PAD 0x0 | ||
| 200 | #define REPLY_ALIVE 0x1 | ||
| 201 | #define REPLY_ERROR 0x2 | ||
| 202 | #define REPLY_ECHO 0x3 | ||
| 203 | #define REPLY_HALT 0x6 | ||
| 204 | |||
| 205 | /* RXON state commands */ | ||
| 206 | #define REPLY_RX_ON 0x10 | ||
| 207 | #define REPLY_RX_ON_ASSOC 0x11 | ||
| 208 | #define REPLY_RX_OFF 0x12 | ||
| 209 | #define REPLY_QOS_PARAM 0x13 | ||
| 210 | #define REPLY_RX_ON_TIMING 0x14 | ||
| 211 | #define REPLY_INTERNAL_QOS_PARAM 0x15 | ||
| 212 | #define REPLY_RX_INT_TIMEOUT_CNFG 0x16 | ||
| 213 | #define REPLY_NULL 0x17 | ||
| 214 | |||
| 215 | /* Multi-Station support */ | ||
| 216 | #define REPLY_ADD_STA 0x18 | ||
| 217 | #define REPLY_REMOVE_STA 0x19 | ||
| 218 | #define REPLY_RESET_ALL_STA 0x1a | ||
| 219 | |||
| 220 | /* RX, TX */ | ||
| 221 | #define REPLY_ALM_RX 0x1b | ||
| 222 | #define REPLY_TX 0x1c | ||
| 223 | #define REPLY_TXFIFO_FLUSH 0x1e | ||
| 224 | |||
| 225 | /* MISC commands */ | ||
| 226 | #define REPLY_MGMT_MCAST_KEY 0x1f | ||
| 227 | #define REPLY_WEPKEY 0x20 | ||
| 228 | #define REPLY_INIT_IV 0x21 | ||
| 229 | #define REPLY_WRITE_MIB 0x22 | ||
| 230 | #define REPLY_READ_MIB 0x23 | ||
| 231 | #define REPLY_RADIO_FE 0x24 | ||
| 232 | #define REPLY_TXFIFO_CFG 0x25 | ||
| 233 | #define REPLY_WRITE_READ 0x26 | ||
| 234 | #define REPLY_INSTALL_SEC_KEY 0x27 | ||
| 235 | |||
| 236 | |||
| 237 | #define REPLY_RATE_SCALE 0x47 | ||
| 238 | #define REPLY_LEDS_CMD 0x48 | ||
| 239 | #define REPLY_TX_LINK_QUALITY_CMD 0x4e | ||
| 240 | #define REPLY_ANA_MIB_OVERRIDE_CMD 0x4f | ||
| 241 | #define REPLY_WRITE2REG_CMD 0x50 | ||
| 242 | |||
| 243 | /* winfi-wifi coexistence */ | ||
| 244 | #define COEX_PRIORITY_TABLE_CMD 0x5a | ||
| 245 | #define COEX_MEDIUM_NOTIFICATION 0x5b | ||
| 246 | #define COEX_EVENT_CMD 0x5c | ||
| 247 | |||
| 248 | /* more Protocol and Protocol-test commands */ | ||
| 249 | #define REPLY_MAX_SLEEP_TIME_CMD 0x61 | ||
| 250 | #define CALIBRATION_CFG_CMD 0x65 | ||
| 251 | #define CALIBRATION_RES_NOTIFICATION 0x66 | ||
| 252 | #define CALIBRATION_COMPLETE_NOTIFICATION 0x67 | ||
| 253 | |||
| 254 | /* Measurements */ | ||
| 255 | #define REPLY_QUIET_CMD 0x71 | ||
| 256 | #define REPLY_CHANNEL_SWITCH 0x72 | ||
| 257 | #define CHANNEL_SWITCH_NOTIFICATION 0x73 | ||
| 258 | |||
| 259 | #define REPLY_SPECTRUM_MEASUREMENT_CMD 0x74 | ||
| 260 | #define SPECTRUM_MEASURE_NOTIFICATION 0x75 | ||
| 261 | #define REPLY_MEASUREMENT_ABORT_CMD 0x76 | ||
| 262 | |||
| 263 | /* Power Management */ | ||
| 264 | #define POWER_TABLE_CMD 0x77 | ||
| 265 | #define SAVE_RESTORE_ADDRESS_CMD 0x78 | ||
| 266 | #define REPLY_WATERMARK_CMD 0x79 | ||
| 267 | #define PM_DEBUG_STATISTIC_NOTIFIC 0x7B | ||
| 268 | #define PD_FLUSH_N_NOTIFICATION 0x7C | ||
| 269 | |||
| 270 | /* Scan commands and notifications */ | ||
| 271 | #define REPLY_SCAN_REQUEST_CMD 0x80 | ||
| 272 | #define REPLY_SCAN_ABORT_CMD 0x81 | ||
| 273 | #define SCAN_START_NOTIFICATION 0x82 | ||
| 274 | #define SCAN_RESULTS_NOTIFICATION 0x83 | ||
| 275 | #define SCAN_COMPLETE_NOTIFICATION 0x84 | ||
| 276 | |||
| 277 | /* Continuous TX commands */ | ||
| 278 | #define REPLY_CONT_TX_CMD 0x85 | ||
| 279 | #define END_OF_CONT_TX_NOTIFICATION 0x86 | ||
| 280 | |||
| 281 | /* Timer/Eeprom commands */ | ||
| 282 | #define TIMER_CMD 0x87 | ||
| 283 | #define EEPROM_WRITE_CMD 0x88 | ||
| 284 | |||
| 285 | /* PAPD commands */ | ||
| 286 | #define FEEDBACK_REQUEST_NOTIFICATION 0x8b | ||
| 287 | #define REPLY_CW_CMD 0x8c | ||
| 288 | |||
| 289 | /* IBSS/AP commands Continue */ | ||
| 290 | #define BEACON_NOTIFICATION 0x90 | ||
| 291 | #define REPLY_TX_BEACON 0x91 | ||
| 292 | #define REPLY_REQUEST_ATIM 0x93 | ||
| 293 | #define WHO_IS_AWAKE_NOTIFICATION 0x94 | ||
| 294 | #define TX_PWR_DBM_LIMIT_CMD 0x95 | ||
| 295 | #define QUIET_NOTIFICATION 0x96 | ||
| 296 | #define TX_PWR_TABLE_CMD 0x97 | ||
| 297 | #define TX_ANT_CONFIGURATION_CMD 0x98 | ||
| 298 | #define MEASURE_ABORT_NOTIFICATION 0x99 | ||
| 299 | #define REPLY_CALIBRATION_TUNE 0x9a | ||
| 300 | |||
| 301 | /* bt config command */ | ||
| 302 | #define REPLY_BT_CONFIG 0x9b | ||
| 303 | #define REPLY_STATISTICS_CMD 0x9c | ||
| 304 | #define STATISTICS_NOTIFICATION 0x9d | ||
| 305 | |||
| 306 | /* RF-KILL commands and notifications */ | ||
| 307 | #define REPLY_CARD_STATE_CMD 0xa0 | ||
| 308 | #define CARD_STATE_NOTIFICATION 0xa1 | ||
| 309 | |||
| 310 | /* Missed beacons notification */ | ||
| 311 | #define MISSED_BEACONS_NOTIFICATION 0xa2 | ||
| 312 | #define MISSED_BEACONS_NOTIFICATION_TH_CMD 0xa3 | ||
| 313 | |||
| 314 | #define REPLY_CT_KILL_CONFIG_CMD 0xa4 | ||
| 315 | |||
| 316 | /* HD commands and notifications */ | ||
| 317 | #define REPLY_HD_PARAMS_CMD 0xa6 | ||
| 318 | #define HD_PARAMS_NOTIFICATION 0xa7 | ||
| 319 | #define SENSITIVITY_CMD 0xa8 | ||
| 320 | #define U_APSD_PARAMS_CMD 0xa9 | ||
| 321 | #define NOISY_PLATFORM_CMD 0xaa | ||
| 322 | #define ILLEGAL_CMD 0xac | ||
| 323 | #define REPLY_PHY_CALIBRATION_CMD 0xb0 | ||
| 324 | #define REPLAY_RX_GAIN_CALIB_CMD 0xb1 | ||
| 325 | |||
| 326 | /* WiPAN commands */ | ||
| 327 | #define REPLY_WIPAN_PARAMS_CMD 0xb2 | ||
| 328 | #define REPLY_WIPAN_RX_ON_CMD 0xb3 | ||
| 329 | #define REPLY_WIPAN_RX_ON_TIMING 0xb4 | ||
| 330 | #define REPLY_WIPAN_TX_PWR_TABLE_CMD 0xb5 | ||
| 331 | #define REPLY_WIPAN_RXON_ASSOC_CMD 0xb6 | ||
| 332 | #define REPLY_WIPAN_QOS_PARAM 0xb7 | ||
| 333 | #define WIPAN_REPLY_WEPKEY 0xb8 | ||
| 334 | |||
| 335 | /* BeamForming commands */ | ||
| 336 | #define BEAMFORMER_CFG_CMD 0xba | ||
| 337 | #define BEAMFORMEE_NOTIFICATION 0xbb | ||
| 338 | |||
| 339 | /* TGn new Commands */ | ||
| 340 | #define REPLY_RX_PHY_CMD 0xc0 | ||
| 341 | #define REPLY_RX_MPDU_CMD 0xc1 | ||
| 342 | #define REPLY_MULTICAST_HASH 0xc2 | ||
| 343 | #define REPLY_KDR_RX 0xc3 | ||
| 344 | #define REPLY_RX_DSP_EXT_INFO 0xc4 | ||
| 345 | #define REPLY_COMPRESSED_BA 0xc5 | ||
| 346 | |||
| 347 | /* PNC commands */ | ||
| 348 | #define PNC_CONFIG_CMD 0xc8 | ||
| 349 | #define PNC_UPDATE_TABLE_CMD 0xc9 | ||
| 350 | #define XVT_GENERAL_CTRL_CMD 0xca | ||
| 351 | #define REPLY_LEGACY_RADIO_FE 0xdd | ||
| 352 | |||
| 353 | /* WoWLAN commands */ | ||
| 354 | #define WOWLAN_PATTERNS 0xe0 | ||
| 355 | #define WOWLAN_WAKEUP_FILTER 0xe1 | ||
| 356 | #define WOWLAN_TSC_RSC_PARAM 0xe2 | ||
| 357 | #define WOWLAN_TKIP_PARAM 0xe3 | ||
| 358 | #define WOWLAN_KEK_KCK_MATERIAL 0xe4 | ||
| 359 | #define WOWLAN_GET_STATUSES 0xe5 | ||
| 360 | #define WOWLAN_TX_POWER_PER_DB 0xe6 | ||
| 361 | #define REPLY_WOWLAN_GET_STATUSES WOWLAN_GET_STATUSES | ||
| 362 | |||
| 363 | #define REPLY_DEBUG_CMD 0xf0 | ||
| 364 | #define REPLY_DSP_DEBUG_CMD 0xf1 | ||
| 365 | #define REPLY_DEBUG_MONITOR_CMD 0xf2 | ||
| 366 | #define REPLY_DEBUG_XVT_CMD 0xf3 | ||
| 367 | #define REPLY_DEBUG_DC_CALIB 0xf4 | ||
| 368 | #define REPLY_DYNAMIC_BP 0xf5 | ||
| 369 | |||
| 370 | /* General purpose Commands */ | ||
| 371 | #define REPLY_GP1_CMD 0xfa | ||
| 372 | #define REPLY_GP2_CMD 0xfb | ||
| 373 | #define REPLY_GP3_CMD 0xfc | ||
| 374 | #define REPLY_GP4_CMD 0xfd | ||
| 375 | #define REPLY_REPLAY_WRAPPER 0xfe | ||
| 376 | #define REPLY_FRAME_DURATION_CALC_CMD 0xff | ||
| 377 | |||
| 378 | #define LMAC_COMMAND_ID_MAX 0xff | ||
| 379 | #define LMAC_COMMAND_ID_NUM (LMAC_COMMAND_ID_MAX + 1) | ||
| 380 | |||
| 381 | |||
| 382 | /* Calibration */ | ||
| 383 | |||
| 384 | enum { | ||
| 385 | PHY_CALIBRATE_DC_CMD = 0, | ||
| 386 | PHY_CALIBRATE_LO_CMD = 1, | ||
| 387 | PHY_CALIBRATE_RX_BB_CMD = 2, | ||
| 388 | PHY_CALIBRATE_TX_IQ_CMD = 3, | ||
| 389 | PHY_CALIBRATE_RX_IQ_CMD = 4, | ||
| 390 | PHY_CALIBRATION_NOISE_CMD = 5, | ||
| 391 | PHY_CALIBRATE_AGC_TABLE_CMD = 6, | ||
| 392 | PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 7, | ||
| 393 | PHY_CALIBRATE_OPCODES_NUM, | ||
| 394 | SHILOH_PHY_CALIBRATE_DC_CMD = 8, | ||
| 395 | SHILOH_PHY_CALIBRATE_LO_CMD = 9, | ||
| 396 | SHILOH_PHY_CALIBRATE_RX_BB_CMD = 10, | ||
| 397 | SHILOH_PHY_CALIBRATE_TX_IQ_CMD = 11, | ||
| 398 | SHILOH_PHY_CALIBRATE_RX_IQ_CMD = 12, | ||
| 399 | SHILOH_PHY_CALIBRATION_NOISE_CMD = 13, | ||
| 400 | SHILOH_PHY_CALIBRATE_AGC_TABLE_CMD = 14, | ||
| 401 | SHILOH_PHY_CALIBRATE_CRYSTAL_FRQ_CMD = 15, | ||
| 402 | SHILOH_PHY_CALIBRATE_BASE_BAND_CMD = 16, | ||
| 403 | SHILOH_PHY_CALIBRATE_TXIQ_PERIODIC_CMD = 17, | ||
| 404 | CALIBRATION_CMD_NUM, | ||
| 405 | }; | ||
| 406 | |||
| 407 | enum { | ||
| 408 | CALIB_CFG_RX_BB_IDX = 0, | ||
| 409 | CALIB_CFG_DC_IDX = 1, | ||
| 410 | CALIB_CFG_LO_IDX = 2, | ||
| 411 | CALIB_CFG_TX_IQ_IDX = 3, | ||
| 412 | CALIB_CFG_RX_IQ_IDX = 4, | ||
| 413 | CALIB_CFG_NOISE_IDX = 5, | ||
| 414 | CALIB_CFG_CRYSTAL_IDX = 6, | ||
| 415 | CALIB_CFG_TEMPERATURE_IDX = 7, | ||
| 416 | CALIB_CFG_PAPD_IDX = 8, | ||
| 417 | CALIB_CFG_LAST_IDX = CALIB_CFG_PAPD_IDX, | ||
| 418 | CALIB_CFG_MODULE_NUM, | ||
| 419 | }; | ||
| 420 | |||
| 421 | #define IWM_CALIB_MAP_INIT_MSK 0xFFFF | ||
| 422 | #define IWM_CALIB_MAP_PER_LMAC(m) ((m & 0xFF0000) >> 16) | ||
| 423 | #define IWM_CALIB_MAP_PER_UMAC(m) ((m & 0xFF000000) >> 24) | ||
| 424 | #define IWM_CALIB_OPCODE_TO_INDEX(op) (op - PHY_CALIBRATE_OPCODES_NUM) | ||
| 425 | |||
| 426 | struct iwm_lmac_calib_hdr { | ||
| 427 | u8 opcode; | ||
| 428 | u8 first_grp; | ||
| 429 | u8 grp_num; | ||
| 430 | u8 all_data_valid; | ||
| 431 | } __packed; | ||
| 432 | |||
| 433 | #define IWM_LMAC_CALIB_FREQ_GROUPS_NR 7 | ||
| 434 | #define IWM_CALIB_FREQ_GROUPS_NR 5 | ||
| 435 | #define IWM_CALIB_DC_MODES_NR 12 | ||
| 436 | |||
| 437 | struct iwm_calib_rxiq_entry { | ||
| 438 | u16 ptam_postdist_ars; | ||
| 439 | u16 ptam_postdist_arc; | ||
| 440 | } __packed; | ||
| 441 | |||
| 442 | struct iwm_calib_rxiq_group { | ||
| 443 | struct iwm_calib_rxiq_entry mode[IWM_CALIB_DC_MODES_NR]; | ||
| 444 | } __packed; | ||
| 445 | |||
| 446 | struct iwm_lmac_calib_rxiq { | ||
| 447 | struct iwm_calib_rxiq_group group[IWM_LMAC_CALIB_FREQ_GROUPS_NR]; | ||
| 448 | } __packed; | ||
| 449 | |||
| 450 | struct iwm_calib_rxiq { | ||
| 451 | struct iwm_lmac_calib_hdr hdr; | ||
| 452 | struct iwm_calib_rxiq_group group[IWM_CALIB_FREQ_GROUPS_NR]; | ||
| 453 | } __packed; | ||
| 454 | |||
| 455 | #define LMAC_STA_ID_SEED 0x0f | ||
| 456 | #define LMAC_STA_ID_POS 0 | ||
| 457 | |||
| 458 | #define LMAC_STA_COLOR_SEED 0x7 | ||
| 459 | #define LMAC_STA_COLOR_POS 4 | ||
| 460 | |||
| 461 | struct iwm_lmac_power_report { | ||
| 462 | u8 pa_status; | ||
| 463 | u8 pa_integ_res_A[3]; | ||
| 464 | u8 pa_integ_res_B[3]; | ||
| 465 | u8 pa_integ_res_C[3]; | ||
| 466 | } __packed; | ||
| 467 | |||
| 468 | struct iwm_lmac_tx_resp { | ||
| 469 | u8 frame_cnt; /* 1-no aggregation, greater then 1 - aggregation */ | ||
| 470 | u8 bt_kill_cnt; | ||
| 471 | __le16 retry_cnt; | ||
| 472 | __le32 initial_tx_rate; | ||
| 473 | __le16 wireless_media_time; | ||
| 474 | struct iwm_lmac_power_report power_report; | ||
| 475 | __le32 tfd_info; | ||
| 476 | __le16 seq_ctl; | ||
| 477 | __le16 byte_cnt; | ||
| 478 | u8 tlc_rate_info; | ||
| 479 | u8 ra_tid; | ||
| 480 | __le16 frame_ctl; | ||
| 481 | __le32 status; | ||
| 482 | } __packed; | ||
| 483 | |||
| 484 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c new file mode 100644 index 00000000000..362002735b1 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/main.c | |||
| @@ -0,0 +1,846 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #include <linux/kernel.h> | ||
| 40 | #include <linux/netdevice.h> | ||
| 41 | #include <linux/sched.h> | ||
| 42 | #include <linux/ieee80211.h> | ||
| 43 | #include <linux/wireless.h> | ||
| 44 | #include <linux/slab.h> | ||
| 45 | |||
| 46 | #include "iwm.h" | ||
| 47 | #include "debug.h" | ||
| 48 | #include "bus.h" | ||
| 49 | #include "umac.h" | ||
| 50 | #include "commands.h" | ||
| 51 | #include "hal.h" | ||
| 52 | #include "fw.h" | ||
| 53 | #include "rx.h" | ||
| 54 | |||
| 55 | static struct iwm_conf def_iwm_conf = { | ||
| 56 | |||
| 57 | .sdio_ior_timeout = 5000, | ||
| 58 | .calib_map = BIT(CALIB_CFG_DC_IDX) | | ||
| 59 | BIT(CALIB_CFG_LO_IDX) | | ||
| 60 | BIT(CALIB_CFG_TX_IQ_IDX) | | ||
| 61 | BIT(CALIB_CFG_RX_IQ_IDX) | | ||
| 62 | BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD), | ||
| 63 | .expected_calib_map = BIT(PHY_CALIBRATE_DC_CMD) | | ||
| 64 | BIT(PHY_CALIBRATE_LO_CMD) | | ||
| 65 | BIT(PHY_CALIBRATE_TX_IQ_CMD) | | ||
| 66 | BIT(PHY_CALIBRATE_RX_IQ_CMD) | | ||
| 67 | BIT(SHILOH_PHY_CALIBRATE_BASE_BAND_CMD), | ||
| 68 | .ct_kill_entry = 110, | ||
| 69 | .ct_kill_exit = 110, | ||
| 70 | .reset_on_fatal_err = 1, | ||
| 71 | .auto_connect = 1, | ||
| 72 | .enable_qos = 1, | ||
| 73 | .mode = UMAC_MODE_BSS, | ||
| 74 | |||
| 75 | /* UMAC configuration */ | ||
| 76 | .power_index = 0, | ||
| 77 | .frag_threshold = IEEE80211_MAX_FRAG_THRESHOLD, | ||
| 78 | .rts_threshold = IEEE80211_MAX_RTS_THRESHOLD, | ||
| 79 | .cts_to_self = 0, | ||
| 80 | |||
| 81 | .assoc_timeout = 2, | ||
| 82 | .roam_timeout = 10, | ||
| 83 | .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G | | ||
| 84 | WIRELESS_MODE_11N, | ||
| 85 | |||
| 86 | /* IBSS */ | ||
| 87 | .ibss_band = UMAC_BAND_2GHZ, | ||
| 88 | .ibss_channel = 1, | ||
| 89 | |||
| 90 | .mac_addr = {0x00, 0x02, 0xb3, 0x01, 0x02, 0x03}, | ||
| 91 | }; | ||
| 92 | |||
| 93 | static int modparam_reset; | ||
| 94 | module_param_named(reset, modparam_reset, bool, 0644); | ||
| 95 | MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])"); | ||
| 96 | |||
| 97 | static int modparam_wimax_enable = 1; | ||
| 98 | module_param_named(wimax_enable, modparam_wimax_enable, bool, 0644); | ||
| 99 | MODULE_PARM_DESC(wimax_enable, "Enable wimax core (default 1 [wimax enabled])"); | ||
| 100 | |||
| 101 | int iwm_mode_to_nl80211_iftype(int mode) | ||
| 102 | { | ||
| 103 | switch (mode) { | ||
| 104 | case UMAC_MODE_BSS: | ||
| 105 | return NL80211_IFTYPE_STATION; | ||
| 106 | case UMAC_MODE_IBSS: | ||
| 107 | return NL80211_IFTYPE_ADHOC; | ||
| 108 | default: | ||
| 109 | return NL80211_IFTYPE_UNSPECIFIED; | ||
| 110 | } | ||
| 111 | |||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static void iwm_statistics_request(struct work_struct *work) | ||
| 116 | { | ||
| 117 | struct iwm_priv *iwm = | ||
| 118 | container_of(work, struct iwm_priv, stats_request.work); | ||
| 119 | |||
| 120 | iwm_send_umac_stats_req(iwm, 0); | ||
| 121 | } | ||
| 122 | |||
| 123 | static void iwm_disconnect_work(struct work_struct *work) | ||
| 124 | { | ||
| 125 | struct iwm_priv *iwm = | ||
| 126 | container_of(work, struct iwm_priv, disconnect.work); | ||
| 127 | |||
| 128 | if (iwm->umac_profile_active) | ||
| 129 | iwm_invalidate_mlme_profile(iwm); | ||
| 130 | |||
| 131 | clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status); | ||
| 132 | iwm->umac_profile_active = 0; | ||
| 133 | memset(iwm->bssid, 0, ETH_ALEN); | ||
| 134 | iwm->channel = 0; | ||
| 135 | |||
| 136 | iwm_link_off(iwm); | ||
| 137 | |||
| 138 | wake_up_interruptible(&iwm->mlme_queue); | ||
| 139 | |||
| 140 | cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, GFP_KERNEL); | ||
| 141 | } | ||
| 142 | |||
| 143 | static void iwm_ct_kill_work(struct work_struct *work) | ||
| 144 | { | ||
| 145 | struct iwm_priv *iwm = | ||
| 146 | container_of(work, struct iwm_priv, ct_kill_delay.work); | ||
| 147 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 148 | |||
| 149 | IWM_INFO(iwm, "CT kill delay timeout\n"); | ||
| 150 | |||
| 151 | wiphy_rfkill_set_hw_state(wiphy, false); | ||
| 152 | } | ||
| 153 | |||
| 154 | static int __iwm_up(struct iwm_priv *iwm); | ||
| 155 | static int __iwm_down(struct iwm_priv *iwm); | ||
| 156 | |||
| 157 | static void iwm_reset_worker(struct work_struct *work) | ||
| 158 | { | ||
| 159 | struct iwm_priv *iwm; | ||
| 160 | struct iwm_umac_profile *profile = NULL; | ||
| 161 | int uninitialized_var(ret), retry = 0; | ||
| 162 | |||
| 163 | iwm = container_of(work, struct iwm_priv, reset_worker); | ||
| 164 | |||
| 165 | /* | ||
| 166 | * XXX: The iwm->mutex is introduced purely for this reset work, | ||
| 167 | * because the other users for iwm_up and iwm_down are only netdev | ||
| 168 | * ndo_open and ndo_stop which are already protected by rtnl. | ||
| 169 | * Please remove iwm->mutex together if iwm_reset_worker() is not | ||
| 170 | * required in the future. | ||
| 171 | */ | ||
| 172 | if (!mutex_trylock(&iwm->mutex)) { | ||
| 173 | IWM_WARN(iwm, "We are in the middle of interface bringing " | ||
| 174 | "UP/DOWN. Skip driver resetting.\n"); | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | |||
| 178 | if (iwm->umac_profile_active) { | ||
| 179 | profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL); | ||
| 180 | if (profile) | ||
| 181 | memcpy(profile, iwm->umac_profile, sizeof(*profile)); | ||
| 182 | else | ||
| 183 | IWM_ERR(iwm, "Couldn't alloc memory for profile\n"); | ||
| 184 | } | ||
| 185 | |||
| 186 | __iwm_down(iwm); | ||
| 187 | |||
| 188 | while (retry++ < 3) { | ||
| 189 | ret = __iwm_up(iwm); | ||
| 190 | if (!ret) | ||
| 191 | break; | ||
| 192 | |||
| 193 | schedule_timeout_uninterruptible(10 * HZ); | ||
| 194 | } | ||
| 195 | |||
| 196 | if (ret) { | ||
| 197 | IWM_WARN(iwm, "iwm_up() failed: %d\n", ret); | ||
| 198 | |||
| 199 | kfree(profile); | ||
| 200 | goto out; | ||
| 201 | } | ||
| 202 | |||
| 203 | if (profile) { | ||
| 204 | IWM_DBG_MLME(iwm, DBG, "Resend UMAC profile\n"); | ||
| 205 | memcpy(iwm->umac_profile, profile, sizeof(*profile)); | ||
| 206 | iwm_send_mlme_profile(iwm); | ||
| 207 | kfree(profile); | ||
| 208 | } else | ||
| 209 | clear_bit(IWM_STATUS_RESETTING, &iwm->status); | ||
| 210 | |||
| 211 | out: | ||
| 212 | mutex_unlock(&iwm->mutex); | ||
| 213 | } | ||
| 214 | |||
| 215 | static void iwm_auth_retry_worker(struct work_struct *work) | ||
| 216 | { | ||
| 217 | struct iwm_priv *iwm; | ||
| 218 | int i, ret; | ||
| 219 | |||
| 220 | iwm = container_of(work, struct iwm_priv, auth_retry_worker); | ||
| 221 | if (iwm->umac_profile_active) { | ||
| 222 | ret = iwm_invalidate_mlme_profile(iwm); | ||
| 223 | if (ret < 0) | ||
| 224 | return; | ||
| 225 | } | ||
| 226 | |||
| 227 | iwm->umac_profile->sec.auth_type = UMAC_AUTH_TYPE_LEGACY_PSK; | ||
| 228 | |||
| 229 | ret = iwm_send_mlme_profile(iwm); | ||
| 230 | if (ret < 0) | ||
| 231 | return; | ||
| 232 | |||
| 233 | for (i = 0; i < IWM_NUM_KEYS; i++) | ||
| 234 | if (iwm->keys[i].key_len) | ||
| 235 | iwm_set_key(iwm, 0, &iwm->keys[i]); | ||
| 236 | |||
| 237 | iwm_set_tx_key(iwm, iwm->default_key); | ||
| 238 | } | ||
| 239 | |||
| 240 | |||
| 241 | |||
| 242 | static void iwm_watchdog(unsigned long data) | ||
| 243 | { | ||
| 244 | struct iwm_priv *iwm = (struct iwm_priv *)data; | ||
| 245 | |||
| 246 | IWM_WARN(iwm, "Watchdog expired: UMAC stalls!\n"); | ||
| 247 | |||
| 248 | if (modparam_reset) | ||
| 249 | iwm_resetting(iwm); | ||
| 250 | } | ||
| 251 | |||
| 252 | int iwm_priv_init(struct iwm_priv *iwm) | ||
| 253 | { | ||
| 254 | int i, j; | ||
| 255 | char name[32]; | ||
| 256 | |||
| 257 | iwm->status = 0; | ||
| 258 | INIT_LIST_HEAD(&iwm->pending_notif); | ||
| 259 | init_waitqueue_head(&iwm->notif_queue); | ||
| 260 | init_waitqueue_head(&iwm->nonwifi_queue); | ||
| 261 | init_waitqueue_head(&iwm->wifi_ntfy_queue); | ||
| 262 | init_waitqueue_head(&iwm->mlme_queue); | ||
| 263 | memcpy(&iwm->conf, &def_iwm_conf, sizeof(struct iwm_conf)); | ||
| 264 | spin_lock_init(&iwm->tx_credit.lock); | ||
| 265 | INIT_LIST_HEAD(&iwm->wifi_pending_cmd); | ||
| 266 | INIT_LIST_HEAD(&iwm->nonwifi_pending_cmd); | ||
| 267 | iwm->wifi_seq_num = UMAC_WIFI_SEQ_NUM_BASE; | ||
| 268 | iwm->nonwifi_seq_num = UMAC_NONWIFI_SEQ_NUM_BASE; | ||
| 269 | spin_lock_init(&iwm->cmd_lock); | ||
| 270 | iwm->scan_id = 1; | ||
| 271 | INIT_DELAYED_WORK(&iwm->stats_request, iwm_statistics_request); | ||
| 272 | INIT_DELAYED_WORK(&iwm->disconnect, iwm_disconnect_work); | ||
| 273 | INIT_DELAYED_WORK(&iwm->ct_kill_delay, iwm_ct_kill_work); | ||
| 274 | INIT_WORK(&iwm->reset_worker, iwm_reset_worker); | ||
| 275 | INIT_WORK(&iwm->auth_retry_worker, iwm_auth_retry_worker); | ||
| 276 | INIT_LIST_HEAD(&iwm->bss_list); | ||
| 277 | |||
| 278 | skb_queue_head_init(&iwm->rx_list); | ||
| 279 | INIT_LIST_HEAD(&iwm->rx_tickets); | ||
| 280 | spin_lock_init(&iwm->ticket_lock); | ||
| 281 | for (i = 0; i < IWM_RX_ID_HASH; i++) { | ||
| 282 | INIT_LIST_HEAD(&iwm->rx_packets[i]); | ||
| 283 | spin_lock_init(&iwm->packet_lock[i]); | ||
| 284 | } | ||
| 285 | |||
| 286 | INIT_WORK(&iwm->rx_worker, iwm_rx_worker); | ||
| 287 | |||
| 288 | iwm->rx_wq = create_singlethread_workqueue(KBUILD_MODNAME "_rx"); | ||
| 289 | if (!iwm->rx_wq) | ||
| 290 | return -EAGAIN; | ||
| 291 | |||
| 292 | for (i = 0; i < IWM_TX_QUEUES; i++) { | ||
| 293 | INIT_WORK(&iwm->txq[i].worker, iwm_tx_worker); | ||
| 294 | snprintf(name, 32, KBUILD_MODNAME "_tx_%d", i); | ||
| 295 | iwm->txq[i].id = i; | ||
| 296 | iwm->txq[i].wq = create_singlethread_workqueue(name); | ||
| 297 | if (!iwm->txq[i].wq) | ||
| 298 | return -EAGAIN; | ||
| 299 | |||
| 300 | skb_queue_head_init(&iwm->txq[i].queue); | ||
| 301 | skb_queue_head_init(&iwm->txq[i].stopped_queue); | ||
| 302 | spin_lock_init(&iwm->txq[i].lock); | ||
| 303 | } | ||
| 304 | |||
| 305 | for (i = 0; i < IWM_NUM_KEYS; i++) | ||
| 306 | memset(&iwm->keys[i], 0, sizeof(struct iwm_key)); | ||
| 307 | |||
| 308 | iwm->default_key = -1; | ||
| 309 | |||
| 310 | for (i = 0; i < IWM_STA_TABLE_NUM; i++) | ||
| 311 | for (j = 0; j < IWM_UMAC_TID_NR; j++) { | ||
| 312 | mutex_init(&iwm->sta_table[i].tid_info[j].mutex); | ||
| 313 | iwm->sta_table[i].tid_info[j].stopped = false; | ||
| 314 | } | ||
| 315 | |||
| 316 | init_timer(&iwm->watchdog); | ||
| 317 | iwm->watchdog.function = iwm_watchdog; | ||
| 318 | iwm->watchdog.data = (unsigned long)iwm; | ||
| 319 | mutex_init(&iwm->mutex); | ||
| 320 | |||
| 321 | iwm->last_fw_err = kzalloc(sizeof(struct iwm_fw_error_hdr), | ||
| 322 | GFP_KERNEL); | ||
| 323 | if (iwm->last_fw_err == NULL) | ||
| 324 | return -ENOMEM; | ||
| 325 | |||
| 326 | return 0; | ||
| 327 | } | ||
| 328 | |||
| 329 | void iwm_priv_deinit(struct iwm_priv *iwm) | ||
| 330 | { | ||
| 331 | int i; | ||
| 332 | |||
| 333 | for (i = 0; i < IWM_TX_QUEUES; i++) | ||
| 334 | destroy_workqueue(iwm->txq[i].wq); | ||
| 335 | |||
| 336 | destroy_workqueue(iwm->rx_wq); | ||
| 337 | kfree(iwm->last_fw_err); | ||
| 338 | } | ||
| 339 | |||
| 340 | /* | ||
| 341 | * We reset all the structures, and we reset the UMAC. | ||
| 342 | * After calling this routine, you're expected to reload | ||
| 343 | * the firmware. | ||
| 344 | */ | ||
| 345 | void iwm_reset(struct iwm_priv *iwm) | ||
| 346 | { | ||
| 347 | struct iwm_notif *notif, *next; | ||
| 348 | |||
| 349 | if (test_bit(IWM_STATUS_READY, &iwm->status)) | ||
| 350 | iwm_target_reset(iwm); | ||
| 351 | |||
| 352 | if (test_bit(IWM_STATUS_RESETTING, &iwm->status)) { | ||
| 353 | iwm->status = 0; | ||
| 354 | set_bit(IWM_STATUS_RESETTING, &iwm->status); | ||
| 355 | } else | ||
| 356 | iwm->status = 0; | ||
| 357 | iwm->scan_id = 1; | ||
| 358 | |||
| 359 | list_for_each_entry_safe(notif, next, &iwm->pending_notif, pending) { | ||
| 360 | list_del(¬if->pending); | ||
| 361 | kfree(notif->buf); | ||
| 362 | kfree(notif); | ||
| 363 | } | ||
| 364 | |||
| 365 | iwm_cmd_flush(iwm); | ||
| 366 | |||
| 367 | flush_workqueue(iwm->rx_wq); | ||
| 368 | |||
| 369 | iwm_link_off(iwm); | ||
| 370 | } | ||
| 371 | |||
| 372 | void iwm_resetting(struct iwm_priv *iwm) | ||
| 373 | { | ||
| 374 | set_bit(IWM_STATUS_RESETTING, &iwm->status); | ||
| 375 | |||
| 376 | schedule_work(&iwm->reset_worker); | ||
| 377 | } | ||
| 378 | |||
| 379 | /* | ||
| 380 | * Notification code: | ||
| 381 | * | ||
| 382 | * We're faced with the following issue: Any host command can | ||
| 383 | * have an answer or not, and if there's an answer to expect, | ||
| 384 | * it can be treated synchronously or asynchronously. | ||
| 385 | * To work around the synchronous answer case, we implemented | ||
| 386 | * our notification mechanism. | ||
| 387 | * When a code path needs to wait for a command response | ||
| 388 | * synchronously, it calls notif_handle(), which waits for the | ||
| 389 | * right notification to show up, and then process it. Before | ||
| 390 | * starting to wait, it registered as a waiter for this specific | ||
| 391 | * answer (by toggling a bit in on of the handler_map), so that | ||
| 392 | * the rx code knows that it needs to send a notification to the | ||
| 393 | * waiting processes. It does so by calling iwm_notif_send(), | ||
| 394 | * which adds the notification to the pending notifications list, | ||
| 395 | * and then wakes the waiting processes up. | ||
| 396 | */ | ||
| 397 | int iwm_notif_send(struct iwm_priv *iwm, struct iwm_wifi_cmd *cmd, | ||
| 398 | u8 cmd_id, u8 source, u8 *buf, unsigned long buf_size) | ||
| 399 | { | ||
| 400 | struct iwm_notif *notif; | ||
| 401 | |||
| 402 | notif = kzalloc(sizeof(struct iwm_notif), GFP_KERNEL); | ||
| 403 | if (!notif) { | ||
| 404 | IWM_ERR(iwm, "Couldn't alloc memory for notification\n"); | ||
| 405 | return -ENOMEM; | ||
| 406 | } | ||
| 407 | |||
| 408 | INIT_LIST_HEAD(¬if->pending); | ||
| 409 | notif->cmd = cmd; | ||
| 410 | notif->cmd_id = cmd_id; | ||
| 411 | notif->src = source; | ||
| 412 | notif->buf = kzalloc(buf_size, GFP_KERNEL); | ||
| 413 | if (!notif->buf) { | ||
| 414 | IWM_ERR(iwm, "Couldn't alloc notification buffer\n"); | ||
| 415 | kfree(notif); | ||
| 416 | return -ENOMEM; | ||
| 417 | } | ||
| 418 | notif->buf_size = buf_size; | ||
| 419 | memcpy(notif->buf, buf, buf_size); | ||
| 420 | list_add_tail(¬if->pending, &iwm->pending_notif); | ||
| 421 | |||
| 422 | wake_up_interruptible(&iwm->notif_queue); | ||
| 423 | |||
| 424 | return 0; | ||
| 425 | } | ||
| 426 | |||
| 427 | static struct iwm_notif *iwm_notif_find(struct iwm_priv *iwm, u32 cmd, | ||
| 428 | u8 source) | ||
| 429 | { | ||
| 430 | struct iwm_notif *notif; | ||
| 431 | |||
| 432 | list_for_each_entry(notif, &iwm->pending_notif, pending) { | ||
| 433 | if ((notif->cmd_id == cmd) && (notif->src == source)) { | ||
| 434 | list_del(¬if->pending); | ||
| 435 | return notif; | ||
| 436 | } | ||
| 437 | } | ||
| 438 | |||
| 439 | return NULL; | ||
| 440 | } | ||
| 441 | |||
| 442 | static struct iwm_notif *iwm_notif_wait(struct iwm_priv *iwm, u32 cmd, | ||
| 443 | u8 source, long timeout) | ||
| 444 | { | ||
| 445 | int ret; | ||
| 446 | struct iwm_notif *notif; | ||
| 447 | unsigned long *map = NULL; | ||
| 448 | |||
| 449 | switch (source) { | ||
| 450 | case IWM_SRC_LMAC: | ||
| 451 | map = &iwm->lmac_handler_map[0]; | ||
| 452 | break; | ||
| 453 | case IWM_SRC_UMAC: | ||
| 454 | map = &iwm->umac_handler_map[0]; | ||
| 455 | break; | ||
| 456 | case IWM_SRC_UDMA: | ||
| 457 | map = &iwm->udma_handler_map[0]; | ||
| 458 | break; | ||
| 459 | } | ||
| 460 | |||
| 461 | set_bit(cmd, map); | ||
| 462 | |||
| 463 | ret = wait_event_interruptible_timeout(iwm->notif_queue, | ||
| 464 | ((notif = iwm_notif_find(iwm, cmd, source)) != NULL), | ||
| 465 | timeout); | ||
| 466 | clear_bit(cmd, map); | ||
| 467 | |||
| 468 | if (!ret) | ||
| 469 | return NULL; | ||
| 470 | |||
| 471 | return notif; | ||
| 472 | } | ||
| 473 | |||
| 474 | int iwm_notif_handle(struct iwm_priv *iwm, u32 cmd, u8 source, long timeout) | ||
| 475 | { | ||
| 476 | int ret; | ||
| 477 | struct iwm_notif *notif; | ||
| 478 | |||
| 479 | notif = iwm_notif_wait(iwm, cmd, source, timeout); | ||
| 480 | if (!notif) | ||
| 481 | return -ETIME; | ||
| 482 | |||
| 483 | ret = iwm_rx_handle_resp(iwm, notif->buf, notif->buf_size, notif->cmd); | ||
| 484 | kfree(notif->buf); | ||
| 485 | kfree(notif); | ||
| 486 | |||
| 487 | return ret; | ||
| 488 | } | ||
| 489 | |||
| 490 | static int iwm_config_boot_params(struct iwm_priv *iwm) | ||
| 491 | { | ||
| 492 | struct iwm_udma_nonwifi_cmd target_cmd; | ||
| 493 | int ret; | ||
| 494 | |||
| 495 | /* check Wimax is off and config debug monitor */ | ||
| 496 | if (!modparam_wimax_enable) { | ||
| 497 | u32 data1 = 0x1f; | ||
| 498 | u32 addr1 = 0x606BE258; | ||
| 499 | |||
| 500 | u32 data2_set = 0x0; | ||
| 501 | u32 data2_clr = 0x1; | ||
| 502 | u32 addr2 = 0x606BE100; | ||
| 503 | |||
| 504 | u32 data3 = 0x1; | ||
| 505 | u32 addr3 = 0x606BEC00; | ||
| 506 | |||
| 507 | target_cmd.resp = 0; | ||
| 508 | target_cmd.handle_by_hw = 0; | ||
| 509 | target_cmd.eop = 1; | ||
| 510 | |||
| 511 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; | ||
| 512 | target_cmd.addr = cpu_to_le32(addr1); | ||
| 513 | target_cmd.op1_sz = cpu_to_le32(sizeof(u32)); | ||
| 514 | target_cmd.op2 = 0; | ||
| 515 | |||
| 516 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1); | ||
| 517 | if (ret < 0) { | ||
| 518 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | ||
| 519 | return ret; | ||
| 520 | } | ||
| 521 | |||
| 522 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE; | ||
| 523 | target_cmd.addr = cpu_to_le32(addr2); | ||
| 524 | target_cmd.op1_sz = cpu_to_le32(data2_set); | ||
| 525 | target_cmd.op2 = cpu_to_le32(data2_clr); | ||
| 526 | |||
| 527 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data1); | ||
| 528 | if (ret < 0) { | ||
| 529 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | ||
| 530 | return ret; | ||
| 531 | } | ||
| 532 | |||
| 533 | target_cmd.opcode = UMAC_HDI_OUT_OPCODE_WRITE; | ||
| 534 | target_cmd.addr = cpu_to_le32(addr3); | ||
| 535 | target_cmd.op1_sz = cpu_to_le32(sizeof(u32)); | ||
| 536 | target_cmd.op2 = 0; | ||
| 537 | |||
| 538 | ret = iwm_hal_send_target_cmd(iwm, &target_cmd, &data3); | ||
| 539 | if (ret < 0) { | ||
| 540 | IWM_ERR(iwm, "iwm_hal_send_target_cmd failed\n"); | ||
| 541 | return ret; | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | return 0; | ||
| 546 | } | ||
| 547 | |||
| 548 | void iwm_init_default_profile(struct iwm_priv *iwm, | ||
| 549 | struct iwm_umac_profile *profile) | ||
| 550 | { | ||
| 551 | memset(profile, 0, sizeof(struct iwm_umac_profile)); | ||
| 552 | |||
| 553 | profile->sec.auth_type = UMAC_AUTH_TYPE_OPEN; | ||
| 554 | profile->sec.flags = UMAC_SEC_FLG_LEGACY_PROFILE; | ||
| 555 | profile->sec.ucast_cipher = UMAC_CIPHER_TYPE_NONE; | ||
| 556 | profile->sec.mcast_cipher = UMAC_CIPHER_TYPE_NONE; | ||
| 557 | |||
| 558 | if (iwm->conf.enable_qos) | ||
| 559 | profile->flags |= cpu_to_le16(UMAC_PROFILE_QOS_ALLOWED); | ||
| 560 | |||
| 561 | profile->wireless_mode = iwm->conf.wireless_mode; | ||
| 562 | profile->mode = cpu_to_le32(iwm->conf.mode); | ||
| 563 | |||
| 564 | profile->ibss.atim = 0; | ||
| 565 | profile->ibss.beacon_interval = 100; | ||
| 566 | profile->ibss.join_only = 0; | ||
| 567 | profile->ibss.band = iwm->conf.ibss_band; | ||
| 568 | profile->ibss.channel = iwm->conf.ibss_channel; | ||
| 569 | } | ||
| 570 | |||
| 571 | void iwm_link_on(struct iwm_priv *iwm) | ||
| 572 | { | ||
| 573 | netif_carrier_on(iwm_to_ndev(iwm)); | ||
| 574 | netif_tx_wake_all_queues(iwm_to_ndev(iwm)); | ||
| 575 | |||
| 576 | iwm_send_umac_stats_req(iwm, 0); | ||
| 577 | } | ||
| 578 | |||
| 579 | void iwm_link_off(struct iwm_priv *iwm) | ||
| 580 | { | ||
| 581 | struct iw_statistics *wstats = &iwm->wstats; | ||
| 582 | int i; | ||
| 583 | |||
| 584 | netif_tx_stop_all_queues(iwm_to_ndev(iwm)); | ||
| 585 | netif_carrier_off(iwm_to_ndev(iwm)); | ||
| 586 | |||
| 587 | for (i = 0; i < IWM_TX_QUEUES; i++) { | ||
| 588 | skb_queue_purge(&iwm->txq[i].queue); | ||
| 589 | skb_queue_purge(&iwm->txq[i].stopped_queue); | ||
| 590 | |||
| 591 | iwm->txq[i].concat_count = 0; | ||
| 592 | iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf; | ||
| 593 | |||
| 594 | flush_workqueue(iwm->txq[i].wq); | ||
| 595 | } | ||
| 596 | |||
| 597 | iwm_rx_free(iwm); | ||
| 598 | |||
| 599 | cancel_delayed_work_sync(&iwm->stats_request); | ||
| 600 | memset(wstats, 0, sizeof(struct iw_statistics)); | ||
| 601 | wstats->qual.updated = IW_QUAL_ALL_INVALID; | ||
| 602 | |||
| 603 | kfree(iwm->req_ie); | ||
| 604 | iwm->req_ie = NULL; | ||
| 605 | iwm->req_ie_len = 0; | ||
| 606 | kfree(iwm->resp_ie); | ||
| 607 | iwm->resp_ie = NULL; | ||
| 608 | iwm->resp_ie_len = 0; | ||
| 609 | |||
| 610 | del_timer_sync(&iwm->watchdog); | ||
| 611 | } | ||
| 612 | |||
| 613 | static void iwm_bss_list_clean(struct iwm_priv *iwm) | ||
| 614 | { | ||
| 615 | struct iwm_bss_info *bss, *next; | ||
| 616 | |||
| 617 | list_for_each_entry_safe(bss, next, &iwm->bss_list, node) { | ||
| 618 | list_del(&bss->node); | ||
| 619 | kfree(bss->bss); | ||
| 620 | kfree(bss); | ||
| 621 | } | ||
| 622 | } | ||
| 623 | |||
| 624 | static int iwm_channels_init(struct iwm_priv *iwm) | ||
| 625 | { | ||
| 626 | int ret; | ||
| 627 | |||
| 628 | ret = iwm_send_umac_channel_list(iwm); | ||
| 629 | if (ret) { | ||
| 630 | IWM_ERR(iwm, "Send channel list failed\n"); | ||
| 631 | return ret; | ||
| 632 | } | ||
| 633 | |||
| 634 | ret = iwm_notif_handle(iwm, UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST, | ||
| 635 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
| 636 | if (ret) { | ||
| 637 | IWM_ERR(iwm, "Didn't get a channel list notification\n"); | ||
| 638 | return ret; | ||
| 639 | } | ||
| 640 | |||
| 641 | return 0; | ||
| 642 | } | ||
| 643 | |||
| 644 | static int __iwm_up(struct iwm_priv *iwm) | ||
| 645 | { | ||
| 646 | int ret; | ||
| 647 | struct iwm_notif *notif_reboot, *notif_ack = NULL; | ||
| 648 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 649 | u32 wireless_mode; | ||
| 650 | |||
| 651 | ret = iwm_bus_enable(iwm); | ||
| 652 | if (ret) { | ||
| 653 | IWM_ERR(iwm, "Couldn't enable function\n"); | ||
| 654 | return ret; | ||
| 655 | } | ||
| 656 | |||
| 657 | iwm_rx_setup_handlers(iwm); | ||
| 658 | |||
| 659 | /* Wait for initial BARKER_REBOOT from hardware */ | ||
| 660 | notif_reboot = iwm_notif_wait(iwm, IWM_BARKER_REBOOT_NOTIFICATION, | ||
| 661 | IWM_SRC_UDMA, 2 * HZ); | ||
| 662 | if (!notif_reboot) { | ||
| 663 | IWM_ERR(iwm, "Wait for REBOOT_BARKER timeout\n"); | ||
| 664 | goto err_disable; | ||
| 665 | } | ||
| 666 | |||
| 667 | /* We send the barker back */ | ||
| 668 | ret = iwm_bus_send_chunk(iwm, notif_reboot->buf, 16); | ||
| 669 | if (ret) { | ||
| 670 | IWM_ERR(iwm, "REBOOT barker response failed\n"); | ||
| 671 | kfree(notif_reboot); | ||
| 672 | goto err_disable; | ||
| 673 | } | ||
| 674 | |||
| 675 | kfree(notif_reboot->buf); | ||
| 676 | kfree(notif_reboot); | ||
| 677 | |||
| 678 | /* Wait for ACK_BARKER from hardware */ | ||
| 679 | notif_ack = iwm_notif_wait(iwm, IWM_ACK_BARKER_NOTIFICATION, | ||
| 680 | IWM_SRC_UDMA, 2 * HZ); | ||
| 681 | if (!notif_ack) { | ||
| 682 | IWM_ERR(iwm, "Wait for ACK_BARKER timeout\n"); | ||
| 683 | goto err_disable; | ||
| 684 | } | ||
| 685 | |||
| 686 | kfree(notif_ack->buf); | ||
| 687 | kfree(notif_ack); | ||
| 688 | |||
| 689 | /* We start to config static boot parameters */ | ||
| 690 | ret = iwm_config_boot_params(iwm); | ||
| 691 | if (ret) { | ||
| 692 | IWM_ERR(iwm, "Config boot parameters failed\n"); | ||
| 693 | goto err_disable; | ||
| 694 | } | ||
| 695 | |||
| 696 | ret = iwm_read_mac(iwm, iwm_to_ndev(iwm)->dev_addr); | ||
| 697 | if (ret) { | ||
| 698 | IWM_ERR(iwm, "MAC reading failed\n"); | ||
| 699 | goto err_disable; | ||
| 700 | } | ||
| 701 | memcpy(iwm_to_ndev(iwm)->perm_addr, iwm_to_ndev(iwm)->dev_addr, | ||
| 702 | ETH_ALEN); | ||
| 703 | |||
| 704 | /* We can load the FWs */ | ||
| 705 | ret = iwm_load_fw(iwm); | ||
| 706 | if (ret) { | ||
| 707 | IWM_ERR(iwm, "FW loading failed\n"); | ||
| 708 | goto err_disable; | ||
| 709 | } | ||
| 710 | |||
| 711 | ret = iwm_eeprom_fat_channels(iwm); | ||
| 712 | if (ret) { | ||
| 713 | IWM_ERR(iwm, "Couldnt read HT channels EEPROM entries\n"); | ||
| 714 | goto err_fw; | ||
| 715 | } | ||
| 716 | |||
| 717 | /* | ||
| 718 | * Read our SKU capabilities. | ||
| 719 | * If it's valid, we AND the configured wireless mode with the | ||
| 720 | * device EEPROM value as the current profile wireless mode. | ||
| 721 | */ | ||
| 722 | wireless_mode = iwm_eeprom_wireless_mode(iwm); | ||
| 723 | if (wireless_mode) { | ||
| 724 | iwm->conf.wireless_mode &= wireless_mode; | ||
| 725 | if (iwm->umac_profile) | ||
| 726 | iwm->umac_profile->wireless_mode = | ||
| 727 | iwm->conf.wireless_mode; | ||
| 728 | } else | ||
| 729 | IWM_ERR(iwm, "Wrong SKU capabilities: 0x%x\n", | ||
| 730 | *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP))); | ||
| 731 | |||
| 732 | snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "L%s_U%s", | ||
| 733 | iwm->lmac_version, iwm->umac_version); | ||
| 734 | |||
| 735 | /* We configure the UMAC and enable the wifi module */ | ||
| 736 | ret = iwm_send_umac_config(iwm, | ||
| 737 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_CORE_EN) | | ||
| 738 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_LINK_EN) | | ||
| 739 | cpu_to_le32(UMAC_RST_CTRL_FLG_WIFI_MLME_EN)); | ||
| 740 | if (ret) { | ||
| 741 | IWM_ERR(iwm, "UMAC config failed\n"); | ||
| 742 | goto err_fw; | ||
| 743 | } | ||
| 744 | |||
| 745 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS, | ||
| 746 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
| 747 | if (ret) { | ||
| 748 | IWM_ERR(iwm, "Didn't get a wifi core status notification\n"); | ||
| 749 | goto err_fw; | ||
| 750 | } | ||
| 751 | |||
| 752 | if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN | | ||
| 753 | UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) { | ||
| 754 | IWM_DBG_BOOT(iwm, DBG, "Not all cores enabled:0x%x\n", | ||
| 755 | iwm->core_enabled); | ||
| 756 | ret = iwm_notif_handle(iwm, UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS, | ||
| 757 | IWM_SRC_UMAC, WAIT_NOTIF_TIMEOUT); | ||
| 758 | if (ret) { | ||
| 759 | IWM_ERR(iwm, "Didn't get a core status notification\n"); | ||
| 760 | goto err_fw; | ||
| 761 | } | ||
| 762 | |||
| 763 | if (iwm->core_enabled != (UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN | | ||
| 764 | UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN)) { | ||
| 765 | IWM_ERR(iwm, "Not all cores enabled: 0x%x\n", | ||
| 766 | iwm->core_enabled); | ||
| 767 | goto err_fw; | ||
| 768 | } else { | ||
| 769 | IWM_INFO(iwm, "All cores enabled\n"); | ||
| 770 | } | ||
| 771 | } | ||
| 772 | |||
| 773 | ret = iwm_channels_init(iwm); | ||
| 774 | if (ret < 0) { | ||
| 775 | IWM_ERR(iwm, "Couldn't init channels\n"); | ||
| 776 | goto err_fw; | ||
| 777 | } | ||
| 778 | |||
| 779 | /* Set the READY bit to indicate interface is brought up successfully */ | ||
| 780 | set_bit(IWM_STATUS_READY, &iwm->status); | ||
| 781 | |||
| 782 | return 0; | ||
| 783 | |||
| 784 | err_fw: | ||
| 785 | iwm_eeprom_exit(iwm); | ||
| 786 | |||
| 787 | err_disable: | ||
| 788 | ret = iwm_bus_disable(iwm); | ||
| 789 | if (ret < 0) | ||
| 790 | IWM_ERR(iwm, "Couldn't disable function\n"); | ||
| 791 | |||
| 792 | return -EIO; | ||
| 793 | } | ||
| 794 | |||
| 795 | int iwm_up(struct iwm_priv *iwm) | ||
| 796 | { | ||
| 797 | int ret; | ||
| 798 | |||
| 799 | mutex_lock(&iwm->mutex); | ||
| 800 | ret = __iwm_up(iwm); | ||
| 801 | mutex_unlock(&iwm->mutex); | ||
| 802 | |||
| 803 | return ret; | ||
| 804 | } | ||
| 805 | |||
| 806 | static int __iwm_down(struct iwm_priv *iwm) | ||
| 807 | { | ||
| 808 | int ret; | ||
| 809 | |||
| 810 | /* The interface is already down */ | ||
| 811 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | ||
| 812 | return 0; | ||
| 813 | |||
| 814 | if (iwm->scan_request) { | ||
| 815 | cfg80211_scan_done(iwm->scan_request, true); | ||
| 816 | iwm->scan_request = NULL; | ||
| 817 | } | ||
| 818 | |||
| 819 | clear_bit(IWM_STATUS_READY, &iwm->status); | ||
| 820 | |||
| 821 | iwm_eeprom_exit(iwm); | ||
| 822 | iwm_bss_list_clean(iwm); | ||
| 823 | iwm_init_default_profile(iwm, iwm->umac_profile); | ||
| 824 | iwm->umac_profile_active = false; | ||
| 825 | iwm->default_key = -1; | ||
| 826 | iwm->core_enabled = 0; | ||
| 827 | |||
| 828 | ret = iwm_bus_disable(iwm); | ||
| 829 | if (ret < 0) { | ||
| 830 | IWM_ERR(iwm, "Couldn't disable function\n"); | ||
| 831 | return ret; | ||
| 832 | } | ||
| 833 | |||
| 834 | return 0; | ||
| 835 | } | ||
| 836 | |||
| 837 | int iwm_down(struct iwm_priv *iwm) | ||
| 838 | { | ||
| 839 | int ret; | ||
| 840 | |||
| 841 | mutex_lock(&iwm->mutex); | ||
| 842 | ret = __iwm_down(iwm); | ||
| 843 | mutex_unlock(&iwm->mutex); | ||
| 844 | |||
| 845 | return ret; | ||
| 846 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c new file mode 100644 index 00000000000..5091d77e02c --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c | |||
| @@ -0,0 +1,191 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation <ilw@linux.intel.com> | ||
| 5 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 6 | * Zhu Yi <yi.zhu@intel.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or | ||
| 9 | * modify it under the terms of the GNU General Public License version | ||
| 10 | * 2 as published by the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License | ||
| 18 | * along with this program; if not, write to the Free Software | ||
| 19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
| 20 | * 02110-1301, USA. | ||
| 21 | * | ||
| 22 | */ | ||
| 23 | |||
| 24 | /* | ||
| 25 | * This is the netdev related hooks for iwm. | ||
| 26 | * | ||
| 27 | * Some interesting code paths: | ||
| 28 | * | ||
| 29 | * iwm_open() (Called at netdev interface bringup time) | ||
| 30 | * -> iwm_up() (main.c) | ||
| 31 | * -> iwm_bus_enable() | ||
| 32 | * -> if_sdio_enable() (In case of an SDIO bus) | ||
| 33 | * -> sdio_enable_func() | ||
| 34 | * -> iwm_notif_wait(BARKER_REBOOT) (wait for reboot barker) | ||
| 35 | * -> iwm_notif_wait(ACK_BARKER) (wait for ACK barker) | ||
| 36 | * -> iwm_load_fw() (fw.c) | ||
| 37 | * -> iwm_load_umac() | ||
| 38 | * -> iwm_load_lmac() (Calibration LMAC) | ||
| 39 | * -> iwm_load_lmac() (Operational LMAC) | ||
| 40 | * -> iwm_send_umac_config() | ||
| 41 | * | ||
| 42 | * iwm_stop() (Called at netdev interface bringdown time) | ||
| 43 | * -> iwm_down() | ||
| 44 | * -> iwm_bus_disable() | ||
| 45 | * -> if_sdio_disable() (In case of an SDIO bus) | ||
| 46 | * -> sdio_disable_func() | ||
| 47 | */ | ||
| 48 | #include <linux/netdevice.h> | ||
| 49 | #include <linux/slab.h> | ||
| 50 | |||
| 51 | #include "iwm.h" | ||
| 52 | #include "commands.h" | ||
| 53 | #include "cfg80211.h" | ||
| 54 | #include "debug.h" | ||
| 55 | |||
| 56 | static int iwm_open(struct net_device *ndev) | ||
| 57 | { | ||
| 58 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 59 | |||
| 60 | return iwm_up(iwm); | ||
| 61 | } | ||
| 62 | |||
| 63 | static int iwm_stop(struct net_device *ndev) | ||
| 64 | { | ||
| 65 | struct iwm_priv *iwm = ndev_to_iwm(ndev); | ||
| 66 | |||
| 67 | return iwm_down(iwm); | ||
| 68 | } | ||
| 69 | |||
| 70 | /* | ||
| 71 | * iwm AC to queue mapping | ||
| 72 | * | ||
| 73 | * AC_VO -> queue 3 | ||
| 74 | * AC_VI -> queue 2 | ||
| 75 | * AC_BE -> queue 1 | ||
| 76 | * AC_BK -> queue 0 | ||
| 77 | */ | ||
| 78 | static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; | ||
| 79 | |||
| 80 | int iwm_tid_to_queue(u16 tid) | ||
| 81 | { | ||
| 82 | if (tid > IWM_UMAC_TID_NR - 2) | ||
| 83 | return -EINVAL; | ||
| 84 | |||
| 85 | return iwm_1d_to_queue[tid]; | ||
| 86 | } | ||
| 87 | |||
| 88 | static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb) | ||
| 89 | { | ||
| 90 | skb->priority = cfg80211_classify8021d(skb); | ||
| 91 | |||
| 92 | return iwm_1d_to_queue[skb->priority]; | ||
| 93 | } | ||
| 94 | |||
| 95 | static const struct net_device_ops iwm_netdev_ops = { | ||
| 96 | .ndo_open = iwm_open, | ||
| 97 | .ndo_stop = iwm_stop, | ||
| 98 | .ndo_start_xmit = iwm_xmit_frame, | ||
| 99 | .ndo_select_queue = iwm_select_queue, | ||
| 100 | }; | ||
| 101 | |||
| 102 | void *iwm_if_alloc(int sizeof_bus, struct device *dev, | ||
| 103 | struct iwm_if_ops *if_ops) | ||
| 104 | { | ||
| 105 | struct net_device *ndev; | ||
| 106 | struct wireless_dev *wdev; | ||
| 107 | struct iwm_priv *iwm; | ||
| 108 | int ret = 0; | ||
| 109 | |||
| 110 | wdev = iwm_wdev_alloc(sizeof_bus, dev); | ||
| 111 | if (IS_ERR(wdev)) | ||
| 112 | return wdev; | ||
| 113 | |||
| 114 | iwm = wdev_to_iwm(wdev); | ||
| 115 | iwm->bus_ops = if_ops; | ||
| 116 | iwm->wdev = wdev; | ||
| 117 | |||
| 118 | ret = iwm_priv_init(iwm); | ||
| 119 | if (ret) { | ||
| 120 | dev_err(dev, "failed to init iwm_priv\n"); | ||
| 121 | goto out_wdev; | ||
| 122 | } | ||
| 123 | |||
| 124 | wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode); | ||
| 125 | |||
| 126 | ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES); | ||
| 127 | if (!ndev) { | ||
| 128 | dev_err(dev, "no memory for network device instance\n"); | ||
| 129 | ret = -ENOMEM; | ||
| 130 | goto out_priv; | ||
| 131 | } | ||
| 132 | |||
| 133 | ndev->netdev_ops = &iwm_netdev_ops; | ||
| 134 | ndev->ieee80211_ptr = wdev; | ||
| 135 | SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); | ||
| 136 | wdev->netdev = ndev; | ||
| 137 | |||
| 138 | iwm->umac_profile = kmalloc(sizeof(struct iwm_umac_profile), | ||
| 139 | GFP_KERNEL); | ||
| 140 | if (!iwm->umac_profile) { | ||
| 141 | dev_err(dev, "Couldn't alloc memory for profile\n"); | ||
| 142 | ret = -ENOMEM; | ||
| 143 | goto out_profile; | ||
| 144 | } | ||
| 145 | |||
| 146 | iwm_init_default_profile(iwm, iwm->umac_profile); | ||
| 147 | |||
| 148 | return iwm; | ||
| 149 | |||
| 150 | out_profile: | ||
| 151 | free_netdev(ndev); | ||
| 152 | |||
| 153 | out_priv: | ||
| 154 | iwm_priv_deinit(iwm); | ||
| 155 | |||
| 156 | out_wdev: | ||
| 157 | iwm_wdev_free(iwm); | ||
| 158 | return ERR_PTR(ret); | ||
| 159 | } | ||
| 160 | |||
| 161 | void iwm_if_free(struct iwm_priv *iwm) | ||
| 162 | { | ||
| 163 | if (!iwm_to_ndev(iwm)) | ||
| 164 | return; | ||
| 165 | |||
| 166 | cancel_delayed_work_sync(&iwm->ct_kill_delay); | ||
| 167 | free_netdev(iwm_to_ndev(iwm)); | ||
| 168 | iwm_priv_deinit(iwm); | ||
| 169 | kfree(iwm->umac_profile); | ||
| 170 | iwm->umac_profile = NULL; | ||
| 171 | iwm_wdev_free(iwm); | ||
| 172 | } | ||
| 173 | |||
| 174 | int iwm_if_add(struct iwm_priv *iwm) | ||
| 175 | { | ||
| 176 | struct net_device *ndev = iwm_to_ndev(iwm); | ||
| 177 | int ret; | ||
| 178 | |||
| 179 | ret = register_netdev(ndev); | ||
| 180 | if (ret < 0) { | ||
| 181 | dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret); | ||
| 182 | return ret; | ||
| 183 | } | ||
| 184 | |||
| 185 | return 0; | ||
| 186 | } | ||
| 187 | |||
| 188 | void iwm_if_remove(struct iwm_priv *iwm) | ||
| 189 | { | ||
| 190 | unregister_netdev(iwm_to_ndev(iwm)); | ||
| 191 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c new file mode 100644 index 00000000000..a414768f40f --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/rx.c | |||
| @@ -0,0 +1,1701 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #include <linux/kernel.h> | ||
| 40 | #include <linux/netdevice.h> | ||
| 41 | #include <linux/sched.h> | ||
| 42 | #include <linux/etherdevice.h> | ||
| 43 | #include <linux/wireless.h> | ||
| 44 | #include <linux/ieee80211.h> | ||
| 45 | #include <linux/if_arp.h> | ||
| 46 | #include <linux/list.h> | ||
| 47 | #include <linux/slab.h> | ||
| 48 | #include <net/iw_handler.h> | ||
| 49 | |||
| 50 | #include "iwm.h" | ||
| 51 | #include "debug.h" | ||
| 52 | #include "hal.h" | ||
| 53 | #include "umac.h" | ||
| 54 | #include "lmac.h" | ||
| 55 | #include "commands.h" | ||
| 56 | #include "rx.h" | ||
| 57 | #include "cfg80211.h" | ||
| 58 | #include "eeprom.h" | ||
| 59 | |||
| 60 | static int iwm_rx_check_udma_hdr(struct iwm_udma_in_hdr *hdr) | ||
| 61 | { | ||
| 62 | if ((le32_to_cpu(hdr->cmd) == UMAC_PAD_TERMINAL) || | ||
| 63 | (le32_to_cpu(hdr->size) == UMAC_PAD_TERMINAL)) | ||
| 64 | return -EINVAL; | ||
| 65 | |||
| 66 | return 0; | ||
| 67 | } | ||
| 68 | |||
| 69 | static inline int iwm_rx_resp_size(struct iwm_udma_in_hdr *hdr) | ||
| 70 | { | ||
| 71 | return ALIGN(le32_to_cpu(hdr->size) + sizeof(struct iwm_udma_in_hdr), | ||
| 72 | 16); | ||
| 73 | } | ||
| 74 | |||
| 75 | /* | ||
| 76 | * Notification handlers: | ||
| 77 | * | ||
| 78 | * For every possible notification we can receive from the | ||
| 79 | * target, we have a handler. | ||
| 80 | * When we get a target notification, and there is no one | ||
| 81 | * waiting for it, it's just processed through the rx code | ||
| 82 | * path: | ||
| 83 | * | ||
| 84 | * iwm_rx_handle() | ||
| 85 | * -> iwm_rx_handle_umac() | ||
| 86 | * -> iwm_rx_handle_wifi() | ||
| 87 | * -> iwm_rx_handle_resp() | ||
| 88 | * -> iwm_ntf_*() | ||
| 89 | * | ||
| 90 | * OR | ||
| 91 | * | ||
| 92 | * -> iwm_rx_handle_non_wifi() | ||
| 93 | * | ||
| 94 | * If there are processes waiting for this notification, then | ||
| 95 | * iwm_rx_handle_wifi() just wakes those processes up and they | ||
| 96 | * grab the pending notification. | ||
| 97 | */ | ||
| 98 | static int iwm_ntf_error(struct iwm_priv *iwm, u8 *buf, | ||
| 99 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 100 | { | ||
| 101 | struct iwm_umac_notif_error *error; | ||
| 102 | struct iwm_fw_error_hdr *fw_err; | ||
| 103 | |||
| 104 | error = (struct iwm_umac_notif_error *)buf; | ||
| 105 | fw_err = &error->err; | ||
| 106 | |||
| 107 | memcpy(iwm->last_fw_err, fw_err, sizeof(struct iwm_fw_error_hdr)); | ||
| 108 | |||
| 109 | IWM_ERR(iwm, "%cMAC FW ERROR:\n", | ||
| 110 | (le32_to_cpu(fw_err->category) == UMAC_SYS_ERR_CAT_LMAC) ? 'L' : 'U'); | ||
| 111 | IWM_ERR(iwm, "\tCategory: %d\n", le32_to_cpu(fw_err->category)); | ||
| 112 | IWM_ERR(iwm, "\tStatus: 0x%x\n", le32_to_cpu(fw_err->status)); | ||
| 113 | IWM_ERR(iwm, "\tPC: 0x%x\n", le32_to_cpu(fw_err->pc)); | ||
| 114 | IWM_ERR(iwm, "\tblink1: %d\n", le32_to_cpu(fw_err->blink1)); | ||
| 115 | IWM_ERR(iwm, "\tblink2: %d\n", le32_to_cpu(fw_err->blink2)); | ||
| 116 | IWM_ERR(iwm, "\tilink1: %d\n", le32_to_cpu(fw_err->ilink1)); | ||
| 117 | IWM_ERR(iwm, "\tilink2: %d\n", le32_to_cpu(fw_err->ilink2)); | ||
| 118 | IWM_ERR(iwm, "\tData1: 0x%x\n", le32_to_cpu(fw_err->data1)); | ||
| 119 | IWM_ERR(iwm, "\tData2: 0x%x\n", le32_to_cpu(fw_err->data2)); | ||
| 120 | IWM_ERR(iwm, "\tLine number: %d\n", le32_to_cpu(fw_err->line_num)); | ||
| 121 | IWM_ERR(iwm, "\tUMAC status: 0x%x\n", le32_to_cpu(fw_err->umac_status)); | ||
| 122 | IWM_ERR(iwm, "\tLMAC status: 0x%x\n", le32_to_cpu(fw_err->lmac_status)); | ||
| 123 | IWM_ERR(iwm, "\tSDIO status: 0x%x\n", le32_to_cpu(fw_err->sdio_status)); | ||
| 124 | |||
| 125 | iwm_resetting(iwm); | ||
| 126 | |||
| 127 | return 0; | ||
| 128 | } | ||
| 129 | |||
| 130 | static int iwm_ntf_umac_alive(struct iwm_priv *iwm, u8 *buf, | ||
| 131 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 132 | { | ||
| 133 | struct iwm_umac_notif_alive *alive_resp = | ||
| 134 | (struct iwm_umac_notif_alive *)(buf); | ||
| 135 | u16 status = le16_to_cpu(alive_resp->status); | ||
| 136 | |||
| 137 | if (status == UMAC_NTFY_ALIVE_STATUS_ERR) { | ||
| 138 | IWM_ERR(iwm, "Receive error UMAC_ALIVE\n"); | ||
| 139 | return -EIO; | ||
| 140 | } | ||
| 141 | |||
| 142 | iwm_tx_credit_init_pools(iwm, alive_resp); | ||
| 143 | |||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | static int iwm_ntf_init_complete(struct iwm_priv *iwm, u8 *buf, | ||
| 148 | unsigned long buf_size, | ||
| 149 | struct iwm_wifi_cmd *cmd) | ||
| 150 | { | ||
| 151 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 152 | struct iwm_umac_notif_init_complete *init_complete = | ||
| 153 | (struct iwm_umac_notif_init_complete *)(buf); | ||
| 154 | u16 status = le16_to_cpu(init_complete->status); | ||
| 155 | bool blocked = (status == UMAC_NTFY_INIT_COMPLETE_STATUS_ERR); | ||
| 156 | |||
| 157 | if (blocked) | ||
| 158 | IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is on (radio off)\n"); | ||
| 159 | else | ||
| 160 | IWM_DBG_NTF(iwm, DBG, "Hardware rf kill is off (radio on)\n"); | ||
| 161 | |||
| 162 | wiphy_rfkill_set_hw_state(wiphy, blocked); | ||
| 163 | |||
| 164 | return 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | static int iwm_ntf_tx_credit_update(struct iwm_priv *iwm, u8 *buf, | ||
| 168 | unsigned long buf_size, | ||
| 169 | struct iwm_wifi_cmd *cmd) | ||
| 170 | { | ||
| 171 | int pool_nr, total_freed_pages; | ||
| 172 | unsigned long pool_map; | ||
| 173 | int i, id; | ||
| 174 | struct iwm_umac_notif_page_dealloc *dealloc = | ||
| 175 | (struct iwm_umac_notif_page_dealloc *)buf; | ||
| 176 | |||
| 177 | pool_nr = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_CNT); | ||
| 178 | pool_map = GET_VAL32(dealloc->changes, UMAC_DEALLOC_NTFY_CHANGES_MSK); | ||
| 179 | |||
| 180 | IWM_DBG_TX(iwm, DBG, "UMAC dealloc notification: pool nr %d, " | ||
| 181 | "update map 0x%lx\n", pool_nr, pool_map); | ||
| 182 | |||
| 183 | spin_lock(&iwm->tx_credit.lock); | ||
| 184 | |||
| 185 | for (i = 0; i < pool_nr; i++) { | ||
| 186 | id = GET_VAL32(dealloc->grp_info[i], | ||
| 187 | UMAC_DEALLOC_NTFY_GROUP_NUM); | ||
| 188 | if (test_bit(id, &pool_map)) { | ||
| 189 | total_freed_pages = GET_VAL32(dealloc->grp_info[i], | ||
| 190 | UMAC_DEALLOC_NTFY_PAGE_CNT); | ||
| 191 | iwm_tx_credit_inc(iwm, id, total_freed_pages); | ||
| 192 | } | ||
| 193 | } | ||
| 194 | |||
| 195 | spin_unlock(&iwm->tx_credit.lock); | ||
| 196 | |||
| 197 | return 0; | ||
| 198 | } | ||
| 199 | |||
| 200 | static int iwm_ntf_umac_reset(struct iwm_priv *iwm, u8 *buf, | ||
| 201 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 202 | { | ||
| 203 | IWM_DBG_NTF(iwm, DBG, "UMAC RESET done\n"); | ||
| 204 | |||
| 205 | return 0; | ||
| 206 | } | ||
| 207 | |||
| 208 | static int iwm_ntf_lmac_version(struct iwm_priv *iwm, u8 *buf, | ||
| 209 | unsigned long buf_size, | ||
| 210 | struct iwm_wifi_cmd *cmd) | ||
| 211 | { | ||
| 212 | IWM_DBG_NTF(iwm, INFO, "LMAC Version: %x.%x\n", buf[9], buf[8]); | ||
| 213 | |||
| 214 | return 0; | ||
| 215 | } | ||
| 216 | |||
| 217 | static int iwm_ntf_tx(struct iwm_priv *iwm, u8 *buf, | ||
| 218 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 219 | { | ||
| 220 | struct iwm_lmac_tx_resp *tx_resp; | ||
| 221 | struct iwm_umac_wifi_in_hdr *hdr; | ||
| 222 | |||
| 223 | tx_resp = (struct iwm_lmac_tx_resp *) | ||
| 224 | (buf + sizeof(struct iwm_umac_wifi_in_hdr)); | ||
| 225 | hdr = (struct iwm_umac_wifi_in_hdr *)buf; | ||
| 226 | |||
| 227 | IWM_DBG_TX(iwm, DBG, "REPLY_TX, buf size: %lu\n", buf_size); | ||
| 228 | |||
| 229 | IWM_DBG_TX(iwm, DBG, "Seqnum: %d\n", | ||
| 230 | le16_to_cpu(hdr->sw_hdr.cmd.seq_num)); | ||
| 231 | IWM_DBG_TX(iwm, DBG, "\tFrame cnt: %d\n", tx_resp->frame_cnt); | ||
| 232 | IWM_DBG_TX(iwm, DBG, "\tRetry cnt: %d\n", | ||
| 233 | le16_to_cpu(tx_resp->retry_cnt)); | ||
| 234 | IWM_DBG_TX(iwm, DBG, "\tSeq ctl: %d\n", le16_to_cpu(tx_resp->seq_ctl)); | ||
| 235 | IWM_DBG_TX(iwm, DBG, "\tByte cnt: %d\n", | ||
| 236 | le16_to_cpu(tx_resp->byte_cnt)); | ||
| 237 | IWM_DBG_TX(iwm, DBG, "\tStatus: 0x%x\n", le32_to_cpu(tx_resp->status)); | ||
| 238 | |||
| 239 | return 0; | ||
| 240 | } | ||
| 241 | |||
| 242 | |||
| 243 | static int iwm_ntf_calib_res(struct iwm_priv *iwm, u8 *buf, | ||
| 244 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 245 | { | ||
| 246 | u8 opcode; | ||
| 247 | u8 *calib_buf; | ||
| 248 | struct iwm_lmac_calib_hdr *hdr = (struct iwm_lmac_calib_hdr *) | ||
| 249 | (buf + sizeof(struct iwm_umac_wifi_in_hdr)); | ||
| 250 | |||
| 251 | opcode = hdr->opcode; | ||
| 252 | |||
| 253 | BUG_ON(opcode >= CALIBRATION_CMD_NUM || | ||
| 254 | opcode < PHY_CALIBRATE_OPCODES_NUM); | ||
| 255 | |||
| 256 | IWM_DBG_NTF(iwm, DBG, "Store calibration result for opcode: %d\n", | ||
| 257 | opcode); | ||
| 258 | |||
| 259 | buf_size -= sizeof(struct iwm_umac_wifi_in_hdr); | ||
| 260 | calib_buf = iwm->calib_res[opcode].buf; | ||
| 261 | |||
| 262 | if (!calib_buf || (iwm->calib_res[opcode].size < buf_size)) { | ||
| 263 | kfree(calib_buf); | ||
| 264 | calib_buf = kzalloc(buf_size, GFP_KERNEL); | ||
| 265 | if (!calib_buf) { | ||
| 266 | IWM_ERR(iwm, "Memory allocation failed: calib_res\n"); | ||
| 267 | return -ENOMEM; | ||
| 268 | } | ||
| 269 | iwm->calib_res[opcode].buf = calib_buf; | ||
| 270 | iwm->calib_res[opcode].size = buf_size; | ||
| 271 | } | ||
| 272 | |||
| 273 | memcpy(calib_buf, hdr, buf_size); | ||
| 274 | set_bit(opcode - PHY_CALIBRATE_OPCODES_NUM, &iwm->calib_done_map); | ||
| 275 | |||
| 276 | return 0; | ||
| 277 | } | ||
| 278 | |||
| 279 | static int iwm_ntf_calib_complete(struct iwm_priv *iwm, u8 *buf, | ||
| 280 | unsigned long buf_size, | ||
| 281 | struct iwm_wifi_cmd *cmd) | ||
| 282 | { | ||
| 283 | IWM_DBG_NTF(iwm, DBG, "Calibration completed\n"); | ||
| 284 | |||
| 285 | return 0; | ||
| 286 | } | ||
| 287 | |||
| 288 | static int iwm_ntf_calib_cfg(struct iwm_priv *iwm, u8 *buf, | ||
| 289 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 290 | { | ||
| 291 | struct iwm_lmac_cal_cfg_resp *cal_resp; | ||
| 292 | |||
| 293 | cal_resp = (struct iwm_lmac_cal_cfg_resp *) | ||
| 294 | (buf + sizeof(struct iwm_umac_wifi_in_hdr)); | ||
| 295 | |||
| 296 | IWM_DBG_NTF(iwm, DBG, "Calibration CFG command status: %d\n", | ||
| 297 | le32_to_cpu(cal_resp->status)); | ||
| 298 | |||
| 299 | return 0; | ||
| 300 | } | ||
| 301 | |||
| 302 | static int iwm_ntf_wifi_status(struct iwm_priv *iwm, u8 *buf, | ||
| 303 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 304 | { | ||
| 305 | struct iwm_umac_notif_wifi_status *status = | ||
| 306 | (struct iwm_umac_notif_wifi_status *)buf; | ||
| 307 | |||
| 308 | iwm->core_enabled |= le16_to_cpu(status->status); | ||
| 309 | |||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | static struct iwm_rx_ticket_node * | ||
| 314 | iwm_rx_ticket_node_alloc(struct iwm_priv *iwm, struct iwm_rx_ticket *ticket) | ||
| 315 | { | ||
| 316 | struct iwm_rx_ticket_node *ticket_node; | ||
| 317 | |||
| 318 | ticket_node = kzalloc(sizeof(struct iwm_rx_ticket_node), GFP_KERNEL); | ||
| 319 | if (!ticket_node) { | ||
| 320 | IWM_ERR(iwm, "Couldn't allocate ticket node\n"); | ||
| 321 | return ERR_PTR(-ENOMEM); | ||
| 322 | } | ||
| 323 | |||
| 324 | ticket_node->ticket = kmemdup(ticket, sizeof(struct iwm_rx_ticket), | ||
| 325 | GFP_KERNEL); | ||
| 326 | if (!ticket_node->ticket) { | ||
| 327 | IWM_ERR(iwm, "Couldn't allocate RX ticket\n"); | ||
| 328 | kfree(ticket_node); | ||
| 329 | return ERR_PTR(-ENOMEM); | ||
| 330 | } | ||
| 331 | |||
| 332 | INIT_LIST_HEAD(&ticket_node->node); | ||
| 333 | |||
| 334 | return ticket_node; | ||
| 335 | } | ||
| 336 | |||
| 337 | static void iwm_rx_ticket_node_free(struct iwm_rx_ticket_node *ticket_node) | ||
| 338 | { | ||
| 339 | kfree(ticket_node->ticket); | ||
| 340 | kfree(ticket_node); | ||
| 341 | } | ||
| 342 | |||
| 343 | static struct iwm_rx_packet *iwm_rx_packet_get(struct iwm_priv *iwm, u16 id) | ||
| 344 | { | ||
| 345 | u8 id_hash = IWM_RX_ID_GET_HASH(id); | ||
| 346 | struct iwm_rx_packet *packet; | ||
| 347 | |||
| 348 | spin_lock(&iwm->packet_lock[id_hash]); | ||
| 349 | list_for_each_entry(packet, &iwm->rx_packets[id_hash], node) | ||
| 350 | if (packet->id == id) { | ||
| 351 | list_del(&packet->node); | ||
| 352 | spin_unlock(&iwm->packet_lock[id_hash]); | ||
| 353 | return packet; | ||
| 354 | } | ||
| 355 | |||
| 356 | spin_unlock(&iwm->packet_lock[id_hash]); | ||
| 357 | return NULL; | ||
| 358 | } | ||
| 359 | |||
| 360 | static struct iwm_rx_packet *iwm_rx_packet_alloc(struct iwm_priv *iwm, u8 *buf, | ||
| 361 | u32 size, u16 id) | ||
| 362 | { | ||
| 363 | struct iwm_rx_packet *packet; | ||
| 364 | |||
| 365 | packet = kzalloc(sizeof(struct iwm_rx_packet), GFP_KERNEL); | ||
| 366 | if (!packet) { | ||
| 367 | IWM_ERR(iwm, "Couldn't allocate packet\n"); | ||
| 368 | return ERR_PTR(-ENOMEM); | ||
| 369 | } | ||
| 370 | |||
| 371 | packet->skb = dev_alloc_skb(size); | ||
| 372 | if (!packet->skb) { | ||
| 373 | IWM_ERR(iwm, "Couldn't allocate packet SKB\n"); | ||
| 374 | kfree(packet); | ||
| 375 | return ERR_PTR(-ENOMEM); | ||
| 376 | } | ||
| 377 | |||
| 378 | packet->pkt_size = size; | ||
| 379 | |||
| 380 | skb_put(packet->skb, size); | ||
| 381 | memcpy(packet->skb->data, buf, size); | ||
| 382 | INIT_LIST_HEAD(&packet->node); | ||
| 383 | packet->id = id; | ||
| 384 | |||
| 385 | return packet; | ||
| 386 | } | ||
| 387 | |||
| 388 | void iwm_rx_free(struct iwm_priv *iwm) | ||
| 389 | { | ||
| 390 | struct iwm_rx_ticket_node *ticket, *nt; | ||
| 391 | struct iwm_rx_packet *packet, *np; | ||
| 392 | int i; | ||
| 393 | |||
| 394 | spin_lock(&iwm->ticket_lock); | ||
| 395 | list_for_each_entry_safe(ticket, nt, &iwm->rx_tickets, node) { | ||
| 396 | list_del(&ticket->node); | ||
| 397 | iwm_rx_ticket_node_free(ticket); | ||
| 398 | } | ||
| 399 | spin_unlock(&iwm->ticket_lock); | ||
| 400 | |||
| 401 | for (i = 0; i < IWM_RX_ID_HASH; i++) { | ||
| 402 | spin_lock(&iwm->packet_lock[i]); | ||
| 403 | list_for_each_entry_safe(packet, np, &iwm->rx_packets[i], | ||
| 404 | node) { | ||
| 405 | list_del(&packet->node); | ||
| 406 | kfree_skb(packet->skb); | ||
| 407 | kfree(packet); | ||
| 408 | } | ||
| 409 | spin_unlock(&iwm->packet_lock[i]); | ||
| 410 | } | ||
| 411 | } | ||
| 412 | |||
| 413 | static int iwm_ntf_rx_ticket(struct iwm_priv *iwm, u8 *buf, | ||
| 414 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 415 | { | ||
| 416 | struct iwm_umac_notif_rx_ticket *ntf_rx_ticket = | ||
| 417 | (struct iwm_umac_notif_rx_ticket *)buf; | ||
| 418 | struct iwm_rx_ticket *ticket = | ||
| 419 | (struct iwm_rx_ticket *)ntf_rx_ticket->tickets; | ||
| 420 | int i, schedule_rx = 0; | ||
| 421 | |||
| 422 | for (i = 0; i < ntf_rx_ticket->num_tickets; i++) { | ||
| 423 | struct iwm_rx_ticket_node *ticket_node; | ||
| 424 | |||
| 425 | switch (le16_to_cpu(ticket->action)) { | ||
| 426 | case IWM_RX_TICKET_RELEASE: | ||
| 427 | case IWM_RX_TICKET_DROP: | ||
| 428 | /* We can push the packet to the stack */ | ||
| 429 | ticket_node = iwm_rx_ticket_node_alloc(iwm, ticket); | ||
| 430 | if (IS_ERR(ticket_node)) | ||
| 431 | return PTR_ERR(ticket_node); | ||
| 432 | |||
| 433 | IWM_DBG_RX(iwm, DBG, "TICKET %s(%d)\n", | ||
| 434 | __le16_to_cpu(ticket->action) == | ||
| 435 | IWM_RX_TICKET_RELEASE ? | ||
| 436 | "RELEASE" : "DROP", | ||
| 437 | ticket->id); | ||
| 438 | spin_lock(&iwm->ticket_lock); | ||
| 439 | list_add_tail(&ticket_node->node, &iwm->rx_tickets); | ||
| 440 | spin_unlock(&iwm->ticket_lock); | ||
| 441 | |||
| 442 | /* | ||
| 443 | * We received an Rx ticket, most likely there's | ||
| 444 | * a packet pending for it, it's not worth going | ||
| 445 | * through the packet hash list to double check. | ||
| 446 | * Let's just fire the rx worker.. | ||
| 447 | */ | ||
| 448 | schedule_rx = 1; | ||
| 449 | |||
| 450 | break; | ||
| 451 | |||
| 452 | default: | ||
| 453 | IWM_ERR(iwm, "Invalid RX ticket action: 0x%x\n", | ||
| 454 | ticket->action); | ||
| 455 | } | ||
| 456 | |||
| 457 | ticket++; | ||
| 458 | } | ||
| 459 | |||
| 460 | if (schedule_rx) | ||
| 461 | queue_work(iwm->rx_wq, &iwm->rx_worker); | ||
| 462 | |||
| 463 | return 0; | ||
| 464 | } | ||
| 465 | |||
| 466 | static int iwm_ntf_rx_packet(struct iwm_priv *iwm, u8 *buf, | ||
| 467 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 468 | { | ||
| 469 | struct iwm_umac_wifi_in_hdr *wifi_hdr; | ||
| 470 | struct iwm_rx_packet *packet; | ||
| 471 | u16 id, buf_offset; | ||
| 472 | u32 packet_size; | ||
| 473 | u8 id_hash; | ||
| 474 | |||
| 475 | IWM_DBG_RX(iwm, DBG, "\n"); | ||
| 476 | |||
| 477 | wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf; | ||
| 478 | id = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num); | ||
| 479 | buf_offset = sizeof(struct iwm_umac_wifi_in_hdr); | ||
| 480 | packet_size = buf_size - sizeof(struct iwm_umac_wifi_in_hdr); | ||
| 481 | |||
| 482 | IWM_DBG_RX(iwm, DBG, "CMD:0x%x, seqnum: %d, packet size: %d\n", | ||
| 483 | wifi_hdr->sw_hdr.cmd.cmd, id, packet_size); | ||
| 484 | IWM_DBG_RX(iwm, DBG, "Packet id: %d\n", id); | ||
| 485 | IWM_HEXDUMP(iwm, DBG, RX, "PACKET: ", buf + buf_offset, packet_size); | ||
| 486 | |||
| 487 | packet = iwm_rx_packet_alloc(iwm, buf + buf_offset, packet_size, id); | ||
| 488 | if (IS_ERR(packet)) | ||
| 489 | return PTR_ERR(packet); | ||
| 490 | |||
| 491 | id_hash = IWM_RX_ID_GET_HASH(id); | ||
| 492 | spin_lock(&iwm->packet_lock[id_hash]); | ||
| 493 | list_add_tail(&packet->node, &iwm->rx_packets[id_hash]); | ||
| 494 | spin_unlock(&iwm->packet_lock[id_hash]); | ||
| 495 | |||
| 496 | /* We might (unlikely) have received the packet _after_ the ticket */ | ||
| 497 | queue_work(iwm->rx_wq, &iwm->rx_worker); | ||
| 498 | |||
| 499 | return 0; | ||
| 500 | } | ||
| 501 | |||
| 502 | /* MLME handlers */ | ||
| 503 | static int iwm_mlme_assoc_start(struct iwm_priv *iwm, u8 *buf, | ||
| 504 | unsigned long buf_size, | ||
| 505 | struct iwm_wifi_cmd *cmd) | ||
| 506 | { | ||
| 507 | struct iwm_umac_notif_assoc_start *start; | ||
| 508 | |||
| 509 | start = (struct iwm_umac_notif_assoc_start *)buf; | ||
| 510 | |||
| 511 | IWM_DBG_MLME(iwm, INFO, "Association with %pM Started, reason: %d\n", | ||
| 512 | start->bssid, le32_to_cpu(start->roam_reason)); | ||
| 513 | |||
| 514 | wake_up_interruptible(&iwm->mlme_queue); | ||
| 515 | |||
| 516 | return 0; | ||
| 517 | } | ||
| 518 | |||
| 519 | static u8 iwm_is_open_wep_profile(struct iwm_priv *iwm) | ||
| 520 | { | ||
| 521 | if ((iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_40 || | ||
| 522 | iwm->umac_profile->sec.ucast_cipher == UMAC_CIPHER_TYPE_WEP_104) && | ||
| 523 | (iwm->umac_profile->sec.ucast_cipher == | ||
| 524 | iwm->umac_profile->sec.mcast_cipher) && | ||
| 525 | (iwm->umac_profile->sec.auth_type == UMAC_AUTH_TYPE_OPEN)) | ||
| 526 | return 1; | ||
| 527 | |||
| 528 | return 0; | ||
| 529 | } | ||
| 530 | |||
| 531 | static int iwm_mlme_assoc_complete(struct iwm_priv *iwm, u8 *buf, | ||
| 532 | unsigned long buf_size, | ||
| 533 | struct iwm_wifi_cmd *cmd) | ||
| 534 | { | ||
| 535 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 536 | struct ieee80211_channel *chan; | ||
| 537 | struct iwm_umac_notif_assoc_complete *complete = | ||
| 538 | (struct iwm_umac_notif_assoc_complete *)buf; | ||
| 539 | |||
| 540 | IWM_DBG_MLME(iwm, INFO, "Association with %pM completed, status: %d\n", | ||
| 541 | complete->bssid, complete->status); | ||
| 542 | |||
| 543 | switch (le32_to_cpu(complete->status)) { | ||
| 544 | case UMAC_ASSOC_COMPLETE_SUCCESS: | ||
| 545 | chan = ieee80211_get_channel(wiphy, | ||
| 546 | ieee80211_channel_to_frequency(complete->channel, | ||
| 547 | complete->band == UMAC_BAND_2GHZ ? | ||
| 548 | IEEE80211_BAND_2GHZ : | ||
| 549 | IEEE80211_BAND_5GHZ)); | ||
| 550 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) { | ||
| 551 | /* Associated to a unallowed channel, disassociate. */ | ||
| 552 | __iwm_invalidate_mlme_profile(iwm); | ||
| 553 | IWM_WARN(iwm, "Couldn't associate with %pM due to " | ||
| 554 | "channel %d is disabled. Check your local " | ||
| 555 | "regulatory setting.\n", | ||
| 556 | complete->bssid, complete->channel); | ||
| 557 | goto failure; | ||
| 558 | } | ||
| 559 | |||
| 560 | set_bit(IWM_STATUS_ASSOCIATED, &iwm->status); | ||
| 561 | memcpy(iwm->bssid, complete->bssid, ETH_ALEN); | ||
| 562 | iwm->channel = complete->channel; | ||
| 563 | |||
| 564 | /* Internal roaming state, avoid notifying SME. */ | ||
| 565 | if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status) | ||
| 566 | && iwm->conf.mode == UMAC_MODE_BSS) { | ||
| 567 | cancel_delayed_work(&iwm->disconnect); | ||
| 568 | cfg80211_roamed(iwm_to_ndev(iwm), NULL, | ||
| 569 | complete->bssid, | ||
| 570 | iwm->req_ie, iwm->req_ie_len, | ||
| 571 | iwm->resp_ie, iwm->resp_ie_len, | ||
| 572 | GFP_KERNEL); | ||
| 573 | break; | ||
| 574 | } | ||
| 575 | |||
| 576 | iwm_link_on(iwm); | ||
| 577 | |||
| 578 | if (iwm->conf.mode == UMAC_MODE_IBSS) | ||
| 579 | goto ibss; | ||
| 580 | |||
| 581 | if (!test_bit(IWM_STATUS_RESETTING, &iwm->status)) | ||
| 582 | cfg80211_connect_result(iwm_to_ndev(iwm), | ||
| 583 | complete->bssid, | ||
| 584 | iwm->req_ie, iwm->req_ie_len, | ||
| 585 | iwm->resp_ie, iwm->resp_ie_len, | ||
| 586 | WLAN_STATUS_SUCCESS, | ||
| 587 | GFP_KERNEL); | ||
| 588 | else | ||
| 589 | cfg80211_roamed(iwm_to_ndev(iwm), NULL, | ||
| 590 | complete->bssid, | ||
| 591 | iwm->req_ie, iwm->req_ie_len, | ||
| 592 | iwm->resp_ie, iwm->resp_ie_len, | ||
| 593 | GFP_KERNEL); | ||
| 594 | break; | ||
| 595 | case UMAC_ASSOC_COMPLETE_FAILURE: | ||
| 596 | failure: | ||
| 597 | clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status); | ||
| 598 | memset(iwm->bssid, 0, ETH_ALEN); | ||
| 599 | iwm->channel = 0; | ||
| 600 | |||
| 601 | /* Internal roaming state, avoid notifying SME. */ | ||
| 602 | if (!test_and_clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status) | ||
| 603 | && iwm->conf.mode == UMAC_MODE_BSS) { | ||
| 604 | cancel_delayed_work(&iwm->disconnect); | ||
| 605 | break; | ||
| 606 | } | ||
| 607 | |||
| 608 | iwm_link_off(iwm); | ||
| 609 | |||
| 610 | if (iwm->conf.mode == UMAC_MODE_IBSS) | ||
| 611 | goto ibss; | ||
| 612 | |||
| 613 | if (!test_bit(IWM_STATUS_RESETTING, &iwm->status)) | ||
| 614 | if (!iwm_is_open_wep_profile(iwm)) { | ||
| 615 | cfg80211_connect_result(iwm_to_ndev(iwm), | ||
| 616 | complete->bssid, | ||
| 617 | NULL, 0, NULL, 0, | ||
| 618 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
| 619 | GFP_KERNEL); | ||
| 620 | } else { | ||
| 621 | /* Let's try shared WEP auth */ | ||
| 622 | IWM_ERR(iwm, "Trying WEP shared auth\n"); | ||
| 623 | schedule_work(&iwm->auth_retry_worker); | ||
| 624 | } | ||
| 625 | else | ||
| 626 | cfg80211_disconnected(iwm_to_ndev(iwm), 0, NULL, 0, | ||
| 627 | GFP_KERNEL); | ||
| 628 | break; | ||
| 629 | default: | ||
| 630 | break; | ||
| 631 | } | ||
| 632 | |||
| 633 | clear_bit(IWM_STATUS_RESETTING, &iwm->status); | ||
| 634 | return 0; | ||
| 635 | |||
| 636 | ibss: | ||
| 637 | cfg80211_ibss_joined(iwm_to_ndev(iwm), iwm->bssid, GFP_KERNEL); | ||
| 638 | clear_bit(IWM_STATUS_RESETTING, &iwm->status); | ||
| 639 | return 0; | ||
| 640 | } | ||
| 641 | |||
| 642 | static int iwm_mlme_profile_invalidate(struct iwm_priv *iwm, u8 *buf, | ||
| 643 | unsigned long buf_size, | ||
| 644 | struct iwm_wifi_cmd *cmd) | ||
| 645 | { | ||
| 646 | struct iwm_umac_notif_profile_invalidate *invalid; | ||
| 647 | u32 reason; | ||
| 648 | |||
| 649 | invalid = (struct iwm_umac_notif_profile_invalidate *)buf; | ||
| 650 | reason = le32_to_cpu(invalid->reason); | ||
| 651 | |||
| 652 | IWM_DBG_MLME(iwm, INFO, "Profile Invalidated. Reason: %d\n", reason); | ||
| 653 | |||
| 654 | if (reason != UMAC_PROFILE_INVALID_REQUEST && | ||
| 655 | test_bit(IWM_STATUS_SME_CONNECTING, &iwm->status)) | ||
| 656 | cfg80211_connect_result(iwm_to_ndev(iwm), NULL, NULL, 0, NULL, | ||
| 657 | 0, WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
| 658 | GFP_KERNEL); | ||
| 659 | |||
| 660 | clear_bit(IWM_STATUS_SME_CONNECTING, &iwm->status); | ||
| 661 | clear_bit(IWM_STATUS_ASSOCIATED, &iwm->status); | ||
| 662 | |||
| 663 | iwm->umac_profile_active = 0; | ||
| 664 | memset(iwm->bssid, 0, ETH_ALEN); | ||
| 665 | iwm->channel = 0; | ||
| 666 | |||
| 667 | iwm_link_off(iwm); | ||
| 668 | |||
| 669 | wake_up_interruptible(&iwm->mlme_queue); | ||
| 670 | |||
| 671 | return 0; | ||
| 672 | } | ||
| 673 | |||
| 674 | #define IWM_DISCONNECT_INTERVAL (5 * HZ) | ||
| 675 | |||
| 676 | static int iwm_mlme_connection_terminated(struct iwm_priv *iwm, u8 *buf, | ||
| 677 | unsigned long buf_size, | ||
| 678 | struct iwm_wifi_cmd *cmd) | ||
| 679 | { | ||
| 680 | IWM_DBG_MLME(iwm, DBG, "Connection terminated\n"); | ||
| 681 | |||
| 682 | schedule_delayed_work(&iwm->disconnect, IWM_DISCONNECT_INTERVAL); | ||
| 683 | |||
| 684 | return 0; | ||
| 685 | } | ||
| 686 | |||
| 687 | static int iwm_mlme_scan_complete(struct iwm_priv *iwm, u8 *buf, | ||
| 688 | unsigned long buf_size, | ||
| 689 | struct iwm_wifi_cmd *cmd) | ||
| 690 | { | ||
| 691 | int ret; | ||
| 692 | struct iwm_umac_notif_scan_complete *scan_complete = | ||
| 693 | (struct iwm_umac_notif_scan_complete *)buf; | ||
| 694 | u32 result = le32_to_cpu(scan_complete->result); | ||
| 695 | |||
| 696 | IWM_DBG_MLME(iwm, INFO, "type:0x%x result:0x%x seq:%d\n", | ||
| 697 | le32_to_cpu(scan_complete->type), | ||
| 698 | le32_to_cpu(scan_complete->result), | ||
| 699 | scan_complete->seq_num); | ||
| 700 | |||
| 701 | if (!test_and_clear_bit(IWM_STATUS_SCANNING, &iwm->status)) { | ||
| 702 | IWM_ERR(iwm, "Scan complete while device not scanning\n"); | ||
| 703 | return -EIO; | ||
| 704 | } | ||
| 705 | if (!iwm->scan_request) | ||
| 706 | return 0; | ||
| 707 | |||
| 708 | ret = iwm_cfg80211_inform_bss(iwm); | ||
| 709 | |||
| 710 | cfg80211_scan_done(iwm->scan_request, | ||
| 711 | (result & UMAC_SCAN_RESULT_ABORTED) ? 1 : !!ret); | ||
| 712 | iwm->scan_request = NULL; | ||
| 713 | |||
| 714 | return ret; | ||
| 715 | } | ||
| 716 | |||
| 717 | static int iwm_mlme_update_sta_table(struct iwm_priv *iwm, u8 *buf, | ||
| 718 | unsigned long buf_size, | ||
| 719 | struct iwm_wifi_cmd *cmd) | ||
| 720 | { | ||
| 721 | struct iwm_umac_notif_sta_info *umac_sta = | ||
| 722 | (struct iwm_umac_notif_sta_info *)buf; | ||
| 723 | struct iwm_sta_info *sta; | ||
| 724 | int i; | ||
| 725 | |||
| 726 | switch (le32_to_cpu(umac_sta->opcode)) { | ||
| 727 | case UMAC_OPCODE_ADD_MODIFY: | ||
| 728 | sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)]; | ||
| 729 | |||
| 730 | IWM_DBG_MLME(iwm, INFO, "%s STA: ID = %d, Color = %d, " | ||
| 731 | "addr = %pM, qos = %d\n", | ||
| 732 | sta->valid ? "Modify" : "Add", | ||
| 733 | GET_VAL8(umac_sta->sta_id, LMAC_STA_ID), | ||
| 734 | GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR), | ||
| 735 | umac_sta->mac_addr, | ||
| 736 | umac_sta->flags & UMAC_STA_FLAG_QOS); | ||
| 737 | |||
| 738 | sta->valid = 1; | ||
| 739 | sta->qos = umac_sta->flags & UMAC_STA_FLAG_QOS; | ||
| 740 | sta->color = GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR); | ||
| 741 | memcpy(sta->addr, umac_sta->mac_addr, ETH_ALEN); | ||
| 742 | break; | ||
| 743 | case UMAC_OPCODE_REMOVE: | ||
| 744 | IWM_DBG_MLME(iwm, INFO, "Remove STA: ID = %d, Color = %d, " | ||
| 745 | "addr = %pM\n", | ||
| 746 | GET_VAL8(umac_sta->sta_id, LMAC_STA_ID), | ||
| 747 | GET_VAL8(umac_sta->sta_id, LMAC_STA_COLOR), | ||
| 748 | umac_sta->mac_addr); | ||
| 749 | |||
| 750 | sta = &iwm->sta_table[GET_VAL8(umac_sta->sta_id, LMAC_STA_ID)]; | ||
| 751 | |||
| 752 | if (!memcmp(sta->addr, umac_sta->mac_addr, ETH_ALEN)) | ||
| 753 | sta->valid = 0; | ||
| 754 | |||
| 755 | break; | ||
| 756 | case UMAC_OPCODE_CLEAR_ALL: | ||
| 757 | for (i = 0; i < IWM_STA_TABLE_NUM; i++) | ||
| 758 | iwm->sta_table[i].valid = 0; | ||
| 759 | |||
| 760 | break; | ||
| 761 | default: | ||
| 762 | break; | ||
| 763 | } | ||
| 764 | |||
| 765 | return 0; | ||
| 766 | } | ||
| 767 | |||
| 768 | static int iwm_mlme_medium_lost(struct iwm_priv *iwm, u8 *buf, | ||
| 769 | unsigned long buf_size, | ||
| 770 | struct iwm_wifi_cmd *cmd) | ||
| 771 | { | ||
| 772 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 773 | |||
| 774 | IWM_DBG_NTF(iwm, DBG, "WiFi/WiMax coexistence radio is OFF\n"); | ||
| 775 | |||
| 776 | wiphy_rfkill_set_hw_state(wiphy, true); | ||
| 777 | |||
| 778 | return 0; | ||
| 779 | } | ||
| 780 | |||
| 781 | static int iwm_mlme_update_bss_table(struct iwm_priv *iwm, u8 *buf, | ||
| 782 | unsigned long buf_size, | ||
| 783 | struct iwm_wifi_cmd *cmd) | ||
| 784 | { | ||
| 785 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 786 | struct ieee80211_mgmt *mgmt; | ||
| 787 | struct iwm_umac_notif_bss_info *umac_bss = | ||
| 788 | (struct iwm_umac_notif_bss_info *)buf; | ||
| 789 | struct ieee80211_channel *channel; | ||
| 790 | struct ieee80211_supported_band *band; | ||
| 791 | struct iwm_bss_info *bss; | ||
| 792 | s32 signal; | ||
| 793 | int freq; | ||
| 794 | u16 frame_len = le16_to_cpu(umac_bss->frame_len); | ||
| 795 | size_t bss_len = sizeof(struct iwm_umac_notif_bss_info) + frame_len; | ||
| 796 | |||
| 797 | mgmt = (struct ieee80211_mgmt *)(umac_bss->frame_buf); | ||
| 798 | |||
| 799 | IWM_DBG_MLME(iwm, DBG, "New BSS info entry: %pM\n", mgmt->bssid); | ||
| 800 | IWM_DBG_MLME(iwm, DBG, "\tType: 0x%x\n", le32_to_cpu(umac_bss->type)); | ||
| 801 | IWM_DBG_MLME(iwm, DBG, "\tTimestamp: %d\n", | ||
| 802 | le32_to_cpu(umac_bss->timestamp)); | ||
| 803 | IWM_DBG_MLME(iwm, DBG, "\tTable Index: %d\n", | ||
| 804 | le16_to_cpu(umac_bss->table_idx)); | ||
| 805 | IWM_DBG_MLME(iwm, DBG, "\tBand: %d\n", umac_bss->band); | ||
| 806 | IWM_DBG_MLME(iwm, DBG, "\tChannel: %d\n", umac_bss->channel); | ||
| 807 | IWM_DBG_MLME(iwm, DBG, "\tRSSI: %d\n", umac_bss->rssi); | ||
| 808 | IWM_DBG_MLME(iwm, DBG, "\tFrame Length: %d\n", frame_len); | ||
| 809 | |||
| 810 | list_for_each_entry(bss, &iwm->bss_list, node) | ||
| 811 | if (bss->bss->table_idx == umac_bss->table_idx) | ||
| 812 | break; | ||
| 813 | |||
| 814 | if (&bss->node != &iwm->bss_list) { | ||
| 815 | /* Remove the old BSS entry, we will add it back later. */ | ||
| 816 | list_del(&bss->node); | ||
| 817 | kfree(bss->bss); | ||
| 818 | } else { | ||
| 819 | /* New BSS entry */ | ||
| 820 | |||
| 821 | bss = kzalloc(sizeof(struct iwm_bss_info), GFP_KERNEL); | ||
| 822 | if (!bss) { | ||
| 823 | IWM_ERR(iwm, "Couldn't allocate bss_info\n"); | ||
| 824 | return -ENOMEM; | ||
| 825 | } | ||
| 826 | } | ||
| 827 | |||
| 828 | bss->bss = kzalloc(bss_len, GFP_KERNEL); | ||
| 829 | if (!bss->bss) { | ||
| 830 | kfree(bss); | ||
| 831 | IWM_ERR(iwm, "Couldn't allocate bss\n"); | ||
| 832 | return -ENOMEM; | ||
| 833 | } | ||
| 834 | |||
| 835 | INIT_LIST_HEAD(&bss->node); | ||
| 836 | memcpy(bss->bss, umac_bss, bss_len); | ||
| 837 | |||
| 838 | if (umac_bss->band == UMAC_BAND_2GHZ) | ||
| 839 | band = wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
| 840 | else if (umac_bss->band == UMAC_BAND_5GHZ) | ||
| 841 | band = wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
| 842 | else { | ||
| 843 | IWM_ERR(iwm, "Invalid band: %d\n", umac_bss->band); | ||
| 844 | goto err; | ||
| 845 | } | ||
| 846 | |||
| 847 | freq = ieee80211_channel_to_frequency(umac_bss->channel, band->band); | ||
| 848 | channel = ieee80211_get_channel(wiphy, freq); | ||
| 849 | signal = umac_bss->rssi * 100; | ||
| 850 | |||
| 851 | bss->cfg_bss = cfg80211_inform_bss_frame(wiphy, channel, | ||
| 852 | mgmt, frame_len, | ||
| 853 | signal, GFP_KERNEL); | ||
| 854 | if (!bss->cfg_bss) | ||
| 855 | goto err; | ||
| 856 | |||
| 857 | list_add_tail(&bss->node, &iwm->bss_list); | ||
| 858 | |||
| 859 | return 0; | ||
| 860 | err: | ||
| 861 | kfree(bss->bss); | ||
| 862 | kfree(bss); | ||
| 863 | |||
| 864 | return -EINVAL; | ||
| 865 | } | ||
| 866 | |||
| 867 | static int iwm_mlme_remove_bss(struct iwm_priv *iwm, u8 *buf, | ||
| 868 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 869 | { | ||
| 870 | struct iwm_umac_notif_bss_removed *bss_rm = | ||
| 871 | (struct iwm_umac_notif_bss_removed *)buf; | ||
| 872 | struct iwm_bss_info *bss, *next; | ||
| 873 | u16 table_idx; | ||
| 874 | int i; | ||
| 875 | |||
| 876 | for (i = 0; i < le32_to_cpu(bss_rm->count); i++) { | ||
| 877 | table_idx = le16_to_cpu(bss_rm->entries[i]) & | ||
| 878 | IWM_BSS_REMOVE_INDEX_MSK; | ||
| 879 | list_for_each_entry_safe(bss, next, &iwm->bss_list, node) | ||
| 880 | if (bss->bss->table_idx == cpu_to_le16(table_idx)) { | ||
| 881 | struct ieee80211_mgmt *mgmt; | ||
| 882 | |||
| 883 | mgmt = (struct ieee80211_mgmt *) | ||
| 884 | (bss->bss->frame_buf); | ||
| 885 | IWM_DBG_MLME(iwm, ERR, "BSS removed: %pM\n", | ||
| 886 | mgmt->bssid); | ||
| 887 | list_del(&bss->node); | ||
| 888 | kfree(bss->bss); | ||
| 889 | kfree(bss); | ||
| 890 | } | ||
| 891 | } | ||
| 892 | |||
| 893 | return 0; | ||
| 894 | } | ||
| 895 | |||
| 896 | static int iwm_mlme_mgt_frame(struct iwm_priv *iwm, u8 *buf, | ||
| 897 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 898 | { | ||
| 899 | struct iwm_umac_notif_mgt_frame *mgt_frame = | ||
| 900 | (struct iwm_umac_notif_mgt_frame *)buf; | ||
| 901 | struct ieee80211_mgmt *mgt = (struct ieee80211_mgmt *)mgt_frame->frame; | ||
| 902 | |||
| 903 | IWM_HEXDUMP(iwm, DBG, MLME, "MGT: ", mgt_frame->frame, | ||
| 904 | le16_to_cpu(mgt_frame->len)); | ||
| 905 | |||
| 906 | if (ieee80211_is_assoc_req(mgt->frame_control)) { | ||
| 907 | iwm->req_ie_len = le16_to_cpu(mgt_frame->len) | ||
| 908 | - offsetof(struct ieee80211_mgmt, | ||
| 909 | u.assoc_req.variable); | ||
| 910 | kfree(iwm->req_ie); | ||
| 911 | iwm->req_ie = kmemdup(mgt->u.assoc_req.variable, | ||
| 912 | iwm->req_ie_len, GFP_KERNEL); | ||
| 913 | } else if (ieee80211_is_reassoc_req(mgt->frame_control)) { | ||
| 914 | iwm->req_ie_len = le16_to_cpu(mgt_frame->len) | ||
| 915 | - offsetof(struct ieee80211_mgmt, | ||
| 916 | u.reassoc_req.variable); | ||
| 917 | kfree(iwm->req_ie); | ||
| 918 | iwm->req_ie = kmemdup(mgt->u.reassoc_req.variable, | ||
| 919 | iwm->req_ie_len, GFP_KERNEL); | ||
| 920 | } else if (ieee80211_is_assoc_resp(mgt->frame_control)) { | ||
| 921 | iwm->resp_ie_len = le16_to_cpu(mgt_frame->len) | ||
| 922 | - offsetof(struct ieee80211_mgmt, | ||
| 923 | u.assoc_resp.variable); | ||
| 924 | kfree(iwm->resp_ie); | ||
| 925 | iwm->resp_ie = kmemdup(mgt->u.assoc_resp.variable, | ||
| 926 | iwm->resp_ie_len, GFP_KERNEL); | ||
| 927 | } else if (ieee80211_is_reassoc_resp(mgt->frame_control)) { | ||
| 928 | iwm->resp_ie_len = le16_to_cpu(mgt_frame->len) | ||
| 929 | - offsetof(struct ieee80211_mgmt, | ||
| 930 | u.reassoc_resp.variable); | ||
| 931 | kfree(iwm->resp_ie); | ||
| 932 | iwm->resp_ie = kmemdup(mgt->u.reassoc_resp.variable, | ||
| 933 | iwm->resp_ie_len, GFP_KERNEL); | ||
| 934 | } else { | ||
| 935 | IWM_ERR(iwm, "Unsupported management frame: 0x%x", | ||
| 936 | le16_to_cpu(mgt->frame_control)); | ||
| 937 | return 0; | ||
| 938 | } | ||
| 939 | |||
| 940 | return 0; | ||
| 941 | } | ||
| 942 | |||
| 943 | static int iwm_ntf_mlme(struct iwm_priv *iwm, u8 *buf, | ||
| 944 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 945 | { | ||
| 946 | struct iwm_umac_notif_wifi_if *notif = | ||
| 947 | (struct iwm_umac_notif_wifi_if *)buf; | ||
| 948 | |||
| 949 | switch (notif->status) { | ||
| 950 | case WIFI_IF_NTFY_ASSOC_START: | ||
| 951 | return iwm_mlme_assoc_start(iwm, buf, buf_size, cmd); | ||
| 952 | case WIFI_IF_NTFY_ASSOC_COMPLETE: | ||
| 953 | return iwm_mlme_assoc_complete(iwm, buf, buf_size, cmd); | ||
| 954 | case WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE: | ||
| 955 | return iwm_mlme_profile_invalidate(iwm, buf, buf_size, cmd); | ||
| 956 | case WIFI_IF_NTFY_CONNECTION_TERMINATED: | ||
| 957 | return iwm_mlme_connection_terminated(iwm, buf, buf_size, cmd); | ||
| 958 | case WIFI_IF_NTFY_SCAN_COMPLETE: | ||
| 959 | return iwm_mlme_scan_complete(iwm, buf, buf_size, cmd); | ||
| 960 | case WIFI_IF_NTFY_STA_TABLE_CHANGE: | ||
| 961 | return iwm_mlme_update_sta_table(iwm, buf, buf_size, cmd); | ||
| 962 | case WIFI_IF_NTFY_EXTENDED_IE_REQUIRED: | ||
| 963 | IWM_DBG_MLME(iwm, DBG, "Extended IE required\n"); | ||
| 964 | break; | ||
| 965 | case WIFI_IF_NTFY_RADIO_PREEMPTION: | ||
| 966 | return iwm_mlme_medium_lost(iwm, buf, buf_size, cmd); | ||
| 967 | case WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED: | ||
| 968 | return iwm_mlme_update_bss_table(iwm, buf, buf_size, cmd); | ||
| 969 | case WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED: | ||
| 970 | return iwm_mlme_remove_bss(iwm, buf, buf_size, cmd); | ||
| 971 | break; | ||
| 972 | case WIFI_IF_NTFY_MGMT_FRAME: | ||
| 973 | return iwm_mlme_mgt_frame(iwm, buf, buf_size, cmd); | ||
| 974 | case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START: | ||
| 975 | case WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE: | ||
| 976 | case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START: | ||
| 977 | case WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT: | ||
| 978 | case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START: | ||
| 979 | case WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE: | ||
| 980 | case WIFI_DBG_IF_NTFY_CNCT_ATC_START: | ||
| 981 | case WIFI_DBG_IF_NTFY_COEX_NOTIFICATION: | ||
| 982 | case WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP: | ||
| 983 | case WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP: | ||
| 984 | IWM_DBG_MLME(iwm, DBG, "MLME debug notification: 0x%x\n", | ||
| 985 | notif->status); | ||
| 986 | break; | ||
| 987 | default: | ||
| 988 | IWM_ERR(iwm, "Unhandled notification: 0x%x\n", notif->status); | ||
| 989 | break; | ||
| 990 | } | ||
| 991 | |||
| 992 | return 0; | ||
| 993 | } | ||
| 994 | |||
| 995 | #define IWM_STATS_UPDATE_INTERVAL (2 * HZ) | ||
| 996 | |||
| 997 | static int iwm_ntf_statistics(struct iwm_priv *iwm, u8 *buf, | ||
| 998 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 999 | { | ||
| 1000 | struct iwm_umac_notif_stats *stats = (struct iwm_umac_notif_stats *)buf; | ||
| 1001 | struct iw_statistics *wstats = &iwm->wstats; | ||
| 1002 | u16 max_rate = 0; | ||
| 1003 | int i; | ||
| 1004 | |||
| 1005 | IWM_DBG_MLME(iwm, DBG, "Statistics notification received\n"); | ||
| 1006 | |||
| 1007 | if (test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { | ||
| 1008 | for (i = 0; i < UMAC_NTF_RATE_SAMPLE_NR; i++) { | ||
| 1009 | max_rate = max_t(u16, max_rate, | ||
| 1010 | max(le16_to_cpu(stats->tx_rate[i]), | ||
| 1011 | le16_to_cpu(stats->rx_rate[i]))); | ||
| 1012 | } | ||
| 1013 | /* UMAC passes rate info multiplies by 2 */ | ||
| 1014 | iwm->rate = max_rate >> 1; | ||
| 1015 | } | ||
| 1016 | iwm->txpower = le32_to_cpu(stats->tx_power); | ||
| 1017 | |||
| 1018 | wstats->status = 0; | ||
| 1019 | |||
| 1020 | wstats->discard.nwid = le32_to_cpu(stats->rx_drop_other_bssid); | ||
| 1021 | wstats->discard.code = le32_to_cpu(stats->rx_drop_decode); | ||
| 1022 | wstats->discard.fragment = le32_to_cpu(stats->rx_drop_reassembly); | ||
| 1023 | wstats->discard.retries = le32_to_cpu(stats->tx_drop_max_retry); | ||
| 1024 | |||
| 1025 | wstats->miss.beacon = le32_to_cpu(stats->missed_beacons); | ||
| 1026 | |||
| 1027 | /* according to cfg80211 */ | ||
| 1028 | if (stats->rssi_dbm < -110) | ||
| 1029 | wstats->qual.qual = 0; | ||
| 1030 | else if (stats->rssi_dbm > -40) | ||
| 1031 | wstats->qual.qual = 70; | ||
| 1032 | else | ||
| 1033 | wstats->qual.qual = stats->rssi_dbm + 110; | ||
| 1034 | |||
| 1035 | wstats->qual.level = stats->rssi_dbm; | ||
| 1036 | wstats->qual.noise = stats->noise_dbm; | ||
| 1037 | wstats->qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; | ||
| 1038 | |||
| 1039 | schedule_delayed_work(&iwm->stats_request, IWM_STATS_UPDATE_INTERVAL); | ||
| 1040 | |||
| 1041 | mod_timer(&iwm->watchdog, round_jiffies(jiffies + IWM_WATCHDOG_PERIOD)); | ||
| 1042 | |||
| 1043 | return 0; | ||
| 1044 | } | ||
| 1045 | |||
| 1046 | static int iwm_ntf_eeprom_proxy(struct iwm_priv *iwm, u8 *buf, | ||
| 1047 | unsigned long buf_size, | ||
| 1048 | struct iwm_wifi_cmd *cmd) | ||
| 1049 | { | ||
| 1050 | struct iwm_umac_cmd_eeprom_proxy *eeprom_proxy = | ||
| 1051 | (struct iwm_umac_cmd_eeprom_proxy *) | ||
| 1052 | (buf + sizeof(struct iwm_umac_wifi_in_hdr)); | ||
| 1053 | struct iwm_umac_cmd_eeprom_proxy_hdr *hdr = &eeprom_proxy->hdr; | ||
| 1054 | u32 hdr_offset = le32_to_cpu(hdr->offset); | ||
| 1055 | u32 hdr_len = le32_to_cpu(hdr->len); | ||
| 1056 | u32 hdr_type = le32_to_cpu(hdr->type); | ||
| 1057 | |||
| 1058 | IWM_DBG_NTF(iwm, DBG, "type: 0x%x, len: %d, offset: 0x%x\n", | ||
| 1059 | hdr_type, hdr_len, hdr_offset); | ||
| 1060 | |||
| 1061 | if ((hdr_offset + hdr_len) > IWM_EEPROM_LEN) | ||
| 1062 | return -EINVAL; | ||
| 1063 | |||
| 1064 | switch (hdr_type) { | ||
| 1065 | case IWM_UMAC_CMD_EEPROM_TYPE_READ: | ||
| 1066 | memcpy(iwm->eeprom + hdr_offset, eeprom_proxy->buf, hdr_len); | ||
| 1067 | break; | ||
| 1068 | case IWM_UMAC_CMD_EEPROM_TYPE_WRITE: | ||
| 1069 | default: | ||
| 1070 | return -ENOTSUPP; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | return 0; | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | static int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf, | ||
| 1077 | unsigned long buf_size, | ||
| 1078 | struct iwm_wifi_cmd *cmd) | ||
| 1079 | { | ||
| 1080 | struct iwm_umac_cmd_get_channel_list *ch_list = | ||
| 1081 | (struct iwm_umac_cmd_get_channel_list *) | ||
| 1082 | (buf + sizeof(struct iwm_umac_wifi_in_hdr)); | ||
| 1083 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 1084 | struct ieee80211_supported_band *band; | ||
| 1085 | int i; | ||
| 1086 | |||
| 1087 | band = wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
| 1088 | |||
| 1089 | for (i = 0; i < band->n_channels; i++) { | ||
| 1090 | unsigned long ch_mask_0 = | ||
| 1091 | le32_to_cpu(ch_list->ch[0].channels_mask); | ||
| 1092 | unsigned long ch_mask_2 = | ||
| 1093 | le32_to_cpu(ch_list->ch[2].channels_mask); | ||
| 1094 | |||
| 1095 | if (!test_bit(i, &ch_mask_0)) | ||
| 1096 | band->channels[i].flags |= IEEE80211_CHAN_DISABLED; | ||
| 1097 | |||
| 1098 | if (!test_bit(i, &ch_mask_2)) | ||
| 1099 | band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | band = wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
| 1103 | |||
| 1104 | for (i = 0; i < min(band->n_channels, 32); i++) { | ||
| 1105 | unsigned long ch_mask_1 = | ||
| 1106 | le32_to_cpu(ch_list->ch[1].channels_mask); | ||
| 1107 | unsigned long ch_mask_3 = | ||
| 1108 | le32_to_cpu(ch_list->ch[3].channels_mask); | ||
| 1109 | |||
| 1110 | if (!test_bit(i, &ch_mask_1)) | ||
| 1111 | band->channels[i].flags |= IEEE80211_CHAN_DISABLED; | ||
| 1112 | |||
| 1113 | if (!test_bit(i, &ch_mask_3)) | ||
| 1114 | band->channels[i].flags |= IEEE80211_CHAN_NO_IBSS; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | return 0; | ||
| 1118 | } | ||
| 1119 | |||
| 1120 | static int iwm_ntf_stop_resume_tx(struct iwm_priv *iwm, u8 *buf, | ||
| 1121 | unsigned long buf_size, | ||
| 1122 | struct iwm_wifi_cmd *cmd) | ||
| 1123 | { | ||
| 1124 | struct iwm_umac_notif_stop_resume_tx *stp_res_tx = | ||
| 1125 | (struct iwm_umac_notif_stop_resume_tx *)buf; | ||
| 1126 | struct iwm_sta_info *sta_info; | ||
| 1127 | struct iwm_tid_info *tid_info; | ||
| 1128 | u8 sta_id = STA_ID_N_COLOR_ID(stp_res_tx->sta_id); | ||
| 1129 | u16 tid_msk = le16_to_cpu(stp_res_tx->stop_resume_tid_msk); | ||
| 1130 | int bit, ret = 0; | ||
| 1131 | bool stop = false; | ||
| 1132 | |||
| 1133 | IWM_DBG_NTF(iwm, DBG, "stop/resume notification:\n" | ||
| 1134 | "\tflags: 0x%x\n" | ||
| 1135 | "\tSTA id: %d\n" | ||
| 1136 | "\tTID bitmask: 0x%x\n", | ||
| 1137 | stp_res_tx->flags, stp_res_tx->sta_id, | ||
| 1138 | stp_res_tx->stop_resume_tid_msk); | ||
| 1139 | |||
| 1140 | if (stp_res_tx->flags & UMAC_STOP_TX_FLAG) | ||
| 1141 | stop = true; | ||
| 1142 | |||
| 1143 | sta_info = &iwm->sta_table[sta_id]; | ||
| 1144 | if (!sta_info->valid) { | ||
| 1145 | IWM_ERR(iwm, "Stoping an invalid STA: %d %d\n", | ||
| 1146 | sta_id, stp_res_tx->sta_id); | ||
| 1147 | return -EINVAL; | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | for_each_set_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) { | ||
| 1151 | tid_info = &sta_info->tid_info[bit]; | ||
| 1152 | |||
| 1153 | mutex_lock(&tid_info->mutex); | ||
| 1154 | tid_info->stopped = stop; | ||
| 1155 | mutex_unlock(&tid_info->mutex); | ||
| 1156 | |||
| 1157 | if (!stop) { | ||
| 1158 | struct iwm_tx_queue *txq; | ||
| 1159 | int queue = iwm_tid_to_queue(bit); | ||
| 1160 | |||
| 1161 | if (queue < 0) | ||
| 1162 | continue; | ||
| 1163 | |||
| 1164 | txq = &iwm->txq[queue]; | ||
| 1165 | /* | ||
| 1166 | * If we resume, we have to move our SKBs | ||
| 1167 | * back to the tx queue and queue some work. | ||
| 1168 | */ | ||
| 1169 | spin_lock_bh(&txq->lock); | ||
| 1170 | skb_queue_splice_init(&txq->queue, &txq->stopped_queue); | ||
| 1171 | spin_unlock_bh(&txq->lock); | ||
| 1172 | |||
| 1173 | queue_work(txq->wq, &txq->worker); | ||
| 1174 | } | ||
| 1175 | |||
| 1176 | } | ||
| 1177 | |||
| 1178 | /* We send an ACK only for the stop case */ | ||
| 1179 | if (stop) | ||
| 1180 | ret = iwm_send_umac_stop_resume_tx(iwm, stp_res_tx); | ||
| 1181 | |||
| 1182 | return ret; | ||
| 1183 | } | ||
| 1184 | |||
| 1185 | static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, | ||
| 1186 | unsigned long buf_size, | ||
| 1187 | struct iwm_wifi_cmd *cmd) | ||
| 1188 | { | ||
| 1189 | struct iwm_umac_wifi_if *hdr; | ||
| 1190 | |||
| 1191 | if (cmd == NULL) { | ||
| 1192 | IWM_ERR(iwm, "Couldn't find expected wifi command\n"); | ||
| 1193 | return -EINVAL; | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | hdr = (struct iwm_umac_wifi_if *)cmd->buf.payload; | ||
| 1197 | |||
| 1198 | IWM_DBG_NTF(iwm, DBG, "WIFI_IF_WRAPPER cmd is delivered to UMAC: " | ||
| 1199 | "oid is 0x%x\n", hdr->oid); | ||
| 1200 | |||
| 1201 | set_bit(hdr->oid, &iwm->wifi_ntfy[0]); | ||
| 1202 | wake_up_interruptible(&iwm->wifi_ntfy_queue); | ||
| 1203 | |||
| 1204 | switch (hdr->oid) { | ||
| 1205 | case UMAC_WIFI_IF_CMD_SET_PROFILE: | ||
| 1206 | iwm->umac_profile_active = 1; | ||
| 1207 | break; | ||
| 1208 | default: | ||
| 1209 | break; | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | return 0; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | #define CT_KILL_DELAY (30 * HZ) | ||
| 1216 | static int iwm_ntf_card_state(struct iwm_priv *iwm, u8 *buf, | ||
| 1217 | unsigned long buf_size, struct iwm_wifi_cmd *cmd) | ||
| 1218 | { | ||
| 1219 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
| 1220 | struct iwm_lmac_card_state *state = (struct iwm_lmac_card_state *) | ||
| 1221 | (buf + sizeof(struct iwm_umac_wifi_in_hdr)); | ||
| 1222 | u32 flags = le32_to_cpu(state->flags); | ||
| 1223 | |||
| 1224 | IWM_INFO(iwm, "HW RF Kill %s, CT Kill %s\n", | ||
| 1225 | flags & IWM_CARD_STATE_HW_DISABLED ? "ON" : "OFF", | ||
| 1226 | flags & IWM_CARD_STATE_CTKILL_DISABLED ? "ON" : "OFF"); | ||
| 1227 | |||
| 1228 | if (flags & IWM_CARD_STATE_CTKILL_DISABLED) { | ||
| 1229 | /* | ||
| 1230 | * We got a CTKILL event: We bring the interface down in | ||
| 1231 | * oder to cool the device down, and try to bring it up | ||
| 1232 | * 30 seconds later. If it's still too hot, we'll go through | ||
| 1233 | * this code path again. | ||
| 1234 | */ | ||
| 1235 | cancel_delayed_work_sync(&iwm->ct_kill_delay); | ||
| 1236 | schedule_delayed_work(&iwm->ct_kill_delay, CT_KILL_DELAY); | ||
| 1237 | } | ||
| 1238 | |||
| 1239 | wiphy_rfkill_set_hw_state(wiphy, flags & | ||
| 1240 | (IWM_CARD_STATE_HW_DISABLED | | ||
| 1241 | IWM_CARD_STATE_CTKILL_DISABLED)); | ||
| 1242 | |||
| 1243 | return 0; | ||
| 1244 | } | ||
| 1245 | |||
| 1246 | static int iwm_rx_handle_wifi(struct iwm_priv *iwm, u8 *buf, | ||
| 1247 | unsigned long buf_size) | ||
| 1248 | { | ||
| 1249 | struct iwm_umac_wifi_in_hdr *wifi_hdr; | ||
| 1250 | struct iwm_wifi_cmd *cmd; | ||
| 1251 | u8 source, cmd_id; | ||
| 1252 | u16 seq_num; | ||
| 1253 | u32 count; | ||
| 1254 | |||
| 1255 | wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf; | ||
| 1256 | cmd_id = wifi_hdr->sw_hdr.cmd.cmd; | ||
| 1257 | source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE); | ||
| 1258 | if (source >= IWM_SRC_NUM) { | ||
| 1259 | IWM_CRIT(iwm, "invalid source %d\n", source); | ||
| 1260 | return -EINVAL; | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | if (cmd_id == REPLY_RX_MPDU_CMD) | ||
| 1264 | trace_iwm_rx_packet(iwm, buf, buf_size); | ||
| 1265 | else if ((cmd_id == UMAC_NOTIFY_OPCODE_RX_TICKET) && | ||
| 1266 | (source == UMAC_HDI_IN_SOURCE_FW)) | ||
| 1267 | trace_iwm_rx_ticket(iwm, buf, buf_size); | ||
| 1268 | else | ||
| 1269 | trace_iwm_rx_wifi_cmd(iwm, wifi_hdr); | ||
| 1270 | |||
| 1271 | count = GET_VAL32(wifi_hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT); | ||
| 1272 | count += sizeof(struct iwm_umac_wifi_in_hdr) - | ||
| 1273 | sizeof(struct iwm_dev_cmd_hdr); | ||
| 1274 | if (count > buf_size) { | ||
| 1275 | IWM_CRIT(iwm, "count %d, buf size:%ld\n", count, buf_size); | ||
| 1276 | return -EINVAL; | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | seq_num = le16_to_cpu(wifi_hdr->sw_hdr.cmd.seq_num); | ||
| 1280 | |||
| 1281 | IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x, seqnum: %d\n", | ||
| 1282 | cmd_id, source, seq_num); | ||
| 1283 | |||
| 1284 | /* | ||
| 1285 | * If this is a response to a previously sent command, there must | ||
| 1286 | * be a pending command for this sequence number. | ||
| 1287 | */ | ||
| 1288 | cmd = iwm_get_pending_wifi_cmd(iwm, seq_num); | ||
| 1289 | |||
| 1290 | /* Notify the caller only for sync commands. */ | ||
| 1291 | switch (source) { | ||
| 1292 | case UMAC_HDI_IN_SOURCE_FHRX: | ||
| 1293 | if (iwm->lmac_handlers[cmd_id] && | ||
| 1294 | test_bit(cmd_id, &iwm->lmac_handler_map[0])) | ||
| 1295 | return iwm_notif_send(iwm, cmd, cmd_id, source, | ||
| 1296 | buf, count); | ||
| 1297 | break; | ||
| 1298 | case UMAC_HDI_IN_SOURCE_FW: | ||
| 1299 | if (iwm->umac_handlers[cmd_id] && | ||
| 1300 | test_bit(cmd_id, &iwm->umac_handler_map[0])) | ||
| 1301 | return iwm_notif_send(iwm, cmd, cmd_id, source, | ||
| 1302 | buf, count); | ||
| 1303 | break; | ||
| 1304 | case UMAC_HDI_IN_SOURCE_UDMA: | ||
| 1305 | break; | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | return iwm_rx_handle_resp(iwm, buf, count, cmd); | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | int iwm_rx_handle_resp(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size, | ||
| 1312 | struct iwm_wifi_cmd *cmd) | ||
| 1313 | { | ||
| 1314 | u8 source, cmd_id; | ||
| 1315 | struct iwm_umac_wifi_in_hdr *wifi_hdr; | ||
| 1316 | int ret = 0; | ||
| 1317 | |||
| 1318 | wifi_hdr = (struct iwm_umac_wifi_in_hdr *)buf; | ||
| 1319 | cmd_id = wifi_hdr->sw_hdr.cmd.cmd; | ||
| 1320 | |||
| 1321 | source = GET_VAL32(wifi_hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE); | ||
| 1322 | |||
| 1323 | IWM_DBG_RX(iwm, DBG, "CMD:0x%x, source: 0x%x\n", cmd_id, source); | ||
| 1324 | |||
| 1325 | switch (source) { | ||
| 1326 | case UMAC_HDI_IN_SOURCE_FHRX: | ||
| 1327 | if (iwm->lmac_handlers[cmd_id]) | ||
| 1328 | ret = iwm->lmac_handlers[cmd_id] | ||
| 1329 | (iwm, buf, buf_size, cmd); | ||
| 1330 | break; | ||
| 1331 | case UMAC_HDI_IN_SOURCE_FW: | ||
| 1332 | if (iwm->umac_handlers[cmd_id]) | ||
| 1333 | ret = iwm->umac_handlers[cmd_id] | ||
| 1334 | (iwm, buf, buf_size, cmd); | ||
| 1335 | break; | ||
| 1336 | case UMAC_HDI_IN_SOURCE_UDMA: | ||
| 1337 | ret = -EINVAL; | ||
| 1338 | break; | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | kfree(cmd); | ||
| 1342 | |||
| 1343 | return ret; | ||
| 1344 | } | ||
| 1345 | |||
| 1346 | static int iwm_rx_handle_nonwifi(struct iwm_priv *iwm, u8 *buf, | ||
| 1347 | unsigned long buf_size) | ||
| 1348 | { | ||
| 1349 | u8 seq_num; | ||
| 1350 | struct iwm_udma_in_hdr *hdr = (struct iwm_udma_in_hdr *)buf; | ||
| 1351 | struct iwm_nonwifi_cmd *cmd; | ||
| 1352 | |||
| 1353 | trace_iwm_rx_nonwifi_cmd(iwm, buf, buf_size); | ||
| 1354 | seq_num = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM); | ||
| 1355 | |||
| 1356 | /* | ||
| 1357 | * We received a non wifi answer. | ||
| 1358 | * Let's check if there's a pending command for it, and if so | ||
| 1359 | * replace the command payload with the buffer, and then wake the | ||
| 1360 | * callers up. | ||
| 1361 | * That means we only support synchronised non wifi command response | ||
| 1362 | * schemes. | ||
| 1363 | */ | ||
| 1364 | list_for_each_entry(cmd, &iwm->nonwifi_pending_cmd, pending) | ||
| 1365 | if (cmd->seq_num == seq_num) { | ||
| 1366 | cmd->resp_received = 1; | ||
| 1367 | cmd->buf.len = buf_size; | ||
| 1368 | memcpy(cmd->buf.hdr, buf, buf_size); | ||
| 1369 | wake_up_interruptible(&iwm->nonwifi_queue); | ||
| 1370 | } | ||
| 1371 | |||
| 1372 | return 0; | ||
| 1373 | } | ||
| 1374 | |||
| 1375 | static int iwm_rx_handle_umac(struct iwm_priv *iwm, u8 *buf, | ||
| 1376 | unsigned long buf_size) | ||
| 1377 | { | ||
| 1378 | int ret = 0; | ||
| 1379 | u8 op_code; | ||
| 1380 | unsigned long buf_offset = 0; | ||
| 1381 | struct iwm_udma_in_hdr *hdr; | ||
| 1382 | |||
| 1383 | /* | ||
| 1384 | * To allow for a more efficient bus usage, UMAC | ||
| 1385 | * messages are encapsulated into UDMA ones. This | ||
| 1386 | * way we can have several UMAC messages in one bus | ||
| 1387 | * transfer. | ||
| 1388 | * A UDMA frame size is always aligned on 16 bytes, | ||
| 1389 | * and a UDMA frame must not start with a UMAC_PAD_TERMINAL | ||
| 1390 | * word. This is how we parse a bus frame into several | ||
| 1391 | * UDMA ones. | ||
| 1392 | */ | ||
| 1393 | while (buf_offset < buf_size) { | ||
| 1394 | |||
| 1395 | hdr = (struct iwm_udma_in_hdr *)(buf + buf_offset); | ||
| 1396 | |||
| 1397 | if (iwm_rx_check_udma_hdr(hdr) < 0) { | ||
| 1398 | IWM_DBG_RX(iwm, DBG, "End of frame\n"); | ||
| 1399 | break; | ||
| 1400 | } | ||
| 1401 | |||
| 1402 | op_code = GET_VAL32(hdr->cmd, UMAC_HDI_IN_CMD_OPCODE); | ||
| 1403 | |||
| 1404 | IWM_DBG_RX(iwm, DBG, "Op code: 0x%x\n", op_code); | ||
| 1405 | |||
| 1406 | if (op_code == UMAC_HDI_IN_OPCODE_WIFI) { | ||
| 1407 | ret |= iwm_rx_handle_wifi(iwm, buf + buf_offset, | ||
| 1408 | buf_size - buf_offset); | ||
| 1409 | } else if (op_code < UMAC_HDI_IN_OPCODE_NONWIFI_MAX) { | ||
| 1410 | if (GET_VAL32(hdr->cmd, | ||
| 1411 | UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) != | ||
| 1412 | UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG) { | ||
| 1413 | IWM_ERR(iwm, "Incorrect hw signature\n"); | ||
| 1414 | return -EINVAL; | ||
| 1415 | } | ||
| 1416 | ret |= iwm_rx_handle_nonwifi(iwm, buf + buf_offset, | ||
| 1417 | buf_size - buf_offset); | ||
| 1418 | } else { | ||
| 1419 | IWM_ERR(iwm, "Invalid RX opcode: 0x%x\n", op_code); | ||
| 1420 | ret |= -EINVAL; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | buf_offset += iwm_rx_resp_size(hdr); | ||
| 1424 | } | ||
| 1425 | |||
| 1426 | return ret; | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | int iwm_rx_handle(struct iwm_priv *iwm, u8 *buf, unsigned long buf_size) | ||
| 1430 | { | ||
| 1431 | struct iwm_udma_in_hdr *hdr; | ||
| 1432 | |||
| 1433 | hdr = (struct iwm_udma_in_hdr *)buf; | ||
| 1434 | |||
| 1435 | switch (le32_to_cpu(hdr->cmd)) { | ||
| 1436 | case UMAC_REBOOT_BARKER: | ||
| 1437 | if (test_bit(IWM_STATUS_READY, &iwm->status)) { | ||
| 1438 | IWM_ERR(iwm, "Unexpected BARKER\n"); | ||
| 1439 | |||
| 1440 | schedule_work(&iwm->reset_worker); | ||
| 1441 | |||
| 1442 | return 0; | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | return iwm_notif_send(iwm, NULL, IWM_BARKER_REBOOT_NOTIFICATION, | ||
| 1446 | IWM_SRC_UDMA, buf, buf_size); | ||
| 1447 | case UMAC_ACK_BARKER: | ||
| 1448 | return iwm_notif_send(iwm, NULL, IWM_ACK_BARKER_NOTIFICATION, | ||
| 1449 | IWM_SRC_UDMA, NULL, 0); | ||
| 1450 | default: | ||
| 1451 | IWM_DBG_RX(iwm, DBG, "Received cmd: 0x%x\n", hdr->cmd); | ||
| 1452 | return iwm_rx_handle_umac(iwm, buf, buf_size); | ||
| 1453 | } | ||
| 1454 | |||
| 1455 | return 0; | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | static const iwm_handler iwm_umac_handlers[] = | ||
| 1459 | { | ||
| 1460 | [UMAC_NOTIFY_OPCODE_ERROR] = iwm_ntf_error, | ||
| 1461 | [UMAC_NOTIFY_OPCODE_ALIVE] = iwm_ntf_umac_alive, | ||
| 1462 | [UMAC_NOTIFY_OPCODE_INIT_COMPLETE] = iwm_ntf_init_complete, | ||
| 1463 | [UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS] = iwm_ntf_wifi_status, | ||
| 1464 | [UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_mlme, | ||
| 1465 | [UMAC_NOTIFY_OPCODE_PAGE_DEALLOC] = iwm_ntf_tx_credit_update, | ||
| 1466 | [UMAC_NOTIFY_OPCODE_RX_TICKET] = iwm_ntf_rx_ticket, | ||
| 1467 | [UMAC_CMD_OPCODE_RESET] = iwm_ntf_umac_reset, | ||
| 1468 | [UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics, | ||
| 1469 | [UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy, | ||
| 1470 | [UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list, | ||
| 1471 | [UMAC_CMD_OPCODE_STOP_RESUME_STA_TX] = iwm_ntf_stop_resume_tx, | ||
| 1472 | [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet, | ||
| 1473 | [UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper, | ||
| 1474 | }; | ||
| 1475 | |||
| 1476 | static const iwm_handler iwm_lmac_handlers[] = | ||
| 1477 | { | ||
| 1478 | [REPLY_TX] = iwm_ntf_tx, | ||
| 1479 | [REPLY_ALIVE] = iwm_ntf_lmac_version, | ||
| 1480 | [CALIBRATION_RES_NOTIFICATION] = iwm_ntf_calib_res, | ||
| 1481 | [CALIBRATION_COMPLETE_NOTIFICATION] = iwm_ntf_calib_complete, | ||
| 1482 | [CALIBRATION_CFG_CMD] = iwm_ntf_calib_cfg, | ||
| 1483 | [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet, | ||
| 1484 | [CARD_STATE_NOTIFICATION] = iwm_ntf_card_state, | ||
| 1485 | }; | ||
| 1486 | |||
| 1487 | void iwm_rx_setup_handlers(struct iwm_priv *iwm) | ||
| 1488 | { | ||
| 1489 | iwm->umac_handlers = (iwm_handler *) iwm_umac_handlers; | ||
| 1490 | iwm->lmac_handlers = (iwm_handler *) iwm_lmac_handlers; | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | static void iwm_remove_iv(struct sk_buff *skb, u32 hdr_total_len) | ||
| 1494 | { | ||
| 1495 | struct ieee80211_hdr *hdr; | ||
| 1496 | unsigned int hdr_len; | ||
| 1497 | |||
| 1498 | hdr = (struct ieee80211_hdr *)skb->data; | ||
| 1499 | |||
| 1500 | if (!ieee80211_has_protected(hdr->frame_control)) | ||
| 1501 | return; | ||
| 1502 | |||
| 1503 | hdr_len = ieee80211_hdrlen(hdr->frame_control); | ||
| 1504 | if (hdr_total_len <= hdr_len) | ||
| 1505 | return; | ||
| 1506 | |||
| 1507 | memmove(skb->data + (hdr_total_len - hdr_len), skb->data, hdr_len); | ||
| 1508 | skb_pull(skb, (hdr_total_len - hdr_len)); | ||
| 1509 | } | ||
| 1510 | |||
| 1511 | static void iwm_rx_adjust_packet(struct iwm_priv *iwm, | ||
| 1512 | struct iwm_rx_packet *packet, | ||
| 1513 | struct iwm_rx_ticket_node *ticket_node) | ||
| 1514 | { | ||
| 1515 | u32 payload_offset = 0, payload_len; | ||
| 1516 | struct iwm_rx_ticket *ticket = ticket_node->ticket; | ||
| 1517 | struct iwm_rx_mpdu_hdr *mpdu_hdr; | ||
| 1518 | struct ieee80211_hdr *hdr; | ||
| 1519 | |||
| 1520 | mpdu_hdr = (struct iwm_rx_mpdu_hdr *)packet->skb->data; | ||
| 1521 | payload_offset += sizeof(struct iwm_rx_mpdu_hdr); | ||
| 1522 | /* Padding is 0 or 2 bytes */ | ||
| 1523 | payload_len = le16_to_cpu(mpdu_hdr->len) + | ||
| 1524 | (le16_to_cpu(ticket->flags) & IWM_RX_TICKET_PAD_SIZE_MSK); | ||
| 1525 | payload_len -= ticket->tail_len; | ||
| 1526 | |||
| 1527 | IWM_DBG_RX(iwm, DBG, "Packet adjusted, len:%d, offset:%d, " | ||
| 1528 | "ticket offset:%d ticket tail len:%d\n", | ||
| 1529 | payload_len, payload_offset, ticket->payload_offset, | ||
| 1530 | ticket->tail_len); | ||
| 1531 | |||
| 1532 | IWM_HEXDUMP(iwm, DBG, RX, "RAW: ", packet->skb->data, packet->skb->len); | ||
| 1533 | |||
| 1534 | skb_pull(packet->skb, payload_offset); | ||
| 1535 | skb_trim(packet->skb, payload_len); | ||
| 1536 | |||
| 1537 | iwm_remove_iv(packet->skb, ticket->payload_offset); | ||
| 1538 | |||
| 1539 | hdr = (struct ieee80211_hdr *) packet->skb->data; | ||
| 1540 | if (ieee80211_is_data_qos(hdr->frame_control)) { | ||
| 1541 | /* UMAC handed QOS_DATA frame with 2 padding bytes appended | ||
| 1542 | * to the qos_ctl field in IEEE 802.11 headers. */ | ||
| 1543 | memmove(packet->skb->data + IEEE80211_QOS_CTL_LEN + 2, | ||
| 1544 | packet->skb->data, | ||
| 1545 | ieee80211_hdrlen(hdr->frame_control) - | ||
| 1546 | IEEE80211_QOS_CTL_LEN); | ||
| 1547 | hdr = (struct ieee80211_hdr *) skb_pull(packet->skb, | ||
| 1548 | IEEE80211_QOS_CTL_LEN + 2); | ||
| 1549 | hdr->frame_control &= ~cpu_to_le16(IEEE80211_STYPE_QOS_DATA); | ||
| 1550 | } | ||
| 1551 | |||
| 1552 | IWM_HEXDUMP(iwm, DBG, RX, "ADJUSTED: ", | ||
| 1553 | packet->skb->data, packet->skb->len); | ||
| 1554 | } | ||
| 1555 | |||
| 1556 | static void classify8023(struct sk_buff *skb) | ||
| 1557 | { | ||
| 1558 | struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; | ||
| 1559 | |||
| 1560 | if (ieee80211_is_data_qos(hdr->frame_control)) { | ||
| 1561 | u8 *qc = ieee80211_get_qos_ctl(hdr); | ||
| 1562 | /* frame has qos control */ | ||
| 1563 | skb->priority = *qc & IEEE80211_QOS_CTL_TID_MASK; | ||
| 1564 | } else { | ||
| 1565 | skb->priority = 0; | ||
| 1566 | } | ||
| 1567 | } | ||
| 1568 | |||
| 1569 | static void iwm_rx_process_amsdu(struct iwm_priv *iwm, struct sk_buff *skb) | ||
| 1570 | { | ||
| 1571 | struct wireless_dev *wdev = iwm_to_wdev(iwm); | ||
| 1572 | struct net_device *ndev = iwm_to_ndev(iwm); | ||
| 1573 | struct sk_buff_head list; | ||
| 1574 | struct sk_buff *frame; | ||
| 1575 | |||
| 1576 | IWM_HEXDUMP(iwm, DBG, RX, "A-MSDU: ", skb->data, skb->len); | ||
| 1577 | |||
| 1578 | __skb_queue_head_init(&list); | ||
| 1579 | ieee80211_amsdu_to_8023s(skb, &list, ndev->dev_addr, wdev->iftype, 0, | ||
| 1580 | true); | ||
| 1581 | |||
| 1582 | while ((frame = __skb_dequeue(&list))) { | ||
| 1583 | ndev->stats.rx_packets++; | ||
| 1584 | ndev->stats.rx_bytes += frame->len; | ||
| 1585 | |||
| 1586 | frame->protocol = eth_type_trans(frame, ndev); | ||
| 1587 | frame->ip_summed = CHECKSUM_NONE; | ||
| 1588 | memset(frame->cb, 0, sizeof(frame->cb)); | ||
| 1589 | |||
| 1590 | if (netif_rx_ni(frame) == NET_RX_DROP) { | ||
| 1591 | IWM_ERR(iwm, "Packet dropped\n"); | ||
| 1592 | ndev->stats.rx_dropped++; | ||
| 1593 | } | ||
| 1594 | } | ||
| 1595 | } | ||
| 1596 | |||
| 1597 | static void iwm_rx_process_packet(struct iwm_priv *iwm, | ||
| 1598 | struct iwm_rx_packet *packet, | ||
| 1599 | struct iwm_rx_ticket_node *ticket_node) | ||
| 1600 | { | ||
| 1601 | int ret; | ||
| 1602 | struct sk_buff *skb = packet->skb; | ||
| 1603 | struct wireless_dev *wdev = iwm_to_wdev(iwm); | ||
| 1604 | struct net_device *ndev = iwm_to_ndev(iwm); | ||
| 1605 | |||
| 1606 | IWM_DBG_RX(iwm, DBG, "Processing packet ID %d\n", packet->id); | ||
| 1607 | |||
| 1608 | switch (le16_to_cpu(ticket_node->ticket->action)) { | ||
| 1609 | case IWM_RX_TICKET_RELEASE: | ||
| 1610 | IWM_DBG_RX(iwm, DBG, "RELEASE packet\n"); | ||
| 1611 | |||
| 1612 | iwm_rx_adjust_packet(iwm, packet, ticket_node); | ||
| 1613 | skb->dev = iwm_to_ndev(iwm); | ||
| 1614 | classify8023(skb); | ||
| 1615 | |||
| 1616 | if (le16_to_cpu(ticket_node->ticket->flags) & | ||
| 1617 | IWM_RX_TICKET_AMSDU_MSK) { | ||
| 1618 | iwm_rx_process_amsdu(iwm, skb); | ||
| 1619 | break; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | ret = ieee80211_data_to_8023(skb, ndev->dev_addr, wdev->iftype); | ||
| 1623 | if (ret < 0) { | ||
| 1624 | IWM_DBG_RX(iwm, DBG, "Couldn't convert 802.11 header - " | ||
| 1625 | "%d\n", ret); | ||
| 1626 | kfree_skb(packet->skb); | ||
| 1627 | break; | ||
| 1628 | } | ||
| 1629 | |||
| 1630 | IWM_HEXDUMP(iwm, DBG, RX, "802.3: ", skb->data, skb->len); | ||
| 1631 | |||
| 1632 | ndev->stats.rx_packets++; | ||
| 1633 | ndev->stats.rx_bytes += skb->len; | ||
| 1634 | |||
| 1635 | skb->protocol = eth_type_trans(skb, ndev); | ||
| 1636 | skb->ip_summed = CHECKSUM_NONE; | ||
| 1637 | memset(skb->cb, 0, sizeof(skb->cb)); | ||
| 1638 | |||
| 1639 | if (netif_rx_ni(skb) == NET_RX_DROP) { | ||
| 1640 | IWM_ERR(iwm, "Packet dropped\n"); | ||
| 1641 | ndev->stats.rx_dropped++; | ||
| 1642 | } | ||
| 1643 | break; | ||
| 1644 | case IWM_RX_TICKET_DROP: | ||
| 1645 | IWM_DBG_RX(iwm, DBG, "DROP packet: 0x%x\n", | ||
| 1646 | le16_to_cpu(ticket_node->ticket->flags)); | ||
| 1647 | kfree_skb(packet->skb); | ||
| 1648 | break; | ||
| 1649 | default: | ||
| 1650 | IWM_ERR(iwm, "Unknown ticket action: %d\n", | ||
| 1651 | le16_to_cpu(ticket_node->ticket->action)); | ||
| 1652 | kfree_skb(packet->skb); | ||
| 1653 | } | ||
| 1654 | |||
| 1655 | kfree(packet); | ||
| 1656 | iwm_rx_ticket_node_free(ticket_node); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | /* | ||
| 1660 | * Rx data processing: | ||
| 1661 | * | ||
| 1662 | * We're receiving Rx packet from the LMAC, and Rx ticket from | ||
| 1663 | * the UMAC. | ||
| 1664 | * To forward a target data packet upstream (i.e. to the | ||
| 1665 | * kernel network stack), we must have received an Rx ticket | ||
| 1666 | * that tells us we're allowed to release this packet (ticket | ||
| 1667 | * action is IWM_RX_TICKET_RELEASE). The Rx ticket also indicates, | ||
| 1668 | * among other things, where valid data actually starts in the Rx | ||
| 1669 | * packet. | ||
| 1670 | */ | ||
| 1671 | void iwm_rx_worker(struct work_struct *work) | ||
| 1672 | { | ||
| 1673 | struct iwm_priv *iwm; | ||
| 1674 | struct iwm_rx_ticket_node *ticket, *next; | ||
| 1675 | |||
| 1676 | iwm = container_of(work, struct iwm_priv, rx_worker); | ||
| 1677 | |||
| 1678 | /* | ||
| 1679 | * We go through the tickets list and if there is a pending | ||
| 1680 | * packet for it, we push it upstream. | ||
| 1681 | * We stop whenever a ticket is missing its packet, as we're | ||
| 1682 | * supposed to send the packets in order. | ||
| 1683 | */ | ||
| 1684 | spin_lock(&iwm->ticket_lock); | ||
| 1685 | list_for_each_entry_safe(ticket, next, &iwm->rx_tickets, node) { | ||
| 1686 | struct iwm_rx_packet *packet = | ||
| 1687 | iwm_rx_packet_get(iwm, le16_to_cpu(ticket->ticket->id)); | ||
| 1688 | |||
| 1689 | if (!packet) { | ||
| 1690 | IWM_DBG_RX(iwm, DBG, "Skip rx_work: Wait for ticket %d " | ||
| 1691 | "to be handled first\n", | ||
| 1692 | le16_to_cpu(ticket->ticket->id)); | ||
| 1693 | break; | ||
| 1694 | } | ||
| 1695 | |||
| 1696 | list_del(&ticket->node); | ||
| 1697 | iwm_rx_process_packet(iwm, packet, ticket); | ||
| 1698 | } | ||
| 1699 | spin_unlock(&iwm->ticket_lock); | ||
| 1700 | } | ||
| 1701 | |||
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.h b/drivers/net/wireless/iwmc3200wifi/rx.h new file mode 100644 index 00000000000..da0db91cee5 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/rx.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_RX_H__ | ||
| 40 | #define __IWM_RX_H__ | ||
| 41 | |||
| 42 | #include <linux/skbuff.h> | ||
| 43 | |||
| 44 | #include "umac.h" | ||
| 45 | |||
| 46 | struct iwm_rx_ticket_node { | ||
| 47 | struct list_head node; | ||
| 48 | struct iwm_rx_ticket *ticket; | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct iwm_rx_packet { | ||
| 52 | struct list_head node; | ||
| 53 | u16 id; | ||
| 54 | struct sk_buff *skb; | ||
| 55 | unsigned long pkt_size; | ||
| 56 | }; | ||
| 57 | |||
| 58 | void iwm_rx_worker(struct work_struct *work); | ||
| 59 | |||
| 60 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c new file mode 100644 index 00000000000..56383e7be83 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c | |||
| @@ -0,0 +1,515 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | /* | ||
| 40 | * This is the SDIO bus specific hooks for iwm. | ||
| 41 | * It also is the module's entry point. | ||
| 42 | * | ||
| 43 | * Interesting code paths: | ||
| 44 | * iwm_sdio_probe() (Called by an SDIO bus scan) | ||
| 45 | * -> iwm_if_alloc() (netdev.c) | ||
| 46 | * -> iwm_wdev_alloc() (cfg80211.c, allocates and register our wiphy) | ||
| 47 | * -> wiphy_new() | ||
| 48 | * -> wiphy_register() | ||
| 49 | * -> alloc_netdev_mq() | ||
| 50 | * -> register_netdev() | ||
| 51 | * | ||
| 52 | * iwm_sdio_remove() | ||
| 53 | * -> iwm_if_free() (netdev.c) | ||
| 54 | * -> unregister_netdev() | ||
| 55 | * -> iwm_wdev_free() (cfg80211.c) | ||
| 56 | * -> wiphy_unregister() | ||
| 57 | * -> wiphy_free() | ||
| 58 | * | ||
| 59 | * iwm_sdio_isr() (called in process context from the SDIO core code) | ||
| 60 | * -> queue_work(.., isr_worker) | ||
| 61 | * -- [async] --> iwm_sdio_isr_worker() | ||
| 62 | * -> iwm_rx_handle() | ||
| 63 | */ | ||
| 64 | |||
| 65 | #include <linux/kernel.h> | ||
| 66 | #include <linux/slab.h> | ||
| 67 | #include <linux/netdevice.h> | ||
| 68 | #include <linux/debugfs.h> | ||
| 69 | #include <linux/mmc/sdio_ids.h> | ||
| 70 | #include <linux/mmc/sdio.h> | ||
| 71 | #include <linux/mmc/sdio_func.h> | ||
| 72 | |||
| 73 | #include "iwm.h" | ||
| 74 | #include "debug.h" | ||
| 75 | #include "bus.h" | ||
| 76 | #include "sdio.h" | ||
| 77 | |||
| 78 | static void iwm_sdio_isr_worker(struct work_struct *work) | ||
| 79 | { | ||
| 80 | struct iwm_sdio_priv *hw; | ||
| 81 | struct iwm_priv *iwm; | ||
| 82 | struct iwm_rx_info *rx_info; | ||
| 83 | struct sk_buff *skb; | ||
| 84 | u8 *rx_buf; | ||
| 85 | unsigned long rx_size; | ||
| 86 | |||
| 87 | hw = container_of(work, struct iwm_sdio_priv, isr_worker); | ||
| 88 | iwm = hw_to_iwm(hw); | ||
| 89 | |||
| 90 | while (!skb_queue_empty(&iwm->rx_list)) { | ||
| 91 | skb = skb_dequeue(&iwm->rx_list); | ||
| 92 | rx_info = skb_to_rx_info(skb); | ||
| 93 | rx_size = rx_info->rx_size; | ||
| 94 | rx_buf = skb->data; | ||
| 95 | |||
| 96 | IWM_HEXDUMP(iwm, DBG, SDIO, "RX: ", rx_buf, rx_size); | ||
| 97 | if (iwm_rx_handle(iwm, rx_buf, rx_size) < 0) | ||
| 98 | IWM_WARN(iwm, "RX error\n"); | ||
| 99 | |||
| 100 | kfree_skb(skb); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | |||
| 104 | static void iwm_sdio_isr(struct sdio_func *func) | ||
| 105 | { | ||
| 106 | struct iwm_priv *iwm; | ||
| 107 | struct iwm_sdio_priv *hw; | ||
| 108 | struct iwm_rx_info *rx_info; | ||
| 109 | struct sk_buff *skb; | ||
| 110 | unsigned long buf_size, read_size; | ||
| 111 | int ret; | ||
| 112 | u8 val; | ||
| 113 | |||
| 114 | hw = sdio_get_drvdata(func); | ||
| 115 | iwm = hw_to_iwm(hw); | ||
| 116 | |||
| 117 | buf_size = hw->blk_size; | ||
| 118 | |||
| 119 | /* We're checking the status */ | ||
| 120 | val = sdio_readb(func, IWM_SDIO_INTR_STATUS_ADDR, &ret); | ||
| 121 | if (val == 0 || ret < 0) { | ||
| 122 | IWM_ERR(iwm, "Wrong INTR_STATUS\n"); | ||
| 123 | return; | ||
| 124 | } | ||
| 125 | |||
| 126 | /* See if we have free buffers */ | ||
| 127 | if (skb_queue_len(&iwm->rx_list) > IWM_RX_LIST_SIZE) { | ||
| 128 | IWM_ERR(iwm, "No buffer for more Rx frames\n"); | ||
| 129 | return; | ||
| 130 | } | ||
| 131 | |||
| 132 | /* We first read the transaction size */ | ||
| 133 | read_size = sdio_readb(func, IWM_SDIO_INTR_GET_SIZE_ADDR + 1, &ret); | ||
| 134 | read_size = read_size << 8; | ||
| 135 | |||
| 136 | if (ret < 0) { | ||
| 137 | IWM_ERR(iwm, "Couldn't read the xfer size\n"); | ||
| 138 | return; | ||
| 139 | } | ||
| 140 | |||
| 141 | /* We need to clear the INT register */ | ||
| 142 | sdio_writeb(func, 1, IWM_SDIO_INTR_CLEAR_ADDR, &ret); | ||
| 143 | if (ret < 0) { | ||
| 144 | IWM_ERR(iwm, "Couldn't clear the INT register\n"); | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | |||
| 148 | while (buf_size < read_size) | ||
| 149 | buf_size <<= 1; | ||
| 150 | |||
| 151 | skb = dev_alloc_skb(buf_size); | ||
| 152 | if (!skb) { | ||
| 153 | IWM_ERR(iwm, "Couldn't alloc RX skb\n"); | ||
| 154 | return; | ||
| 155 | } | ||
| 156 | rx_info = skb_to_rx_info(skb); | ||
| 157 | rx_info->rx_size = read_size; | ||
| 158 | rx_info->rx_buf_size = buf_size; | ||
| 159 | |||
| 160 | /* Now we can read the actual buffer */ | ||
| 161 | ret = sdio_memcpy_fromio(func, skb_put(skb, read_size), | ||
| 162 | IWM_SDIO_DATA_ADDR, read_size); | ||
| 163 | |||
| 164 | /* The skb is put on a driver's specific Rx SKB list */ | ||
| 165 | skb_queue_tail(&iwm->rx_list, skb); | ||
| 166 | |||
| 167 | /* We can now schedule the actual worker */ | ||
| 168 | queue_work(hw->isr_wq, &hw->isr_worker); | ||
| 169 | } | ||
| 170 | |||
| 171 | static void iwm_sdio_rx_free(struct iwm_sdio_priv *hw) | ||
| 172 | { | ||
| 173 | struct iwm_priv *iwm = hw_to_iwm(hw); | ||
| 174 | |||
| 175 | flush_workqueue(hw->isr_wq); | ||
| 176 | |||
| 177 | skb_queue_purge(&iwm->rx_list); | ||
| 178 | } | ||
| 179 | |||
| 180 | /* Bus ops */ | ||
| 181 | static int if_sdio_enable(struct iwm_priv *iwm) | ||
| 182 | { | ||
| 183 | struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); | ||
| 184 | int ret; | ||
| 185 | |||
| 186 | sdio_claim_host(hw->func); | ||
| 187 | |||
| 188 | ret = sdio_enable_func(hw->func); | ||
| 189 | if (ret) { | ||
| 190 | IWM_ERR(iwm, "Couldn't enable the device: is TOP driver " | ||
| 191 | "loaded and functional?\n"); | ||
| 192 | goto release_host; | ||
| 193 | } | ||
| 194 | |||
| 195 | iwm_reset(iwm); | ||
| 196 | |||
| 197 | ret = sdio_claim_irq(hw->func, iwm_sdio_isr); | ||
| 198 | if (ret) { | ||
| 199 | IWM_ERR(iwm, "Failed to claim irq: %d\n", ret); | ||
| 200 | goto release_host; | ||
| 201 | } | ||
| 202 | |||
| 203 | sdio_writeb(hw->func, 1, IWM_SDIO_INTR_ENABLE_ADDR, &ret); | ||
| 204 | if (ret < 0) { | ||
| 205 | IWM_ERR(iwm, "Couldn't enable INTR: %d\n", ret); | ||
| 206 | goto release_irq; | ||
| 207 | } | ||
| 208 | |||
| 209 | sdio_release_host(hw->func); | ||
| 210 | |||
| 211 | IWM_DBG_SDIO(iwm, INFO, "IWM SDIO enable\n"); | ||
| 212 | |||
| 213 | return 0; | ||
| 214 | |||
| 215 | release_irq: | ||
| 216 | sdio_release_irq(hw->func); | ||
| 217 | release_host: | ||
| 218 | sdio_release_host(hw->func); | ||
| 219 | |||
| 220 | return ret; | ||
| 221 | } | ||
| 222 | |||
| 223 | static int if_sdio_disable(struct iwm_priv *iwm) | ||
| 224 | { | ||
| 225 | struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); | ||
| 226 | int ret; | ||
| 227 | |||
| 228 | sdio_claim_host(hw->func); | ||
| 229 | sdio_writeb(hw->func, 0, IWM_SDIO_INTR_ENABLE_ADDR, &ret); | ||
| 230 | if (ret < 0) | ||
| 231 | IWM_WARN(iwm, "Couldn't disable INTR: %d\n", ret); | ||
| 232 | |||
| 233 | sdio_release_irq(hw->func); | ||
| 234 | sdio_disable_func(hw->func); | ||
| 235 | sdio_release_host(hw->func); | ||
| 236 | |||
| 237 | iwm_sdio_rx_free(hw); | ||
| 238 | |||
| 239 | iwm_reset(iwm); | ||
| 240 | |||
| 241 | IWM_DBG_SDIO(iwm, INFO, "IWM SDIO disable\n"); | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
| 245 | |||
| 246 | static int if_sdio_send_chunk(struct iwm_priv *iwm, u8 *buf, int count) | ||
| 247 | { | ||
| 248 | struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); | ||
| 249 | int aligned_count = ALIGN(count, hw->blk_size); | ||
| 250 | int ret; | ||
| 251 | |||
| 252 | if ((unsigned long)buf & 0x3) { | ||
| 253 | IWM_ERR(iwm, "buf <%p> is not dword aligned\n", buf); | ||
| 254 | /* TODO: Is this a hardware limitation? use get_unligned */ | ||
| 255 | return -EINVAL; | ||
| 256 | } | ||
| 257 | |||
| 258 | sdio_claim_host(hw->func); | ||
| 259 | ret = sdio_memcpy_toio(hw->func, IWM_SDIO_DATA_ADDR, buf, | ||
| 260 | aligned_count); | ||
| 261 | sdio_release_host(hw->func); | ||
| 262 | |||
| 263 | return ret; | ||
| 264 | } | ||
| 265 | |||
| 266 | /* debugfs hooks */ | ||
| 267 | static int iwm_debugfs_sdio_open(struct inode *inode, struct file *filp) | ||
| 268 | { | ||
| 269 | filp->private_data = inode->i_private; | ||
| 270 | return 0; | ||
| 271 | } | ||
| 272 | |||
| 273 | static ssize_t iwm_debugfs_sdio_read(struct file *filp, char __user *buffer, | ||
| 274 | size_t count, loff_t *ppos) | ||
| 275 | { | ||
| 276 | struct iwm_priv *iwm = filp->private_data; | ||
| 277 | struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); | ||
| 278 | char *buf; | ||
| 279 | u8 cccr; | ||
| 280 | int buf_len = 4096, ret; | ||
| 281 | size_t len = 0; | ||
| 282 | |||
| 283 | if (*ppos != 0) | ||
| 284 | return 0; | ||
| 285 | if (count < sizeof(buf)) | ||
| 286 | return -ENOSPC; | ||
| 287 | |||
| 288 | buf = kzalloc(buf_len, GFP_KERNEL); | ||
| 289 | if (!buf) | ||
| 290 | return -ENOMEM; | ||
| 291 | |||
| 292 | sdio_claim_host(hw->func); | ||
| 293 | |||
| 294 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IOEx, &ret); | ||
| 295 | if (ret) { | ||
| 296 | IWM_ERR(iwm, "Could not read SDIO_CCCR_IOEx\n"); | ||
| 297 | goto err; | ||
| 298 | } | ||
| 299 | len += snprintf(buf + len, buf_len - len, "CCCR_IOEx: 0x%x\n", cccr); | ||
| 300 | |||
| 301 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IORx, &ret); | ||
| 302 | if (ret) { | ||
| 303 | IWM_ERR(iwm, "Could not read SDIO_CCCR_IORx\n"); | ||
| 304 | goto err; | ||
| 305 | } | ||
| 306 | len += snprintf(buf + len, buf_len - len, "CCCR_IORx: 0x%x\n", cccr); | ||
| 307 | |||
| 308 | |||
| 309 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IENx, &ret); | ||
| 310 | if (ret) { | ||
| 311 | IWM_ERR(iwm, "Could not read SDIO_CCCR_IENx\n"); | ||
| 312 | goto err; | ||
| 313 | } | ||
| 314 | len += snprintf(buf + len, buf_len - len, "CCCR_IENx: 0x%x\n", cccr); | ||
| 315 | |||
| 316 | |||
| 317 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_INTx, &ret); | ||
| 318 | if (ret) { | ||
| 319 | IWM_ERR(iwm, "Could not read SDIO_CCCR_INTx\n"); | ||
| 320 | goto err; | ||
| 321 | } | ||
| 322 | len += snprintf(buf + len, buf_len - len, "CCCR_INTx: 0x%x\n", cccr); | ||
| 323 | |||
| 324 | |||
| 325 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_ABORT, &ret); | ||
| 326 | if (ret) { | ||
| 327 | IWM_ERR(iwm, "Could not read SDIO_CCCR_ABORTx\n"); | ||
| 328 | goto err; | ||
| 329 | } | ||
| 330 | len += snprintf(buf + len, buf_len - len, "CCCR_ABORT: 0x%x\n", cccr); | ||
| 331 | |||
| 332 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_IF, &ret); | ||
| 333 | if (ret) { | ||
| 334 | IWM_ERR(iwm, "Could not read SDIO_CCCR_IF\n"); | ||
| 335 | goto err; | ||
| 336 | } | ||
| 337 | len += snprintf(buf + len, buf_len - len, "CCCR_IF: 0x%x\n", cccr); | ||
| 338 | |||
| 339 | |||
| 340 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CAPS, &ret); | ||
| 341 | if (ret) { | ||
| 342 | IWM_ERR(iwm, "Could not read SDIO_CCCR_CAPS\n"); | ||
| 343 | goto err; | ||
| 344 | } | ||
| 345 | len += snprintf(buf + len, buf_len - len, "CCCR_CAPS: 0x%x\n", cccr); | ||
| 346 | |||
| 347 | cccr = sdio_f0_readb(hw->func, SDIO_CCCR_CIS, &ret); | ||
| 348 | if (ret) { | ||
| 349 | IWM_ERR(iwm, "Could not read SDIO_CCCR_CIS\n"); | ||
| 350 | goto err; | ||
| 351 | } | ||
| 352 | len += snprintf(buf + len, buf_len - len, "CCCR_CIS: 0x%x\n", cccr); | ||
| 353 | |||
| 354 | ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); | ||
| 355 | err: | ||
| 356 | sdio_release_host(hw->func); | ||
| 357 | |||
| 358 | kfree(buf); | ||
| 359 | |||
| 360 | return ret; | ||
| 361 | } | ||
| 362 | |||
| 363 | static const struct file_operations iwm_debugfs_sdio_fops = { | ||
| 364 | .owner = THIS_MODULE, | ||
| 365 | .open = iwm_debugfs_sdio_open, | ||
| 366 | .read = iwm_debugfs_sdio_read, | ||
| 367 | .llseek = default_llseek, | ||
| 368 | }; | ||
| 369 | |||
| 370 | static void if_sdio_debugfs_init(struct iwm_priv *iwm, struct dentry *parent_dir) | ||
| 371 | { | ||
| 372 | struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); | ||
| 373 | |||
| 374 | hw->cccr_dentry = debugfs_create_file("cccr", 0200, | ||
| 375 | parent_dir, iwm, | ||
| 376 | &iwm_debugfs_sdio_fops); | ||
| 377 | } | ||
| 378 | |||
| 379 | static void if_sdio_debugfs_exit(struct iwm_priv *iwm) | ||
| 380 | { | ||
| 381 | struct iwm_sdio_priv *hw = iwm_to_if_sdio(iwm); | ||
| 382 | |||
| 383 | debugfs_remove(hw->cccr_dentry); | ||
| 384 | } | ||
| 385 | |||
| 386 | static struct iwm_if_ops if_sdio_ops = { | ||
| 387 | .enable = if_sdio_enable, | ||
| 388 | .disable = if_sdio_disable, | ||
| 389 | .send_chunk = if_sdio_send_chunk, | ||
| 390 | .debugfs_init = if_sdio_debugfs_init, | ||
| 391 | .debugfs_exit = if_sdio_debugfs_exit, | ||
| 392 | .umac_name = "iwmc3200wifi-umac-sdio.bin", | ||
| 393 | .calib_lmac_name = "iwmc3200wifi-calib-sdio.bin", | ||
| 394 | .lmac_name = "iwmc3200wifi-lmac-sdio.bin", | ||
| 395 | }; | ||
| 396 | MODULE_FIRMWARE("iwmc3200wifi-umac-sdio.bin"); | ||
| 397 | MODULE_FIRMWARE("iwmc3200wifi-calib-sdio.bin"); | ||
| 398 | MODULE_FIRMWARE("iwmc3200wifi-lmac-sdio.bin"); | ||
| 399 | |||
| 400 | static int iwm_sdio_probe(struct sdio_func *func, | ||
| 401 | const struct sdio_device_id *id) | ||
| 402 | { | ||
| 403 | struct iwm_priv *iwm; | ||
| 404 | struct iwm_sdio_priv *hw; | ||
| 405 | struct device *dev = &func->dev; | ||
| 406 | int ret; | ||
| 407 | |||
| 408 | /* check if TOP has already initialized the card */ | ||
| 409 | sdio_claim_host(func); | ||
| 410 | ret = sdio_enable_func(func); | ||
| 411 | if (ret) { | ||
| 412 | dev_err(dev, "wait for TOP to enable the device\n"); | ||
| 413 | sdio_release_host(func); | ||
| 414 | return ret; | ||
| 415 | } | ||
| 416 | |||
| 417 | ret = sdio_set_block_size(func, IWM_SDIO_BLK_SIZE); | ||
| 418 | |||
| 419 | sdio_disable_func(func); | ||
| 420 | sdio_release_host(func); | ||
| 421 | |||
| 422 | if (ret < 0) { | ||
| 423 | dev_err(dev, "Failed to set block size: %d\n", ret); | ||
| 424 | return ret; | ||
| 425 | } | ||
| 426 | |||
| 427 | iwm = iwm_if_alloc(sizeof(struct iwm_sdio_priv), dev, &if_sdio_ops); | ||
| 428 | if (IS_ERR(iwm)) { | ||
| 429 | dev_err(dev, "allocate SDIO interface failed\n"); | ||
| 430 | return PTR_ERR(iwm); | ||
| 431 | } | ||
| 432 | |||
| 433 | hw = iwm_private(iwm); | ||
| 434 | hw->iwm = iwm; | ||
| 435 | |||
| 436 | iwm_debugfs_init(iwm); | ||
| 437 | |||
| 438 | sdio_set_drvdata(func, hw); | ||
| 439 | |||
| 440 | hw->func = func; | ||
| 441 | hw->blk_size = IWM_SDIO_BLK_SIZE; | ||
| 442 | |||
| 443 | hw->isr_wq = create_singlethread_workqueue(KBUILD_MODNAME "_sdio"); | ||
| 444 | if (!hw->isr_wq) { | ||
| 445 | ret = -ENOMEM; | ||
| 446 | goto debugfs_exit; | ||
| 447 | } | ||
| 448 | |||
| 449 | INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker); | ||
| 450 | |||
| 451 | ret = iwm_if_add(iwm); | ||
| 452 | if (ret) { | ||
| 453 | dev_err(dev, "add SDIO interface failed\n"); | ||
| 454 | goto destroy_wq; | ||
| 455 | } | ||
| 456 | |||
| 457 | dev_info(dev, "IWM SDIO probe\n"); | ||
| 458 | |||
| 459 | return 0; | ||
| 460 | |||
| 461 | destroy_wq: | ||
| 462 | destroy_workqueue(hw->isr_wq); | ||
| 463 | debugfs_exit: | ||
| 464 | iwm_debugfs_exit(iwm); | ||
| 465 | iwm_if_free(iwm); | ||
| 466 | return ret; | ||
| 467 | } | ||
| 468 | |||
| 469 | static void iwm_sdio_remove(struct sdio_func *func) | ||
| 470 | { | ||
| 471 | struct iwm_sdio_priv *hw = sdio_get_drvdata(func); | ||
| 472 | struct iwm_priv *iwm = hw_to_iwm(hw); | ||
| 473 | struct device *dev = &func->dev; | ||
| 474 | |||
| 475 | iwm_if_remove(iwm); | ||
| 476 | destroy_workqueue(hw->isr_wq); | ||
| 477 | iwm_debugfs_exit(iwm); | ||
| 478 | iwm_if_free(iwm); | ||
| 479 | |||
| 480 | sdio_set_drvdata(func, NULL); | ||
| 481 | |||
| 482 | dev_info(dev, "IWM SDIO remove\n"); | ||
| 483 | } | ||
| 484 | |||
| 485 | static const struct sdio_device_id iwm_sdio_ids[] = { | ||
| 486 | /* Global/AGN SKU */ | ||
| 487 | { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1403) }, | ||
| 488 | /* BGN SKU */ | ||
| 489 | { SDIO_DEVICE(SDIO_VENDOR_ID_INTEL, 0x1408) }, | ||
| 490 | { /* end: all zeroes */ }, | ||
| 491 | }; | ||
| 492 | MODULE_DEVICE_TABLE(sdio, iwm_sdio_ids); | ||
| 493 | |||
| 494 | static struct sdio_driver iwm_sdio_driver = { | ||
| 495 | .name = "iwm_sdio", | ||
| 496 | .id_table = iwm_sdio_ids, | ||
| 497 | .probe = iwm_sdio_probe, | ||
| 498 | .remove = iwm_sdio_remove, | ||
| 499 | }; | ||
| 500 | |||
| 501 | static int __init iwm_sdio_init_module(void) | ||
| 502 | { | ||
| 503 | return sdio_register_driver(&iwm_sdio_driver); | ||
| 504 | } | ||
| 505 | |||
| 506 | static void __exit iwm_sdio_exit_module(void) | ||
| 507 | { | ||
| 508 | sdio_unregister_driver(&iwm_sdio_driver); | ||
| 509 | } | ||
| 510 | |||
| 511 | module_init(iwm_sdio_init_module); | ||
| 512 | module_exit(iwm_sdio_exit_module); | ||
| 513 | |||
| 514 | MODULE_LICENSE("GPL"); | ||
| 515 | MODULE_AUTHOR(IWM_COPYRIGHT " " IWM_AUTHOR); | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.h b/drivers/net/wireless/iwmc3200wifi/sdio.h new file mode 100644 index 00000000000..aab6b6892e4 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/sdio.h | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_SDIO_H__ | ||
| 40 | #define __IWM_SDIO_H__ | ||
| 41 | |||
| 42 | #define IWM_SDIO_DATA_ADDR 0x0 | ||
| 43 | #define IWM_SDIO_INTR_ENABLE_ADDR 0x14 | ||
| 44 | #define IWM_SDIO_INTR_STATUS_ADDR 0x13 | ||
| 45 | #define IWM_SDIO_INTR_CLEAR_ADDR 0x13 | ||
| 46 | #define IWM_SDIO_INTR_GET_SIZE_ADDR 0x2C | ||
| 47 | |||
| 48 | #define IWM_SDIO_BLK_SIZE 256 | ||
| 49 | |||
| 50 | #define iwm_to_if_sdio(i) (struct iwm_sdio_priv *)(iwm->private) | ||
| 51 | |||
| 52 | struct iwm_sdio_priv { | ||
| 53 | struct sdio_func *func; | ||
| 54 | struct iwm_priv *iwm; | ||
| 55 | |||
| 56 | struct workqueue_struct *isr_wq; | ||
| 57 | struct work_struct isr_worker; | ||
| 58 | |||
| 59 | struct dentry *cccr_dentry; | ||
| 60 | |||
| 61 | unsigned int blk_size; | ||
| 62 | }; | ||
| 63 | |||
| 64 | #endif | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/trace.c b/drivers/net/wireless/iwmc3200wifi/trace.c new file mode 100644 index 00000000000..904d36f2231 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/trace.c | |||
| @@ -0,0 +1,3 @@ | |||
| 1 | #include "iwm.h" | ||
| 2 | #define CREATE_TRACE_POINTS | ||
| 3 | #include "trace.h" | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/trace.h b/drivers/net/wireless/iwmc3200wifi/trace.h new file mode 100644 index 00000000000..abb4805fa8d --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/trace.h | |||
| @@ -0,0 +1,283 @@ | |||
| 1 | #if !defined(__IWM_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ) | ||
| 2 | #define __IWM_TRACE_H__ | ||
| 3 | |||
| 4 | #include <linux/tracepoint.h> | ||
| 5 | |||
| 6 | #if !defined(CONFIG_IWM_TRACING) | ||
| 7 | #undef TRACE_EVENT | ||
| 8 | #define TRACE_EVENT(name, proto, ...) \ | ||
| 9 | static inline void trace_ ## name(proto) {} | ||
| 10 | #endif | ||
| 11 | |||
| 12 | #undef TRACE_SYSTEM | ||
| 13 | #define TRACE_SYSTEM iwm | ||
| 14 | |||
| 15 | #define IWM_ENTRY __array(char, ndev_name, 16) | ||
| 16 | #define IWM_ASSIGN strlcpy(__entry->ndev_name, iwm_to_ndev(iwm)->name, 16) | ||
| 17 | #define IWM_PR_FMT "%s" | ||
| 18 | #define IWM_PR_ARG __entry->ndev_name | ||
| 19 | |||
| 20 | TRACE_EVENT(iwm_tx_nonwifi_cmd, | ||
| 21 | TP_PROTO(struct iwm_priv *iwm, struct iwm_udma_out_nonwifi_hdr *hdr), | ||
| 22 | |||
| 23 | TP_ARGS(iwm, hdr), | ||
| 24 | |||
| 25 | TP_STRUCT__entry( | ||
| 26 | IWM_ENTRY | ||
| 27 | __field(u8, opcode) | ||
| 28 | __field(u8, resp) | ||
| 29 | __field(u8, eot) | ||
| 30 | __field(u8, hw) | ||
| 31 | __field(u16, seq) | ||
| 32 | __field(u32, addr) | ||
| 33 | __field(u32, op1) | ||
| 34 | __field(u32, op2) | ||
| 35 | ), | ||
| 36 | |||
| 37 | TP_fast_assign( | ||
| 38 | IWM_ASSIGN; | ||
| 39 | __entry->opcode = GET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_OPCODE); | ||
| 40 | __entry->resp = GET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_RESP); | ||
| 41 | __entry->eot = GET_VAL32(hdr->cmd, UMAC_HDI_OUT_CMD_EOT); | ||
| 42 | __entry->hw = GET_VAL32(hdr->cmd, UDMA_HDI_OUT_NW_CMD_HANDLE_BY_HW); | ||
| 43 | __entry->seq = GET_VAL32(hdr->cmd, UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM); | ||
| 44 | __entry->addr = le32_to_cpu(hdr->addr); | ||
| 45 | __entry->op1 = le32_to_cpu(hdr->op1_sz); | ||
| 46 | __entry->op2 = le32_to_cpu(hdr->op2); | ||
| 47 | ), | ||
| 48 | |||
| 49 | TP_printk( | ||
| 50 | IWM_PR_FMT " Tx TARGET CMD: opcode 0x%x, resp %d, eot %d, " | ||
| 51 | "hw %d, seq 0x%x, addr 0x%x, op1 0x%x, op2 0x%x", | ||
| 52 | IWM_PR_ARG, __entry->opcode, __entry->resp, __entry->eot, | ||
| 53 | __entry->hw, __entry->seq, __entry->addr, __entry->op1, | ||
| 54 | __entry->op2 | ||
| 55 | ) | ||
| 56 | ); | ||
| 57 | |||
| 58 | TRACE_EVENT(iwm_tx_wifi_cmd, | ||
| 59 | TP_PROTO(struct iwm_priv *iwm, struct iwm_umac_wifi_out_hdr *hdr), | ||
| 60 | |||
| 61 | TP_ARGS(iwm, hdr), | ||
| 62 | |||
| 63 | TP_STRUCT__entry( | ||
| 64 | IWM_ENTRY | ||
| 65 | __field(u8, opcode) | ||
| 66 | __field(u8, lmac) | ||
| 67 | __field(u8, resp) | ||
| 68 | __field(u8, eot) | ||
| 69 | __field(u8, ra_tid) | ||
| 70 | __field(u8, credit_group) | ||
| 71 | __field(u8, color) | ||
| 72 | __field(u16, seq) | ||
| 73 | ), | ||
| 74 | |||
| 75 | TP_fast_assign( | ||
| 76 | IWM_ASSIGN; | ||
| 77 | __entry->opcode = hdr->sw_hdr.cmd.cmd; | ||
| 78 | __entry->lmac = 0; | ||
| 79 | __entry->seq = __le16_to_cpu(hdr->sw_hdr.cmd.seq_num); | ||
| 80 | __entry->resp = GET_VAL8(hdr->sw_hdr.cmd.flags, UMAC_DEV_CMD_FLAGS_RESP_REQ); | ||
| 81 | __entry->color = GET_VAL32(hdr->sw_hdr.meta_data, UMAC_FW_CMD_TX_STA_COLOR); | ||
| 82 | __entry->eot = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_OUT_CMD_EOT); | ||
| 83 | __entry->ra_tid = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_RATID); | ||
| 84 | __entry->credit_group = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_CREDIT_GRP); | ||
| 85 | if (__entry->opcode == UMAC_CMD_OPCODE_WIFI_PASS_THROUGH || | ||
| 86 | __entry->opcode == UMAC_CMD_OPCODE_WIFI_IF_WRAPPER) { | ||
| 87 | __entry->lmac = 1; | ||
| 88 | __entry->opcode = ((struct iwm_lmac_hdr *)(hdr + 1))->id; | ||
| 89 | } | ||
| 90 | ), | ||
| 91 | |||
| 92 | TP_printk( | ||
| 93 | IWM_PR_FMT " Tx %cMAC CMD: opcode 0x%x, resp %d, eot %d, " | ||
| 94 | "seq 0x%x, sta_color 0x%x, ra_tid 0x%x, credit_group 0x%x", | ||
| 95 | IWM_PR_ARG, __entry->lmac ? 'L' : 'U', __entry->opcode, | ||
| 96 | __entry->resp, __entry->eot, __entry->seq, __entry->color, | ||
| 97 | __entry->ra_tid, __entry->credit_group | ||
| 98 | ) | ||
| 99 | ); | ||
| 100 | |||
| 101 | TRACE_EVENT(iwm_tx_packets, | ||
| 102 | TP_PROTO(struct iwm_priv *iwm, u8 *buf, int len), | ||
| 103 | |||
| 104 | TP_ARGS(iwm, buf, len), | ||
| 105 | |||
| 106 | TP_STRUCT__entry( | ||
| 107 | IWM_ENTRY | ||
| 108 | __field(u8, eot) | ||
| 109 | __field(u8, ra_tid) | ||
| 110 | __field(u8, credit_group) | ||
| 111 | __field(u8, color) | ||
| 112 | __field(u16, seq) | ||
| 113 | __field(u8, npkt) | ||
| 114 | __field(u32, bytes) | ||
| 115 | ), | ||
| 116 | |||
| 117 | TP_fast_assign( | ||
| 118 | struct iwm_umac_wifi_out_hdr *hdr = | ||
| 119 | (struct iwm_umac_wifi_out_hdr *)buf; | ||
| 120 | |||
| 121 | IWM_ASSIGN; | ||
| 122 | __entry->eot = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_OUT_CMD_EOT); | ||
| 123 | __entry->ra_tid = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_RATID); | ||
| 124 | __entry->credit_group = GET_VAL32(hdr->hw_hdr.meta_data, UMAC_HDI_OUT_CREDIT_GRP); | ||
| 125 | __entry->color = GET_VAL32(hdr->sw_hdr.meta_data, UMAC_FW_CMD_TX_STA_COLOR); | ||
| 126 | __entry->seq = __le16_to_cpu(hdr->sw_hdr.cmd.seq_num); | ||
| 127 | __entry->npkt = 1; | ||
| 128 | __entry->bytes = len; | ||
| 129 | |||
| 130 | if (!__entry->eot) { | ||
| 131 | int count; | ||
| 132 | u8 *ptr = buf; | ||
| 133 | |||
| 134 | __entry->npkt = 0; | ||
| 135 | while (ptr < buf + len) { | ||
| 136 | count = GET_VAL32(hdr->sw_hdr.meta_data, | ||
| 137 | UMAC_FW_CMD_BYTE_COUNT); | ||
| 138 | ptr += ALIGN(sizeof(*hdr) + count, 16); | ||
| 139 | hdr = (struct iwm_umac_wifi_out_hdr *)ptr; | ||
| 140 | __entry->npkt++; | ||
| 141 | } | ||
| 142 | } | ||
| 143 | ), | ||
| 144 | |||
| 145 | TP_printk( | ||
| 146 | IWM_PR_FMT " Tx %spacket: eot %d, seq 0x%x, sta_color 0x%x, " | ||
| 147 | "ra_tid 0x%x, credit_group 0x%x, embeded_packets %d, %d bytes", | ||
| 148 | IWM_PR_ARG, !__entry->eot ? "concatenated " : "", | ||
| 149 | __entry->eot, __entry->seq, __entry->color, __entry->ra_tid, | ||
| 150 | __entry->credit_group, __entry->npkt, __entry->bytes | ||
| 151 | ) | ||
| 152 | ); | ||
| 153 | |||
| 154 | TRACE_EVENT(iwm_rx_nonwifi_cmd, | ||
| 155 | TP_PROTO(struct iwm_priv *iwm, void *buf, int len), | ||
| 156 | |||
| 157 | TP_ARGS(iwm, buf, len), | ||
| 158 | |||
| 159 | TP_STRUCT__entry( | ||
| 160 | IWM_ENTRY | ||
| 161 | __field(u8, opcode) | ||
| 162 | __field(u16, seq) | ||
| 163 | __field(u32, len) | ||
| 164 | ), | ||
| 165 | |||
| 166 | TP_fast_assign( | ||
| 167 | struct iwm_udma_in_hdr *hdr = buf; | ||
| 168 | |||
| 169 | IWM_ASSIGN; | ||
| 170 | __entry->opcode = GET_VAL32(hdr->cmd, UDMA_HDI_IN_NW_CMD_OPCODE); | ||
| 171 | __entry->seq = GET_VAL32(hdr->cmd, UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM); | ||
| 172 | __entry->len = len; | ||
| 173 | ), | ||
| 174 | |||
| 175 | TP_printk( | ||
| 176 | IWM_PR_FMT " Rx TARGET RESP: opcode 0x%x, seq 0x%x, len 0x%x", | ||
| 177 | IWM_PR_ARG, __entry->opcode, __entry->seq, __entry->len | ||
| 178 | ) | ||
| 179 | ); | ||
| 180 | |||
| 181 | TRACE_EVENT(iwm_rx_wifi_cmd, | ||
| 182 | TP_PROTO(struct iwm_priv *iwm, struct iwm_umac_wifi_in_hdr *hdr), | ||
| 183 | |||
| 184 | TP_ARGS(iwm, hdr), | ||
| 185 | |||
| 186 | TP_STRUCT__entry( | ||
| 187 | IWM_ENTRY | ||
| 188 | __field(u8, cmd) | ||
| 189 | __field(u8, source) | ||
| 190 | __field(u16, seq) | ||
| 191 | __field(u32, count) | ||
| 192 | ), | ||
| 193 | |||
| 194 | TP_fast_assign( | ||
| 195 | IWM_ASSIGN; | ||
| 196 | __entry->cmd = hdr->sw_hdr.cmd.cmd; | ||
| 197 | __entry->source = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE); | ||
| 198 | __entry->count = GET_VAL32(hdr->sw_hdr.meta_data, UMAC_FW_CMD_BYTE_COUNT); | ||
| 199 | __entry->seq = le16_to_cpu(hdr->sw_hdr.cmd.seq_num); | ||
| 200 | ), | ||
| 201 | |||
| 202 | TP_printk( | ||
| 203 | IWM_PR_FMT " Rx %s RESP: cmd 0x%x, seq 0x%x, count 0x%x", | ||
| 204 | IWM_PR_ARG, __entry->source == UMAC_HDI_IN_SOURCE_FHRX ? "LMAC" : | ||
| 205 | __entry->source == UMAC_HDI_IN_SOURCE_FW ? "UMAC" : "UDMA", | ||
| 206 | __entry->cmd, __entry->seq, __entry->count | ||
| 207 | ) | ||
| 208 | ); | ||
| 209 | |||
| 210 | #define iwm_ticket_action_symbol \ | ||
| 211 | { IWM_RX_TICKET_DROP, "DROP" }, \ | ||
| 212 | { IWM_RX_TICKET_RELEASE, "RELEASE" }, \ | ||
| 213 | { IWM_RX_TICKET_SNIFFER, "SNIFFER" }, \ | ||
| 214 | { IWM_RX_TICKET_ENQUEUE, "ENQUEUE" } | ||
| 215 | |||
| 216 | TRACE_EVENT(iwm_rx_ticket, | ||
| 217 | TP_PROTO(struct iwm_priv *iwm, void *buf, int len), | ||
| 218 | |||
| 219 | TP_ARGS(iwm, buf, len), | ||
| 220 | |||
| 221 | TP_STRUCT__entry( | ||
| 222 | IWM_ENTRY | ||
| 223 | __field(u8, action) | ||
| 224 | __field(u8, reason) | ||
| 225 | __field(u16, id) | ||
| 226 | __field(u16, flags) | ||
| 227 | ), | ||
| 228 | |||
| 229 | TP_fast_assign( | ||
| 230 | struct iwm_rx_ticket *ticket = | ||
| 231 | ((struct iwm_umac_notif_rx_ticket *)buf)->tickets; | ||
| 232 | |||
| 233 | IWM_ASSIGN; | ||
| 234 | __entry->id = le16_to_cpu(ticket->id); | ||
| 235 | __entry->action = le16_to_cpu(ticket->action); | ||
| 236 | __entry->flags = le16_to_cpu(ticket->flags); | ||
| 237 | __entry->reason = (__entry->flags & IWM_RX_TICKET_DROP_REASON_MSK) >> IWM_RX_TICKET_DROP_REASON_POS; | ||
| 238 | ), | ||
| 239 | |||
| 240 | TP_printk( | ||
| 241 | IWM_PR_FMT " Rx ticket: id 0x%x, action %s, %s 0x%x%s", | ||
| 242 | IWM_PR_ARG, __entry->id, | ||
| 243 | __print_symbolic(__entry->action, iwm_ticket_action_symbol), | ||
| 244 | __entry->reason ? "reason" : "flags", | ||
| 245 | __entry->reason ? __entry->reason : __entry->flags, | ||
| 246 | __entry->flags & IWM_RX_TICKET_AMSDU_MSK ? ", AMSDU frame" : "" | ||
| 247 | ) | ||
| 248 | ); | ||
| 249 | |||
| 250 | TRACE_EVENT(iwm_rx_packet, | ||
| 251 | TP_PROTO(struct iwm_priv *iwm, void *buf, int len), | ||
| 252 | |||
| 253 | TP_ARGS(iwm, buf, len), | ||
| 254 | |||
| 255 | TP_STRUCT__entry( | ||
| 256 | IWM_ENTRY | ||
| 257 | __field(u8, source) | ||
| 258 | __field(u16, id) | ||
| 259 | __field(u32, len) | ||
| 260 | ), | ||
| 261 | |||
| 262 | TP_fast_assign( | ||
| 263 | struct iwm_umac_wifi_in_hdr *hdr = buf; | ||
| 264 | |||
| 265 | IWM_ASSIGN; | ||
| 266 | __entry->source = GET_VAL32(hdr->hw_hdr.cmd, UMAC_HDI_IN_CMD_SOURCE); | ||
| 267 | __entry->id = le16_to_cpu(hdr->sw_hdr.cmd.seq_num); | ||
| 268 | __entry->len = len - sizeof(*hdr); | ||
| 269 | ), | ||
| 270 | |||
| 271 | TP_printk( | ||
| 272 | IWM_PR_FMT " Rx %s packet: id 0x%x, %d bytes", | ||
| 273 | IWM_PR_ARG, __entry->source == UMAC_HDI_IN_SOURCE_FHRX ? | ||
| 274 | "LMAC" : "UMAC", __entry->id, __entry->len | ||
| 275 | ) | ||
| 276 | ); | ||
| 277 | #endif | ||
| 278 | |||
| 279 | #undef TRACE_INCLUDE_PATH | ||
| 280 | #define TRACE_INCLUDE_PATH . | ||
| 281 | #undef TRACE_INCLUDE_FILE | ||
| 282 | #define TRACE_INCLUDE_FILE trace | ||
| 283 | #include <trace/define_trace.h> | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/tx.c b/drivers/net/wireless/iwmc3200wifi/tx.c new file mode 100644 index 00000000000..be98074c060 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/tx.c | |||
| @@ -0,0 +1,529 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | /* | ||
| 40 | * iwm Tx theory of operation: | ||
| 41 | * | ||
| 42 | * 1) We receive a 802.3 frame from the stack | ||
| 43 | * 2) We convert it to a 802.11 frame [iwm_xmit_frame] | ||
| 44 | * 3) We queue it to its corresponding tx queue [iwm_xmit_frame] | ||
| 45 | * 4) We schedule the tx worker. There is one worker per tx | ||
| 46 | * queue. [iwm_xmit_frame] | ||
| 47 | * 5) The tx worker is scheduled | ||
| 48 | * 6) We go through every queued skb on the tx queue, and for each | ||
| 49 | * and every one of them: [iwm_tx_worker] | ||
| 50 | * a) We check if we have enough Tx credits (see below for a Tx | ||
| 51 | * credits description) for the frame length. [iwm_tx_worker] | ||
| 52 | * b) If we do, we aggregate the Tx frame into a UDMA one, by | ||
| 53 | * concatenating one REPLY_TX command per Tx frame. [iwm_tx_worker] | ||
| 54 | * c) When we run out of credits, or when we reach the maximum | ||
| 55 | * concatenation size, we actually send the concatenated UDMA | ||
| 56 | * frame. [iwm_tx_worker] | ||
| 57 | * | ||
| 58 | * When we run out of Tx credits, the skbs are filling the tx queue, | ||
| 59 | * and eventually we will stop the netdev queue. [iwm_tx_worker] | ||
| 60 | * The tx queue is emptied as we're getting new tx credits, by | ||
| 61 | * scheduling the tx_worker. [iwm_tx_credit_inc] | ||
| 62 | * The netdev queue is started again when we have enough tx credits, | ||
| 63 | * and when our tx queue has some reasonable amout of space available | ||
| 64 | * (i.e. half of the max size). [iwm_tx_worker] | ||
| 65 | */ | ||
| 66 | |||
| 67 | #include <linux/slab.h> | ||
| 68 | #include <linux/skbuff.h> | ||
| 69 | #include <linux/netdevice.h> | ||
| 70 | #include <linux/ieee80211.h> | ||
| 71 | |||
| 72 | #include "iwm.h" | ||
| 73 | #include "debug.h" | ||
| 74 | #include "commands.h" | ||
| 75 | #include "hal.h" | ||
| 76 | #include "umac.h" | ||
| 77 | #include "bus.h" | ||
| 78 | |||
| 79 | #define IWM_UMAC_PAGE_ALLOC_WRAP 0xffff | ||
| 80 | |||
| 81 | #define BYTES_TO_PAGES(n) (1 + ((n) >> ilog2(IWM_UMAC_PAGE_SIZE)) - \ | ||
| 82 | (((n) & (IWM_UMAC_PAGE_SIZE - 1)) == 0)) | ||
| 83 | |||
| 84 | #define pool_id_to_queue(id) ((id < IWM_TX_CMD_QUEUE) ? id : id - 1) | ||
| 85 | #define queue_to_pool_id(q) ((q < IWM_TX_CMD_QUEUE) ? q : q + 1) | ||
| 86 | |||
| 87 | /* require to hold tx_credit lock */ | ||
| 88 | static int iwm_tx_credit_get(struct iwm_tx_credit *tx_credit, int id) | ||
| 89 | { | ||
| 90 | struct pool_entry *pool = &tx_credit->pools[id]; | ||
| 91 | struct spool_entry *spool = &tx_credit->spools[pool->sid]; | ||
| 92 | int spool_pages; | ||
| 93 | |||
| 94 | /* number of pages can be taken from spool by this pool */ | ||
| 95 | spool_pages = spool->max_pages - spool->alloc_pages + | ||
| 96 | max(pool->min_pages - pool->alloc_pages, 0); | ||
| 97 | |||
| 98 | return min(pool->max_pages - pool->alloc_pages, spool_pages); | ||
| 99 | } | ||
| 100 | |||
| 101 | static bool iwm_tx_credit_ok(struct iwm_priv *iwm, int id, int nb) | ||
| 102 | { | ||
| 103 | u32 npages = BYTES_TO_PAGES(nb); | ||
| 104 | |||
| 105 | if (npages <= iwm_tx_credit_get(&iwm->tx_credit, id)) | ||
| 106 | return 1; | ||
| 107 | |||
| 108 | set_bit(id, &iwm->tx_credit.full_pools_map); | ||
| 109 | |||
| 110 | IWM_DBG_TX(iwm, DBG, "LINK: stop txq[%d], available credit: %d\n", | ||
| 111 | pool_id_to_queue(id), | ||
| 112 | iwm_tx_credit_get(&iwm->tx_credit, id)); | ||
| 113 | |||
| 114 | return 0; | ||
| 115 | } | ||
| 116 | |||
| 117 | void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages) | ||
| 118 | { | ||
| 119 | struct pool_entry *pool; | ||
| 120 | struct spool_entry *spool; | ||
| 121 | int freed_pages; | ||
| 122 | int queue; | ||
| 123 | |||
| 124 | BUG_ON(id >= IWM_MACS_OUT_GROUPS); | ||
| 125 | |||
| 126 | pool = &iwm->tx_credit.pools[id]; | ||
| 127 | spool = &iwm->tx_credit.spools[pool->sid]; | ||
| 128 | |||
| 129 | freed_pages = total_freed_pages - pool->total_freed_pages; | ||
| 130 | IWM_DBG_TX(iwm, DBG, "Free %d pages for pool[%d]\n", freed_pages, id); | ||
| 131 | |||
| 132 | if (!freed_pages) { | ||
| 133 | IWM_DBG_TX(iwm, DBG, "No pages are freed by UMAC\n"); | ||
| 134 | return; | ||
| 135 | } else if (freed_pages < 0) | ||
| 136 | freed_pages += IWM_UMAC_PAGE_ALLOC_WRAP + 1; | ||
| 137 | |||
| 138 | if (pool->alloc_pages > pool->min_pages) { | ||
| 139 | int spool_pages = pool->alloc_pages - pool->min_pages; | ||
| 140 | spool_pages = min(spool_pages, freed_pages); | ||
| 141 | spool->alloc_pages -= spool_pages; | ||
| 142 | } | ||
| 143 | |||
| 144 | pool->alloc_pages -= freed_pages; | ||
| 145 | pool->total_freed_pages = total_freed_pages; | ||
| 146 | |||
| 147 | IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, " | ||
| 148 | "Spool[%d] pages alloc: %d\n", id, pool->alloc_pages, | ||
| 149 | pool->total_freed_pages, pool->sid, spool->alloc_pages); | ||
| 150 | |||
| 151 | if (test_bit(id, &iwm->tx_credit.full_pools_map) && | ||
| 152 | (pool->alloc_pages < pool->max_pages / 2)) { | ||
| 153 | clear_bit(id, &iwm->tx_credit.full_pools_map); | ||
| 154 | |||
| 155 | queue = pool_id_to_queue(id); | ||
| 156 | |||
| 157 | IWM_DBG_TX(iwm, DBG, "LINK: start txq[%d], available " | ||
| 158 | "credit: %d\n", queue, | ||
| 159 | iwm_tx_credit_get(&iwm->tx_credit, id)); | ||
| 160 | queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); | ||
| 161 | } | ||
| 162 | } | ||
| 163 | |||
| 164 | static void iwm_tx_credit_dec(struct iwm_priv *iwm, int id, int alloc_pages) | ||
| 165 | { | ||
| 166 | struct pool_entry *pool; | ||
| 167 | struct spool_entry *spool; | ||
| 168 | int spool_pages; | ||
| 169 | |||
| 170 | IWM_DBG_TX(iwm, DBG, "Allocate %d pages for pool[%d]\n", | ||
| 171 | alloc_pages, id); | ||
| 172 | |||
| 173 | BUG_ON(id >= IWM_MACS_OUT_GROUPS); | ||
| 174 | |||
| 175 | pool = &iwm->tx_credit.pools[id]; | ||
| 176 | spool = &iwm->tx_credit.spools[pool->sid]; | ||
| 177 | |||
| 178 | spool_pages = pool->alloc_pages + alloc_pages - pool->min_pages; | ||
| 179 | |||
| 180 | if (pool->alloc_pages >= pool->min_pages) | ||
| 181 | spool->alloc_pages += alloc_pages; | ||
| 182 | else if (spool_pages > 0) | ||
| 183 | spool->alloc_pages += spool_pages; | ||
| 184 | |||
| 185 | pool->alloc_pages += alloc_pages; | ||
| 186 | |||
| 187 | IWM_DBG_TX(iwm, DBG, "Pool[%d] pages alloc: %d, total_freed: %d, " | ||
| 188 | "Spool[%d] pages alloc: %d\n", id, pool->alloc_pages, | ||
| 189 | pool->total_freed_pages, pool->sid, spool->alloc_pages); | ||
| 190 | } | ||
| 191 | |||
| 192 | int iwm_tx_credit_alloc(struct iwm_priv *iwm, int id, int nb) | ||
| 193 | { | ||
| 194 | u32 npages = BYTES_TO_PAGES(nb); | ||
| 195 | int ret = 0; | ||
| 196 | |||
| 197 | spin_lock(&iwm->tx_credit.lock); | ||
| 198 | |||
| 199 | if (!iwm_tx_credit_ok(iwm, id, nb)) { | ||
| 200 | IWM_DBG_TX(iwm, DBG, "No credit available for pool[%d]\n", id); | ||
| 201 | ret = -ENOSPC; | ||
| 202 | goto out; | ||
| 203 | } | ||
| 204 | |||
| 205 | iwm_tx_credit_dec(iwm, id, npages); | ||
| 206 | |||
| 207 | out: | ||
| 208 | spin_unlock(&iwm->tx_credit.lock); | ||
| 209 | return ret; | ||
| 210 | } | ||
| 211 | |||
| 212 | /* | ||
| 213 | * Since we're on an SDIO or USB bus, we are not sharing memory | ||
| 214 | * for storing to be transmitted frames. The host needs to push | ||
| 215 | * them upstream. As a consequence there needs to be a way for | ||
| 216 | * the target to let us know if it can actually take more TX frames | ||
| 217 | * or not. This is what Tx credits are for. | ||
| 218 | * | ||
| 219 | * For each Tx HW queue, we have a Tx pool, and then we have one | ||
| 220 | * unique super pool (spool), which is actually a global pool of | ||
| 221 | * all the UMAC pages. | ||
| 222 | * For each Tx pool we have a min_pages, a max_pages fields, and a | ||
| 223 | * alloc_pages fields. The alloc_pages tracks the number of pages | ||
| 224 | * currently allocated from the tx pool. | ||
| 225 | * Here are the rules to check if given a tx frame we have enough | ||
| 226 | * tx credits for it: | ||
| 227 | * 1) We translate the frame length into a number of UMAC pages. | ||
| 228 | * Let's call them n_pages. | ||
| 229 | * 2) For the corresponding tx pool, we check if n_pages + | ||
| 230 | * pool->alloc_pages is higher than pool->min_pages. min_pages | ||
| 231 | * represent a set of pre-allocated pages on the tx pool. If | ||
| 232 | * that's the case, then we need to allocate those pages from | ||
| 233 | * the spool. We can do so until we reach spool->max_pages. | ||
| 234 | * 3) Each tx pool is not allowed to allocate more than pool->max_pages | ||
| 235 | * from the spool, so once we're over min_pages, we can allocate | ||
| 236 | * pages from the spool, but not more than max_pages. | ||
| 237 | * | ||
| 238 | * When the tx code path needs to send a tx frame, it checks first | ||
| 239 | * if it has enough tx credits, following those rules. [iwm_tx_credit_get] | ||
| 240 | * If it does, it then updates the pool and spool counters and | ||
| 241 | * then send the frame. [iwm_tx_credit_alloc and iwm_tx_credit_dec] | ||
| 242 | * On the other side, when the UMAC is done transmitting frames, it | ||
| 243 | * will send a credit update notification to the host. This is when | ||
| 244 | * the pool and spool counters gets to be decreased. [iwm_tx_credit_inc, | ||
| 245 | * called from rx.c:iwm_ntf_tx_credit_update] | ||
| 246 | * | ||
| 247 | */ | ||
| 248 | void iwm_tx_credit_init_pools(struct iwm_priv *iwm, | ||
| 249 | struct iwm_umac_notif_alive *alive) | ||
| 250 | { | ||
| 251 | int i, sid, pool_pages; | ||
| 252 | |||
| 253 | spin_lock(&iwm->tx_credit.lock); | ||
| 254 | |||
| 255 | iwm->tx_credit.pool_nr = le16_to_cpu(alive->page_grp_count); | ||
| 256 | iwm->tx_credit.full_pools_map = 0; | ||
| 257 | memset(&iwm->tx_credit.spools[0], 0, sizeof(struct spool_entry)); | ||
| 258 | |||
| 259 | IWM_DBG_TX(iwm, DBG, "Pools number is %d\n", iwm->tx_credit.pool_nr); | ||
| 260 | |||
| 261 | for (i = 0; i < iwm->tx_credit.pool_nr; i++) { | ||
| 262 | __le32 page_grp_state = alive->page_grp_state[i]; | ||
| 263 | |||
| 264 | iwm->tx_credit.pools[i].id = GET_VAL32(page_grp_state, | ||
| 265 | UMAC_ALIVE_PAGE_STS_GRP_NUM); | ||
| 266 | iwm->tx_credit.pools[i].sid = GET_VAL32(page_grp_state, | ||
| 267 | UMAC_ALIVE_PAGE_STS_SGRP_NUM); | ||
| 268 | iwm->tx_credit.pools[i].min_pages = GET_VAL32(page_grp_state, | ||
| 269 | UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE); | ||
| 270 | iwm->tx_credit.pools[i].max_pages = GET_VAL32(page_grp_state, | ||
| 271 | UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE); | ||
| 272 | iwm->tx_credit.pools[i].alloc_pages = 0; | ||
| 273 | iwm->tx_credit.pools[i].total_freed_pages = 0; | ||
| 274 | |||
| 275 | sid = iwm->tx_credit.pools[i].sid; | ||
| 276 | pool_pages = iwm->tx_credit.pools[i].min_pages; | ||
| 277 | |||
| 278 | if (iwm->tx_credit.spools[sid].max_pages == 0) { | ||
| 279 | iwm->tx_credit.spools[sid].id = sid; | ||
| 280 | iwm->tx_credit.spools[sid].max_pages = | ||
| 281 | GET_VAL32(page_grp_state, | ||
| 282 | UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE); | ||
| 283 | iwm->tx_credit.spools[sid].alloc_pages = 0; | ||
| 284 | } | ||
| 285 | |||
| 286 | iwm->tx_credit.spools[sid].alloc_pages += pool_pages; | ||
| 287 | |||
| 288 | IWM_DBG_TX(iwm, DBG, "Pool idx: %d, id: %d, sid: %d, capacity " | ||
| 289 | "min: %d, max: %d, pool alloc: %d, total_free: %d, " | ||
| 290 | "super poll alloc: %d\n", | ||
| 291 | i, iwm->tx_credit.pools[i].id, | ||
| 292 | iwm->tx_credit.pools[i].sid, | ||
| 293 | iwm->tx_credit.pools[i].min_pages, | ||
| 294 | iwm->tx_credit.pools[i].max_pages, | ||
| 295 | iwm->tx_credit.pools[i].alloc_pages, | ||
| 296 | iwm->tx_credit.pools[i].total_freed_pages, | ||
| 297 | iwm->tx_credit.spools[sid].alloc_pages); | ||
| 298 | } | ||
| 299 | |||
| 300 | spin_unlock(&iwm->tx_credit.lock); | ||
| 301 | } | ||
| 302 | |||
| 303 | #define IWM_UDMA_HDR_LEN sizeof(struct iwm_umac_wifi_out_hdr) | ||
| 304 | |||
| 305 | static __le16 iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb, | ||
| 306 | int pool_id, u8 *buf) | ||
| 307 | { | ||
| 308 | struct iwm_umac_wifi_out_hdr *hdr = (struct iwm_umac_wifi_out_hdr *)buf; | ||
| 309 | struct iwm_udma_wifi_cmd udma_cmd; | ||
| 310 | struct iwm_umac_cmd umac_cmd; | ||
| 311 | struct iwm_tx_info *tx_info = skb_to_tx_info(skb); | ||
| 312 | |||
| 313 | udma_cmd.count = cpu_to_le16(skb->len + | ||
| 314 | sizeof(struct iwm_umac_fw_cmd_hdr)); | ||
| 315 | /* set EOP to 0 here. iwm_udma_wifi_hdr_set_eop() will be | ||
| 316 | * called later to set EOP for the last packet. */ | ||
| 317 | udma_cmd.eop = 0; | ||
| 318 | udma_cmd.credit_group = pool_id; | ||
| 319 | udma_cmd.ra_tid = tx_info->sta << 4 | tx_info->tid; | ||
| 320 | udma_cmd.lmac_offset = 0; | ||
| 321 | |||
| 322 | umac_cmd.id = REPLY_TX; | ||
| 323 | umac_cmd.count = cpu_to_le16(skb->len); | ||
| 324 | umac_cmd.color = tx_info->color; | ||
| 325 | umac_cmd.resp = 0; | ||
| 326 | umac_cmd.seq_num = cpu_to_le16(iwm_alloc_wifi_cmd_seq(iwm)); | ||
| 327 | |||
| 328 | iwm_build_udma_wifi_hdr(iwm, &hdr->hw_hdr, &udma_cmd); | ||
| 329 | iwm_build_umac_hdr(iwm, &hdr->sw_hdr, &umac_cmd); | ||
| 330 | |||
| 331 | memcpy(buf + sizeof(*hdr), skb->data, skb->len); | ||
| 332 | |||
| 333 | return umac_cmd.seq_num; | ||
| 334 | } | ||
| 335 | |||
| 336 | static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, | ||
| 337 | struct iwm_tx_queue *txq) | ||
| 338 | { | ||
| 339 | int ret; | ||
| 340 | |||
| 341 | if (!txq->concat_count) | ||
| 342 | return 0; | ||
| 343 | |||
| 344 | IWM_DBG_TX(iwm, DBG, "Send concatenated Tx: queue %d, %d bytes\n", | ||
| 345 | txq->id, txq->concat_count); | ||
| 346 | |||
| 347 | /* mark EOP for the last packet */ | ||
| 348 | iwm_udma_wifi_hdr_set_eop(iwm, txq->concat_ptr, 1); | ||
| 349 | |||
| 350 | trace_iwm_tx_packets(iwm, txq->concat_buf, txq->concat_count); | ||
| 351 | ret = iwm_bus_send_chunk(iwm, txq->concat_buf, txq->concat_count); | ||
| 352 | |||
| 353 | txq->concat_count = 0; | ||
| 354 | txq->concat_ptr = txq->concat_buf; | ||
| 355 | |||
| 356 | return ret; | ||
| 357 | } | ||
| 358 | |||
| 359 | void iwm_tx_worker(struct work_struct *work) | ||
| 360 | { | ||
| 361 | struct iwm_priv *iwm; | ||
| 362 | struct iwm_tx_info *tx_info = NULL; | ||
| 363 | struct sk_buff *skb; | ||
| 364 | struct iwm_tx_queue *txq; | ||
| 365 | struct iwm_sta_info *sta_info; | ||
| 366 | struct iwm_tid_info *tid_info; | ||
| 367 | int cmdlen, ret, pool_id; | ||
| 368 | |||
| 369 | txq = container_of(work, struct iwm_tx_queue, worker); | ||
| 370 | iwm = container_of(txq, struct iwm_priv, txq[txq->id]); | ||
| 371 | |||
| 372 | pool_id = queue_to_pool_id(txq->id); | ||
| 373 | |||
| 374 | while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) && | ||
| 375 | !skb_queue_empty(&txq->queue)) { | ||
| 376 | |||
| 377 | spin_lock_bh(&txq->lock); | ||
| 378 | skb = skb_dequeue(&txq->queue); | ||
| 379 | spin_unlock_bh(&txq->lock); | ||
| 380 | |||
| 381 | tx_info = skb_to_tx_info(skb); | ||
| 382 | sta_info = &iwm->sta_table[tx_info->sta]; | ||
| 383 | if (!sta_info->valid) { | ||
| 384 | IWM_ERR(iwm, "Trying to send a frame to unknown STA\n"); | ||
| 385 | kfree_skb(skb); | ||
| 386 | continue; | ||
| 387 | } | ||
| 388 | |||
| 389 | tid_info = &sta_info->tid_info[tx_info->tid]; | ||
| 390 | |||
| 391 | mutex_lock(&tid_info->mutex); | ||
| 392 | |||
| 393 | /* | ||
| 394 | * If the RAxTID is stopped, we queue the skb to the stopped | ||
| 395 | * queue. | ||
| 396 | * Whenever we'll get a UMAC notification to resume the tx flow | ||
| 397 | * for this RAxTID, we'll merge back the stopped queue into the | ||
| 398 | * regular queue. See iwm_ntf_stop_resume_tx() from rx.c. | ||
| 399 | */ | ||
| 400 | if (tid_info->stopped) { | ||
| 401 | IWM_DBG_TX(iwm, DBG, "%dx%d stopped\n", | ||
| 402 | tx_info->sta, tx_info->tid); | ||
| 403 | spin_lock_bh(&txq->lock); | ||
| 404 | skb_queue_tail(&txq->stopped_queue, skb); | ||
| 405 | spin_unlock_bh(&txq->lock); | ||
| 406 | |||
| 407 | mutex_unlock(&tid_info->mutex); | ||
| 408 | continue; | ||
| 409 | } | ||
| 410 | |||
| 411 | cmdlen = IWM_UDMA_HDR_LEN + skb->len; | ||
| 412 | |||
| 413 | IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: " | ||
| 414 | "%d, color: %d\n", txq->id, skb, tx_info->sta, | ||
| 415 | tx_info->color); | ||
| 416 | |||
| 417 | if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE) | ||
| 418 | iwm_tx_send_concat_packets(iwm, txq); | ||
| 419 | |||
| 420 | ret = iwm_tx_credit_alloc(iwm, pool_id, cmdlen); | ||
| 421 | if (ret) { | ||
| 422 | IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue " | ||
| 423 | "%d, Tx worker stopped\n", txq->id); | ||
| 424 | spin_lock_bh(&txq->lock); | ||
| 425 | skb_queue_head(&txq->queue, skb); | ||
| 426 | spin_unlock_bh(&txq->lock); | ||
| 427 | |||
| 428 | mutex_unlock(&tid_info->mutex); | ||
| 429 | break; | ||
| 430 | } | ||
| 431 | |||
| 432 | txq->concat_ptr = txq->concat_buf + txq->concat_count; | ||
| 433 | tid_info->last_seq_num = | ||
| 434 | iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr); | ||
| 435 | txq->concat_count += ALIGN(cmdlen, 16); | ||
| 436 | |||
| 437 | mutex_unlock(&tid_info->mutex); | ||
| 438 | |||
| 439 | kfree_skb(skb); | ||
| 440 | } | ||
| 441 | |||
| 442 | iwm_tx_send_concat_packets(iwm, txq); | ||
| 443 | |||
| 444 | if (__netif_subqueue_stopped(iwm_to_ndev(iwm), txq->id) && | ||
| 445 | !test_bit(pool_id, &iwm->tx_credit.full_pools_map) && | ||
| 446 | (skb_queue_len(&txq->queue) < IWM_TX_LIST_SIZE / 2)) { | ||
| 447 | IWM_DBG_TX(iwm, DBG, "LINK: start netif_subqueue[%d]", txq->id); | ||
| 448 | netif_wake_subqueue(iwm_to_ndev(iwm), txq->id); | ||
| 449 | } | ||
| 450 | } | ||
| 451 | |||
| 452 | int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) | ||
| 453 | { | ||
| 454 | struct iwm_priv *iwm = ndev_to_iwm(netdev); | ||
| 455 | struct wireless_dev *wdev = iwm_to_wdev(iwm); | ||
| 456 | struct iwm_tx_info *tx_info; | ||
| 457 | struct iwm_tx_queue *txq; | ||
| 458 | struct iwm_sta_info *sta_info; | ||
| 459 | u8 *dst_addr, sta_id; | ||
| 460 | u16 queue; | ||
| 461 | int ret; | ||
| 462 | |||
| 463 | |||
| 464 | if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { | ||
| 465 | IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: " | ||
| 466 | "not associated\n"); | ||
| 467 | netif_tx_stop_all_queues(netdev); | ||
| 468 | goto drop; | ||
| 469 | } | ||
| 470 | |||
| 471 | queue = skb_get_queue_mapping(skb); | ||
| 472 | BUG_ON(queue >= IWM_TX_DATA_QUEUES); /* no iPAN yet */ | ||
| 473 | |||
| 474 | txq = &iwm->txq[queue]; | ||
| 475 | |||
| 476 | /* No free space for Tx, tx_worker is too slow */ | ||
| 477 | if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) || | ||
| 478 | (skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) { | ||
| 479 | IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue); | ||
| 480 | netif_stop_subqueue(netdev, queue); | ||
| 481 | return NETDEV_TX_BUSY; | ||
| 482 | } | ||
| 483 | |||
| 484 | ret = ieee80211_data_from_8023(skb, netdev->dev_addr, wdev->iftype, | ||
| 485 | iwm->bssid, 0); | ||
| 486 | if (ret) { | ||
| 487 | IWM_ERR(iwm, "build wifi header failed\n"); | ||
| 488 | goto drop; | ||
| 489 | } | ||
| 490 | |||
| 491 | dst_addr = ((struct ieee80211_hdr *)(skb->data))->addr1; | ||
| 492 | |||
| 493 | for (sta_id = 0; sta_id < IWM_STA_TABLE_NUM; sta_id++) { | ||
| 494 | sta_info = &iwm->sta_table[sta_id]; | ||
| 495 | if (sta_info->valid && | ||
| 496 | !memcmp(dst_addr, sta_info->addr, ETH_ALEN)) | ||
| 497 | break; | ||
| 498 | } | ||
| 499 | |||
| 500 | if (sta_id == IWM_STA_TABLE_NUM) { | ||
| 501 | IWM_ERR(iwm, "STA %pM not found in sta_table, Tx ignored\n", | ||
| 502 | dst_addr); | ||
| 503 | goto drop; | ||
| 504 | } | ||
| 505 | |||
| 506 | tx_info = skb_to_tx_info(skb); | ||
| 507 | tx_info->sta = sta_id; | ||
| 508 | tx_info->color = sta_info->color; | ||
| 509 | /* UMAC uses TID 8 (vs. 0) for non QoS packets */ | ||
| 510 | if (sta_info->qos) | ||
| 511 | tx_info->tid = skb->priority; | ||
| 512 | else | ||
| 513 | tx_info->tid = IWM_UMAC_MGMT_TID; | ||
| 514 | |||
| 515 | spin_lock_bh(&iwm->txq[queue].lock); | ||
| 516 | skb_queue_tail(&iwm->txq[queue].queue, skb); | ||
| 517 | spin_unlock_bh(&iwm->txq[queue].lock); | ||
| 518 | |||
| 519 | queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); | ||
| 520 | |||
| 521 | netdev->stats.tx_packets++; | ||
| 522 | netdev->stats.tx_bytes += skb->len; | ||
| 523 | return NETDEV_TX_OK; | ||
| 524 | |||
| 525 | drop: | ||
| 526 | netdev->stats.tx_dropped++; | ||
| 527 | dev_kfree_skb_any(skb); | ||
| 528 | return NETDEV_TX_OK; | ||
| 529 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h new file mode 100644 index 00000000000..4a137d334a4 --- /dev/null +++ b/drivers/net/wireless/iwmc3200wifi/umac.h | |||
| @@ -0,0 +1,789 @@ | |||
| 1 | /* | ||
| 2 | * Intel Wireless Multicomm 3200 WiFi driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * | ||
| 10 | * * Redistributions of source code must retain the above copyright | ||
| 11 | * notice, this list of conditions and the following disclaimer. | ||
| 12 | * * Redistributions in binary form must reproduce the above copyright | ||
| 13 | * notice, this list of conditions and the following disclaimer in | ||
| 14 | * the documentation and/or other materials provided with the | ||
| 15 | * distribution. | ||
| 16 | * * Neither the name of Intel Corporation nor the names of its | ||
| 17 | * contributors may be used to endorse or promote products derived | ||
| 18 | * from this software without specific prior written permission. | ||
| 19 | * | ||
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| 22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| 23 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| 24 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| 25 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| 26 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| 27 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| 28 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| 29 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| 30 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| 31 | * | ||
| 32 | * | ||
| 33 | * Intel Corporation <ilw@linux.intel.com> | ||
| 34 | * Samuel Ortiz <samuel.ortiz@intel.com> | ||
| 35 | * Zhu Yi <yi.zhu@intel.com> | ||
| 36 | * | ||
| 37 | */ | ||
| 38 | |||
| 39 | #ifndef __IWM_UMAC_H__ | ||
| 40 | #define __IWM_UMAC_H__ | ||
| 41 | |||
| 42 | struct iwm_udma_in_hdr { | ||
| 43 | __le32 cmd; | ||
| 44 | __le32 size; | ||
| 45 | } __packed; | ||
| 46 | |||
| 47 | struct iwm_udma_out_nonwifi_hdr { | ||
| 48 | __le32 cmd; | ||
| 49 | __le32 addr; | ||
| 50 | __le32 op1_sz; | ||
| 51 | __le32 op2; | ||
| 52 | } __packed; | ||
| 53 | |||
| 54 | struct iwm_udma_out_wifi_hdr { | ||
| 55 | __le32 cmd; | ||
| 56 | __le32 meta_data; | ||
| 57 | } __packed; | ||
| 58 | |||
| 59 | /* Sequence numbering */ | ||
| 60 | #define UMAC_WIFI_SEQ_NUM_BASE 1 | ||
| 61 | #define UMAC_WIFI_SEQ_NUM_MAX 0x4000 | ||
| 62 | #define UMAC_NONWIFI_SEQ_NUM_BASE 1 | ||
| 63 | #define UMAC_NONWIFI_SEQ_NUM_MAX 0x10 | ||
| 64 | |||
| 65 | /* MAC address address */ | ||
| 66 | #define WICO_MAC_ADDRESS_ADDR 0x604008F8 | ||
| 67 | |||
| 68 | /* RA / TID */ | ||
| 69 | #define UMAC_HDI_ACT_TBL_IDX_TID_POS 0 | ||
| 70 | #define UMAC_HDI_ACT_TBL_IDX_TID_SEED 0xF | ||
| 71 | |||
| 72 | #define UMAC_HDI_ACT_TBL_IDX_RA_POS 4 | ||
| 73 | #define UMAC_HDI_ACT_TBL_IDX_RA_SEED 0xF | ||
| 74 | |||
| 75 | #define UMAC_HDI_ACT_TBL_IDX_RA_UMAC 0xF | ||
| 76 | #define UMAC_HDI_ACT_TBL_IDX_TID_UMAC 0x9 | ||
| 77 | #define UMAC_HDI_ACT_TBL_IDX_TID_LMAC 0xA | ||
| 78 | |||
| 79 | #define UMAC_HDI_ACT_TBL_IDX_HOST_CMD \ | ||
| 80 | ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\ | ||
| 81 | (UMAC_HDI_ACT_TBL_IDX_TID_UMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS)) | ||
| 82 | #define UMAC_HDI_ACT_TBL_IDX_UMAC_CMD \ | ||
| 83 | ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\ | ||
| 84 | (UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS)) | ||
| 85 | |||
| 86 | /* STA ID and color */ | ||
| 87 | #define STA_ID_SEED (0x0f) | ||
| 88 | #define STA_ID_POS (0) | ||
| 89 | #define STA_ID_MSK (STA_ID_SEED << STA_ID_POS) | ||
| 90 | |||
| 91 | #define STA_COLOR_SEED (0x7) | ||
| 92 | #define STA_COLOR_POS (4) | ||
| 93 | #define STA_COLOR_MSK (STA_COLOR_SEED << STA_COLOR_POS) | ||
| 94 | |||
| 95 | #define STA_ID_N_COLOR_COLOR(id_n_color) \ | ||
| 96 | (((id_n_color) & STA_COLOR_MSK) >> STA_COLOR_POS) | ||
| 97 | #define STA_ID_N_COLOR_ID(id_n_color) \ | ||
| 98 | (((id_n_color) & STA_ID_MSK) >> STA_ID_POS) | ||
| 99 | |||
| 100 | /* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */ | ||
| 101 | #define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0 | ||
| 102 | #define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF | ||
| 103 | |||
| 104 | /* iwm_umac_notif_alive.page_grp_state Super group number -- bits [7:4] */ | ||
| 105 | #define UMAC_ALIVE_PAGE_STS_SGRP_NUM_POS 4 | ||
| 106 | #define UMAC_ALIVE_PAGE_STS_SGRP_NUM_SEED 0xF | ||
| 107 | |||
| 108 | /* iwm_umac_notif_alive.page_grp_state Group min size -- bits [15:8] */ | ||
| 109 | #define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_POS 8 | ||
| 110 | #define UMAC_ALIVE_PAGE_STS_GRP_MIN_SIZE_SEED 0xFF | ||
| 111 | |||
| 112 | /* iwm_umac_notif_alive.page_grp_state Group max size -- bits [23:16] */ | ||
| 113 | #define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_POS 16 | ||
| 114 | #define UMAC_ALIVE_PAGE_STS_GRP_MAX_SIZE_SEED 0xFF | ||
| 115 | |||
| 116 | /* iwm_umac_notif_alive.page_grp_state Super group max size -- bits [31:24] */ | ||
| 117 | #define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_POS 24 | ||
| 118 | #define UMAC_ALIVE_PAGE_STS_SGRP_MAX_SIZE_SEED 0xFF | ||
| 119 | |||
| 120 | /* Barkers */ | ||
| 121 | #define UMAC_REBOOT_BARKER 0xdeadbeef | ||
| 122 | #define UMAC_ACK_BARKER 0xfeedbabe | ||
| 123 | #define UMAC_PAD_TERMINAL 0xadadadad | ||
| 124 | |||
| 125 | /* UMAC JMP address */ | ||
| 126 | #define UMAC_MU_FW_INST_DATA_12_ADDR 0xBF0000 | ||
| 127 | |||
| 128 | /* iwm_umac_hdi_out_hdr.cmd OP code -- bits [3:0] */ | ||
| 129 | #define UMAC_HDI_OUT_CMD_OPCODE_POS 0 | ||
| 130 | #define UMAC_HDI_OUT_CMD_OPCODE_SEED 0xF | ||
| 131 | |||
| 132 | /* iwm_umac_hdi_out_hdr.cmd End-Of-Transfer -- bits [10:10] */ | ||
| 133 | #define UMAC_HDI_OUT_CMD_EOT_POS 10 | ||
| 134 | #define UMAC_HDI_OUT_CMD_EOT_SEED 0x1 | ||
| 135 | |||
| 136 | /* iwm_umac_hdi_out_hdr.cmd UTFD only usage -- bits [11:11] */ | ||
| 137 | #define UMAC_HDI_OUT_CMD_UTFD_ONLY_POS 11 | ||
| 138 | #define UMAC_HDI_OUT_CMD_UTFD_ONLY_SEED 0x1 | ||
| 139 | |||
| 140 | /* iwm_umac_hdi_out_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */ | ||
| 141 | #define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_POS 12 | ||
| 142 | #define UDMA_HDI_OUT_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF | ||
| 143 | |||
| 144 | /* iwm_umac_hdi_out_hdr.cmd Signature -- bits [31:16] */ | ||
| 145 | #define UMAC_HDI_OUT_CMD_SIGNATURE_POS 16 | ||
| 146 | #define UMAC_HDI_OUT_CMD_SIGNATURE_SEED 0xFFFF | ||
| 147 | |||
| 148 | /* iwm_umac_hdi_out_hdr.meta_data Byte count -- bits [11:0] */ | ||
| 149 | #define UMAC_HDI_OUT_BYTE_COUNT_POS 0 | ||
| 150 | #define UMAC_HDI_OUT_BYTE_COUNT_SEED 0xFFF | ||
| 151 | |||
| 152 | /* iwm_umac_hdi_out_hdr.meta_data Credit group -- bits [15:12] */ | ||
| 153 | #define UMAC_HDI_OUT_CREDIT_GRP_POS 12 | ||
| 154 | #define UMAC_HDI_OUT_CREDIT_GRP_SEED 0xF | ||
| 155 | |||
| 156 | /* iwm_umac_hdi_out_hdr.meta_data RA/TID -- bits [23:16] */ | ||
| 157 | #define UMAC_HDI_OUT_RATID_POS 16 | ||
| 158 | #define UMAC_HDI_OUT_RATID_SEED 0xFF | ||
| 159 | |||
| 160 | /* iwm_umac_hdi_out_hdr.meta_data LMAC offset -- bits [31:24] */ | ||
| 161 | #define UMAC_HDI_OUT_LMAC_OFFSET_POS 24 | ||
| 162 | #define UMAC_HDI_OUT_LMAC_OFFSET_SEED 0xFF | ||
| 163 | |||
| 164 | /* Signature */ | ||
| 165 | #define UMAC_HDI_OUT_SIGNATURE 0xCBBC | ||
| 166 | |||
| 167 | /* buffer alignment */ | ||
| 168 | #define UMAC_HDI_BUF_ALIGN_MSK 0xF | ||
| 169 | |||
| 170 | /* iwm_umac_hdi_in_hdr.cmd OP code -- bits [3:0] */ | ||
| 171 | #define UMAC_HDI_IN_CMD_OPCODE_POS 0 | ||
| 172 | #define UMAC_HDI_IN_CMD_OPCODE_SEED 0xF | ||
| 173 | |||
| 174 | /* iwm_umac_hdi_in_hdr.cmd Non-WiFi API response -- bits [6:4] */ | ||
| 175 | #define UMAC_HDI_IN_CMD_NON_WIFI_RESP_POS 4 | ||
| 176 | #define UMAC_HDI_IN_CMD_NON_WIFI_RESP_SEED 0x7 | ||
| 177 | |||
| 178 | /* iwm_umac_hdi_in_hdr.cmd WiFi API source -- bits [5:4] */ | ||
| 179 | #define UMAC_HDI_IN_CMD_SOURCE_POS 4 | ||
| 180 | #define UMAC_HDI_IN_CMD_SOURCE_SEED 0x3 | ||
| 181 | |||
| 182 | /* iwm_umac_hdi_in_hdr.cmd WiFi API EOT -- bits [6:6] */ | ||
| 183 | #define UMAC_HDI_IN_CMD_EOT_POS 6 | ||
| 184 | #define UMAC_HDI_IN_CMD_EOT_SEED 0x1 | ||
| 185 | |||
| 186 | /* iwm_umac_hdi_in_hdr.cmd timestamp present -- bits [7:7] */ | ||
| 187 | #define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_POS 7 | ||
| 188 | #define UMAC_HDI_IN_CMD_TIME_STAMP_PRESENT_SEED 0x1 | ||
| 189 | |||
| 190 | /* iwm_umac_hdi_in_hdr.cmd WiFi Non-last AMSDU -- bits [8:8] */ | ||
| 191 | #define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_POS 8 | ||
| 192 | #define UMAC_HDI_IN_CMD_NON_LAST_AMSDU_SEED 0x1 | ||
| 193 | |||
| 194 | /* iwm_umac_hdi_in_hdr.cmd WiFi HW sequence number -- bits [31:9] */ | ||
| 195 | #define UMAC_HDI_IN_CMD_HW_SEQ_NUM_POS 9 | ||
| 196 | #define UMAC_HDI_IN_CMD_HW_SEQ_NUM_SEED 0x7FFFFF | ||
| 197 | |||
| 198 | /* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW sequence number -- bits [12:15] */ | ||
| 199 | #define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_POS 12 | ||
| 200 | #define UDMA_HDI_IN_CMD_NON_WIFI_HW_SEQ_NUM_SEED 0xF | ||
| 201 | |||
| 202 | /* iwm_umac_hdi_in_hdr.cmd Non-WiFi HW signature -- bits [16:31] */ | ||
| 203 | #define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_POS 16 | ||
| 204 | #define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG_SEED 0xFFFF | ||
| 205 | |||
| 206 | /* Fixed Non-WiFi signature */ | ||
| 207 | #define UDMA_HDI_IN_CMD_NON_WIFI_HW_SIG 0xCBBC | ||
| 208 | |||
| 209 | /* IN NTFY op-codes */ | ||
| 210 | #define UMAC_NOTIFY_OPCODE_ALIVE 0xA1 | ||
| 211 | #define UMAC_NOTIFY_OPCODE_INIT_COMPLETE 0xA2 | ||
| 212 | #define UMAC_NOTIFY_OPCODE_WIFI_CORE_STATUS 0xA3 | ||
| 213 | #define UMAC_NOTIFY_OPCODE_ERROR 0xA4 | ||
| 214 | #define UMAC_NOTIFY_OPCODE_DEBUG 0xA5 | ||
| 215 | #define UMAC_NOTIFY_OPCODE_WIFI_IF_WRAPPER 0xB0 | ||
| 216 | #define UMAC_NOTIFY_OPCODE_STATS 0xB1 | ||
| 217 | #define UMAC_NOTIFY_OPCODE_PAGE_DEALLOC 0xB3 | ||
| 218 | #define UMAC_NOTIFY_OPCODE_RX_TICKET 0xB4 | ||
| 219 | #define UMAC_NOTIFY_OPCODE_MAX (UMAC_NOTIFY_OPCODE_RX_TICKET -\ | ||
| 220 | UMAC_NOTIFY_OPCODE_ALIVE + 1) | ||
| 221 | #define UMAC_NOTIFY_OPCODE_FIRST (UMAC_NOTIFY_OPCODE_ALIVE) | ||
| 222 | |||
| 223 | /* HDI OUT OP CODE */ | ||
| 224 | #define UMAC_HDI_OUT_OPCODE_PING 0x0 | ||
| 225 | #define UMAC_HDI_OUT_OPCODE_READ 0x1 | ||
| 226 | #define UMAC_HDI_OUT_OPCODE_WRITE 0x2 | ||
| 227 | #define UMAC_HDI_OUT_OPCODE_JUMP 0x3 | ||
| 228 | #define UMAC_HDI_OUT_OPCODE_REBOOT 0x4 | ||
| 229 | #define UMAC_HDI_OUT_OPCODE_WRITE_PERSISTENT 0x5 | ||
| 230 | #define UMAC_HDI_OUT_OPCODE_READ_PERSISTENT 0x6 | ||
| 231 | #define UMAC_HDI_OUT_OPCODE_READ_MODIFY_WRITE 0x7 | ||
| 232 | /* #define UMAC_HDI_OUT_OPCODE_RESERVED 0x8..0xA */ | ||
| 233 | #define UMAC_HDI_OUT_OPCODE_WRITE_AUX_REG 0xB | ||
| 234 | #define UMAC_HDI_OUT_OPCODE_WIFI 0xF | ||
| 235 | |||
| 236 | /* HDI IN OP CODE -- Non WiFi*/ | ||
| 237 | #define UMAC_HDI_IN_OPCODE_PING 0x0 | ||
| 238 | #define UMAC_HDI_IN_OPCODE_READ 0x1 | ||
| 239 | #define UMAC_HDI_IN_OPCODE_WRITE 0x2 | ||
| 240 | #define UMAC_HDI_IN_OPCODE_WRITE_PERSISTENT 0x5 | ||
| 241 | #define UMAC_HDI_IN_OPCODE_READ_PERSISTENT 0x6 | ||
| 242 | #define UMAC_HDI_IN_OPCODE_READ_MODIFY_WRITE 0x7 | ||
| 243 | #define UMAC_HDI_IN_OPCODE_EP_MGMT 0x8 | ||
| 244 | #define UMAC_HDI_IN_OPCODE_CREDIT_CHANGE 0x9 | ||
| 245 | #define UMAC_HDI_IN_OPCODE_CTRL_DATABASE 0xA | ||
| 246 | #define UMAC_HDI_IN_OPCODE_WRITE_AUX_REG 0xB | ||
| 247 | #define UMAC_HDI_IN_OPCODE_NONWIFI_MAX \ | ||
| 248 | (UMAC_HDI_IN_OPCODE_WRITE_AUX_REG + 1) | ||
| 249 | #define UMAC_HDI_IN_OPCODE_WIFI 0xF | ||
| 250 | |||
| 251 | /* HDI IN SOURCE */ | ||
| 252 | #define UMAC_HDI_IN_SOURCE_FHRX 0x0 | ||
| 253 | #define UMAC_HDI_IN_SOURCE_UDMA 0x1 | ||
| 254 | #define UMAC_HDI_IN_SOURCE_FW 0x2 | ||
| 255 | #define UMAC_HDI_IN_SOURCE_RESERVED 0x3 | ||
| 256 | |||
| 257 | /* OUT CMD op-codes */ | ||
| 258 | #define UMAC_CMD_OPCODE_ECHO 0x01 | ||
| 259 | #define UMAC_CMD_OPCODE_HALT 0x02 | ||
| 260 | #define UMAC_CMD_OPCODE_RESET 0x03 | ||
| 261 | #define UMAC_CMD_OPCODE_BULK_EP_INACT_TIMEOUT 0x09 | ||
| 262 | #define UMAC_CMD_OPCODE_URB_CANCEL_ACK 0x0A | ||
| 263 | #define UMAC_CMD_OPCODE_DCACHE_FLUSH 0x0B | ||
| 264 | #define UMAC_CMD_OPCODE_EEPROM_PROXY 0x0C | ||
| 265 | #define UMAC_CMD_OPCODE_TX_ECHO 0x0D | ||
| 266 | #define UMAC_CMD_OPCODE_DBG_MON 0x0E | ||
| 267 | #define UMAC_CMD_OPCODE_INTERNAL_TX 0x0F | ||
| 268 | #define UMAC_CMD_OPCODE_SET_PARAM_FIX 0x10 | ||
| 269 | #define UMAC_CMD_OPCODE_SET_PARAM_VAR 0x11 | ||
| 270 | #define UMAC_CMD_OPCODE_GET_PARAM 0x12 | ||
| 271 | #define UMAC_CMD_OPCODE_DBG_EVENT_WRAPPER 0x13 | ||
| 272 | #define UMAC_CMD_OPCODE_TARGET 0x14 | ||
| 273 | #define UMAC_CMD_OPCODE_STATISTIC_REQUEST 0x15 | ||
| 274 | #define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16 | ||
| 275 | #define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17 | ||
| 276 | #define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18 | ||
| 277 | #define UMAC_CMD_OPCODE_STOP_RESUME_STA_TX 0x19 | ||
| 278 | #define UMAC_CMD_OPCODE_TEST_BLOCK_ACK 0x1A | ||
| 279 | |||
| 280 | #define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA | ||
| 281 | #define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB | ||
| 282 | #define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC | ||
| 283 | #define UMAC_CMD_OPCODE_WIFI_IF_WRAPPER 0xFD | ||
| 284 | #define UMAC_CMD_OPCODE_WIFI_WRAPPER 0xFE | ||
| 285 | #define UMAC_CMD_OPCODE_WIFI_PASS_THROUGH 0xFF | ||
| 286 | |||
| 287 | /* UMAC WiFi interface op-codes */ | ||
| 288 | #define UMAC_WIFI_IF_CMD_SET_PROFILE 0x11 | ||
| 289 | #define UMAC_WIFI_IF_CMD_INVALIDATE_PROFILE 0x12 | ||
| 290 | #define UMAC_WIFI_IF_CMD_SET_EXCLUDE_LIST 0x13 | ||
| 291 | #define UMAC_WIFI_IF_CMD_SCAN_REQUEST 0x14 | ||
| 292 | #define UMAC_WIFI_IF_CMD_SCAN_CONFIG 0x15 | ||
| 293 | #define UMAC_WIFI_IF_CMD_ADD_WEP40_KEY 0x16 | ||
| 294 | #define UMAC_WIFI_IF_CMD_ADD_WEP104_KEY 0x17 | ||
| 295 | #define UMAC_WIFI_IF_CMD_ADD_TKIP_KEY 0x18 | ||
| 296 | #define UMAC_WIFI_IF_CMD_ADD_CCMP_KEY 0x19 | ||
| 297 | #define UMAC_WIFI_IF_CMD_REMOVE_KEY 0x1A | ||
| 298 | #define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B | ||
| 299 | #define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C | ||
| 300 | #define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E | ||
| 301 | #define UMAC_WIFI_IF_CMD_PMKID_UPDATE 0x1F | ||
| 302 | #define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20 | ||
| 303 | |||
| 304 | /* UMAC WiFi interface ports */ | ||
| 305 | #define UMAC_WIFI_IF_FLG_PORT_DEF 0x00 | ||
| 306 | #define UMAC_WIFI_IF_FLG_PORT_PAN 0x01 | ||
| 307 | #define UMAC_WIFI_IF_FLG_PORT_PAN_INVALID WIFI_IF_FLG_PORT_DEF | ||
| 308 | |||
| 309 | /* UMAC WiFi interface actions */ | ||
| 310 | #define UMAC_WIFI_IF_FLG_ACT_GET 0x10 | ||
| 311 | #define UMAC_WIFI_IF_FLG_ACT_SET 0x20 | ||
| 312 | |||
| 313 | /* iwm_umac_fw_cmd_hdr.meta_data byte count -- bits [11:0] */ | ||
| 314 | #define UMAC_FW_CMD_BYTE_COUNT_POS 0 | ||
| 315 | #define UMAC_FW_CMD_BYTE_COUNT_SEED 0xFFF | ||
| 316 | |||
| 317 | /* iwm_umac_fw_cmd_hdr.meta_data status -- bits [15:12] */ | ||
| 318 | #define UMAC_FW_CMD_STATUS_POS 12 | ||
| 319 | #define UMAC_FW_CMD_STATUS_SEED 0xF | ||
| 320 | |||
| 321 | /* iwm_umac_fw_cmd_hdr.meta_data full TX command by Driver -- bits [16:16] */ | ||
| 322 | #define UMAC_FW_CMD_TX_DRV_FULL_CMD_POS 16 | ||
| 323 | #define UMAC_FW_CMD_TX_DRV_FULL_CMD_SEED 0x1 | ||
| 324 | |||
| 325 | /* iwm_umac_fw_cmd_hdr.meta_data TX command by FW -- bits [17:17] */ | ||
| 326 | #define UMAC_FW_CMD_TX_FW_CMD_POS 17 | ||
| 327 | #define UMAC_FW_CMD_TX_FW_CMD_SEED 0x1 | ||
| 328 | |||
| 329 | /* iwm_umac_fw_cmd_hdr.meta_data TX plaintext mode -- bits [18:18] */ | ||
| 330 | #define UMAC_FW_CMD_TX_PLAINTEXT_POS 18 | ||
| 331 | #define UMAC_FW_CMD_TX_PLAINTEXT_SEED 0x1 | ||
| 332 | |||
| 333 | /* iwm_umac_fw_cmd_hdr.meta_data STA color -- bits [22:20] */ | ||
| 334 | #define UMAC_FW_CMD_TX_STA_COLOR_POS 20 | ||
| 335 | #define UMAC_FW_CMD_TX_STA_COLOR_SEED 0x7 | ||
| 336 | |||
| 337 | /* iwm_umac_fw_cmd_hdr.meta_data TX life time (TU) -- bits [31:24] */ | ||
| 338 | #define UMAC_FW_CMD_TX_LIFETIME_TU_POS 24 | ||
| 339 | #define UMAC_FW_CMD_TX_LIFETIME_TU_SEED 0xFF | ||
| 340 | |||
| 341 | /* iwm_dev_cmd_hdr.flags Response required -- bits [5:5] */ | ||
| 342 | #define UMAC_DEV_CMD_FLAGS_RESP_REQ_POS 5 | ||
| 343 | #define UMAC_DEV_CMD_FLAGS_RESP_REQ_SEED 0x1 | ||
| 344 | |||
| 345 | /* iwm_dev_cmd_hdr.flags Aborted command -- bits [6:6] */ | ||
| 346 | #define UMAC_DEV_CMD_FLAGS_ABORT_POS 6 | ||
| 347 | #define UMAC_DEV_CMD_FLAGS_ABORT_SEED 0x1 | ||
| 348 | |||
| 349 | /* iwm_dev_cmd_hdr.flags Internal command -- bits [7:7] */ | ||
| 350 | #define DEV_CMD_FLAGS_FLD_INTERNAL_POS 7 | ||
| 351 | #define DEV_CMD_FLAGS_FLD_INTERNAL_SEED 0x1 | ||
| 352 | |||
| 353 | /* Rx */ | ||
| 354 | /* Rx actions */ | ||
| 355 | #define IWM_RX_TICKET_DROP 0x0 | ||
| 356 | #define IWM_RX_TICKET_RELEASE 0x1 | ||
| 357 | #define IWM_RX_TICKET_SNIFFER 0x2 | ||
| 358 | #define IWM_RX_TICKET_ENQUEUE 0x3 | ||
| 359 | |||
| 360 | /* Rx flags */ | ||
| 361 | #define IWM_RX_TICKET_PAD_SIZE_MSK 0x2 | ||
| 362 | #define IWM_RX_TICKET_SPECIAL_SNAP_MSK 0x4 | ||
| 363 | #define IWM_RX_TICKET_AMSDU_MSK 0x8 | ||
| 364 | #define IWM_RX_TICKET_DROP_REASON_POS 4 | ||
| 365 | #define IWM_RX_TICKET_DROP_REASON_MSK (0x1F << IWM_RX_TICKET_DROP_REASON_POS) | ||
| 366 | |||
| 367 | #define IWM_RX_DROP_NO_DROP 0x0 | ||
| 368 | #define IWM_RX_DROP_BAD_CRC 0x1 | ||
| 369 | /* L2P no address match */ | ||
| 370 | #define IWM_RX_DROP_LMAC_ADDR_FILTER 0x2 | ||
| 371 | /* Multicast address not in list */ | ||
| 372 | #define IWM_RX_DROP_MCAST_ADDR_FILTER 0x3 | ||
| 373 | /* Control frames are not sent to the driver */ | ||
| 374 | #define IWM_RX_DROP_CTL_FRAME 0x4 | ||
| 375 | /* Our frame is back */ | ||
| 376 | #define IWM_RX_DROP_OUR_TX 0x5 | ||
| 377 | /* Association class filtering */ | ||
| 378 | #define IWM_RX_DROP_CLASS_FILTER 0x6 | ||
| 379 | /* Duplicated frame */ | ||
| 380 | #define IWM_RX_DROP_DUPLICATE_FILTER 0x7 | ||
| 381 | /* Decryption error */ | ||
| 382 | #define IWM_RX_DROP_SEC_ERR 0x8 | ||
| 383 | /* Unencrypted frame while encryption is on */ | ||
| 384 | #define IWM_RX_DROP_SEC_NO_ENCRYPTION 0x9 | ||
| 385 | /* Replay check failure */ | ||
| 386 | #define IWM_RX_DROP_SEC_REPLAY_ERR 0xa | ||
| 387 | /* uCode and FW key color mismatch, check before replay */ | ||
| 388 | #define IWM_RX_DROP_SEC_KEY_COLOR_MISMATCH 0xb | ||
| 389 | #define IWM_RX_DROP_SEC_TKIP_COUNTER_MEASURE 0xc | ||
| 390 | /* No fragmentations Db is found */ | ||
| 391 | #define IWM_RX_DROP_FRAG_NO_RESOURCE 0xd | ||
| 392 | /* Fragmention Db has seqCtl mismatch Vs. non-1st frag */ | ||
| 393 | #define IWM_RX_DROP_FRAG_ERR 0xe | ||
| 394 | #define IWM_RX_DROP_FRAG_LOST 0xf | ||
| 395 | #define IWM_RX_DROP_FRAG_COMPLETE 0x10 | ||
| 396 | /* Should be handled by UMAC */ | ||
| 397 | #define IWM_RX_DROP_MANAGEMENT 0x11 | ||
| 398 | /* STA not found by UMAC */ | ||
| 399 | #define IWM_RX_DROP_NO_STATION 0x12 | ||
| 400 | /* NULL or QoS NULL */ | ||
| 401 | #define IWM_RX_DROP_NULL_DATA 0x13 | ||
| 402 | #define IWM_RX_DROP_BA_REORDER_OLD_SEQCTL 0x14 | ||
| 403 | #define IWM_RX_DROP_BA_REORDER_DUPLICATE 0x15 | ||
| 404 | |||
| 405 | struct iwm_rx_ticket { | ||
| 406 | __le16 action; | ||
| 407 | __le16 id; | ||
| 408 | __le16 flags; | ||
| 409 | u8 payload_offset; /* includes: MAC header, pad, IV */ | ||
| 410 | u8 tail_len; /* includes: MIC, ICV, CRC (w/o STATUS) */ | ||
| 411 | } __packed; | ||
| 412 | |||
| 413 | struct iwm_rx_mpdu_hdr { | ||
| 414 | __le16 len; | ||
| 415 | __le16 reserved; | ||
| 416 | } __packed; | ||
| 417 | |||
| 418 | /* UMAC SW WIFI API */ | ||
| 419 | |||
| 420 | struct iwm_dev_cmd_hdr { | ||
| 421 | u8 cmd; | ||
| 422 | u8 flags; | ||
| 423 | __le16 seq_num; | ||
| 424 | } __packed; | ||
| 425 | |||
| 426 | struct iwm_umac_fw_cmd_hdr { | ||
| 427 | __le32 meta_data; | ||
| 428 | struct iwm_dev_cmd_hdr cmd; | ||
| 429 | } __packed; | ||
| 430 | |||
| 431 | struct iwm_umac_wifi_out_hdr { | ||
| 432 | struct iwm_udma_out_wifi_hdr hw_hdr; | ||
| 433 | struct iwm_umac_fw_cmd_hdr sw_hdr; | ||
| 434 | } __packed; | ||
| 435 | |||
| 436 | struct iwm_umac_nonwifi_out_hdr { | ||
| 437 | struct iwm_udma_out_nonwifi_hdr hw_hdr; | ||
| 438 | } __packed; | ||
| 439 | |||
| 440 | struct iwm_umac_wifi_in_hdr { | ||
| 441 | struct iwm_udma_in_hdr hw_hdr; | ||
| 442 | struct iwm_umac_fw_cmd_hdr sw_hdr; | ||
| 443 | } __packed; | ||
| 444 | |||
| 445 | struct iwm_umac_nonwifi_in_hdr { | ||
| 446 | struct iwm_udma_in_hdr hw_hdr; | ||
| 447 | __le32 time_stamp; | ||
| 448 | } __packed; | ||
| 449 | |||
| 450 | #define IWM_UMAC_PAGE_SIZE 0x200 | ||
| 451 | |||
| 452 | /* Notify structures */ | ||
| 453 | struct iwm_fw_version { | ||
| 454 | u8 minor; | ||
| 455 | u8 major; | ||
| 456 | __le16 id; | ||
| 457 | }; | ||
| 458 | |||
| 459 | struct iwm_fw_build { | ||
| 460 | u8 type; | ||
| 461 | u8 subtype; | ||
| 462 | u8 platform; | ||
| 463 | u8 opt; | ||
| 464 | }; | ||
| 465 | |||
| 466 | struct iwm_fw_alive_hdr { | ||
| 467 | struct iwm_fw_version ver; | ||
| 468 | struct iwm_fw_build build; | ||
| 469 | __le32 os_build; | ||
| 470 | __le32 log_hdr_addr; | ||
| 471 | __le32 log_buf_addr; | ||
| 472 | __le32 sys_timer_addr; | ||
| 473 | }; | ||
| 474 | |||
| 475 | #define WAIT_NOTIF_TIMEOUT (2 * HZ) | ||
| 476 | #define SCAN_COMPLETE_TIMEOUT (3 * HZ) | ||
| 477 | |||
| 478 | #define UMAC_NTFY_ALIVE_STATUS_ERR 0xDEAD | ||
| 479 | #define UMAC_NTFY_ALIVE_STATUS_OK 0xCAFE | ||
| 480 | |||
| 481 | #define UMAC_NTFY_INIT_COMPLETE_STATUS_ERR 0xDEAD | ||
| 482 | #define UMAC_NTFY_INIT_COMPLETE_STATUS_OK 0xCAFE | ||
| 483 | |||
| 484 | #define UMAC_NTFY_WIFI_CORE_STATUS_LINK_EN 0x40 | ||
| 485 | #define UMAC_NTFY_WIFI_CORE_STATUS_MLME_EN 0x80 | ||
| 486 | |||
| 487 | #define IWM_MACS_OUT_GROUPS 6 | ||
| 488 | #define IWM_MACS_OUT_SGROUPS 1 | ||
| 489 | |||
| 490 | |||
| 491 | #define WIFI_IF_NTFY_ASSOC_START 0x80 | ||
| 492 | #define WIFI_IF_NTFY_ASSOC_COMPLETE 0x81 | ||
| 493 | #define WIFI_IF_NTFY_PROFILE_INVALIDATE_COMPLETE 0x82 | ||
| 494 | #define WIFI_IF_NTFY_CONNECTION_TERMINATED 0x83 | ||
| 495 | #define WIFI_IF_NTFY_SCAN_COMPLETE 0x84 | ||
| 496 | #define WIFI_IF_NTFY_STA_TABLE_CHANGE 0x85 | ||
| 497 | #define WIFI_IF_NTFY_EXTENDED_IE_REQUIRED 0x86 | ||
| 498 | #define WIFI_IF_NTFY_RADIO_PREEMPTION 0x87 | ||
| 499 | #define WIFI_IF_NTFY_BSS_TRK_TABLE_CHANGED 0x88 | ||
| 500 | #define WIFI_IF_NTFY_BSS_TRK_ENTRIES_REMOVED 0x89 | ||
| 501 | #define WIFI_IF_NTFY_LINK_QUALITY_STATISTICS 0x8A | ||
| 502 | #define WIFI_IF_NTFY_MGMT_FRAME 0x8B | ||
| 503 | |||
| 504 | /* DEBUG INDICATIONS */ | ||
| 505 | #define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_START 0xE0 | ||
| 506 | #define WIFI_DBG_IF_NTFY_SCAN_SUPER_JOB_COMPLETE 0xE1 | ||
| 507 | #define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_START 0xE2 | ||
| 508 | #define WIFI_DBG_IF_NTFY_SCAN_CHANNEL_RESULT 0xE3 | ||
| 509 | #define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_START 0xE4 | ||
| 510 | #define WIFI_DBG_IF_NTFY_SCAN_MINI_JOB_COMPLETE 0xE5 | ||
| 511 | #define WIFI_DBG_IF_NTFY_CNCT_ATC_START 0xE6 | ||
| 512 | #define WIFI_DBG_IF_NTFY_COEX_NOTIFICATION 0xE7 | ||
| 513 | #define WIFI_DBG_IF_NTFY_COEX_HANDLE_ENVELOP 0xE8 | ||
| 514 | #define WIFI_DBG_IF_NTFY_COEX_HANDLE_RELEASE_ENVELOP 0xE9 | ||
| 515 | |||
| 516 | #define WIFI_IF_NTFY_MAX 0xff | ||
| 517 | |||
| 518 | /* Notification structures */ | ||
| 519 | struct iwm_umac_notif_wifi_if { | ||
| 520 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 521 | u8 status; | ||
| 522 | u8 flags; | ||
| 523 | __le16 buf_size; | ||
| 524 | } __packed; | ||
| 525 | |||
| 526 | #define UMAC_ROAM_REASON_FIRST_SELECTION 0x1 | ||
| 527 | #define UMAC_ROAM_REASON_AP_DEAUTH 0x2 | ||
| 528 | #define UMAC_ROAM_REASON_AP_CONNECT_LOST 0x3 | ||
| 529 | #define UMAC_ROAM_REASON_RSSI 0x4 | ||
| 530 | #define UMAC_ROAM_REASON_AP_ASSISTED_ROAM 0x5 | ||
| 531 | #define UMAC_ROAM_REASON_IBSS_COALESCING 0x6 | ||
| 532 | |||
| 533 | struct iwm_umac_notif_assoc_start { | ||
| 534 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 535 | __le32 roam_reason; | ||
| 536 | u8 bssid[ETH_ALEN]; | ||
| 537 | u8 reserved[2]; | ||
| 538 | } __packed; | ||
| 539 | |||
| 540 | #define UMAC_ASSOC_COMPLETE_SUCCESS 0x0 | ||
| 541 | #define UMAC_ASSOC_COMPLETE_FAILURE 0x1 | ||
| 542 | |||
| 543 | struct iwm_umac_notif_assoc_complete { | ||
| 544 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 545 | __le32 status; | ||
| 546 | u8 bssid[ETH_ALEN]; | ||
| 547 | u8 band; | ||
| 548 | u8 channel; | ||
| 549 | } __packed; | ||
| 550 | |||
| 551 | #define UMAC_PROFILE_INVALID_ASSOC_TIMEOUT 0x0 | ||
| 552 | #define UMAC_PROFILE_INVALID_ROAM_TIMEOUT 0x1 | ||
| 553 | #define UMAC_PROFILE_INVALID_REQUEST 0x2 | ||
| 554 | #define UMAC_PROFILE_INVALID_RF_PREEMPTED 0x3 | ||
| 555 | |||
| 556 | struct iwm_umac_notif_profile_invalidate { | ||
| 557 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 558 | __le32 reason; | ||
| 559 | } __packed; | ||
| 560 | |||
| 561 | #define UMAC_SCAN_RESULT_SUCCESS 0x0 | ||
| 562 | #define UMAC_SCAN_RESULT_ABORTED 0x1 | ||
| 563 | #define UMAC_SCAN_RESULT_REJECTED 0x2 | ||
| 564 | #define UMAC_SCAN_RESULT_FAILED 0x3 | ||
| 565 | |||
| 566 | struct iwm_umac_notif_scan_complete { | ||
| 567 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 568 | __le32 type; | ||
| 569 | __le32 result; | ||
| 570 | u8 seq_num; | ||
| 571 | } __packed; | ||
| 572 | |||
| 573 | #define UMAC_OPCODE_ADD_MODIFY 0x0 | ||
| 574 | #define UMAC_OPCODE_REMOVE 0x1 | ||
| 575 | #define UMAC_OPCODE_CLEAR_ALL 0x2 | ||
| 576 | |||
| 577 | #define UMAC_STA_FLAG_QOS 0x1 | ||
| 578 | |||
| 579 | struct iwm_umac_notif_sta_info { | ||
| 580 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 581 | __le32 opcode; | ||
| 582 | u8 mac_addr[ETH_ALEN]; | ||
| 583 | u8 sta_id; /* bits 0-3: station ID, bits 4-7: station color */ | ||
| 584 | u8 flags; | ||
| 585 | } __packed; | ||
| 586 | |||
| 587 | #define UMAC_BAND_2GHZ 0 | ||
| 588 | #define UMAC_BAND_5GHZ 1 | ||
| 589 | |||
| 590 | #define UMAC_CHANNEL_WIDTH_20MHZ 0 | ||
| 591 | #define UMAC_CHANNEL_WIDTH_40MHZ 1 | ||
| 592 | |||
| 593 | struct iwm_umac_notif_bss_info { | ||
| 594 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 595 | __le32 type; | ||
| 596 | __le32 timestamp; | ||
| 597 | __le16 table_idx; | ||
| 598 | __le16 frame_len; | ||
| 599 | u8 band; | ||
| 600 | u8 channel; | ||
| 601 | s8 rssi; | ||
| 602 | u8 reserved; | ||
| 603 | u8 frame_buf[1]; | ||
| 604 | } __packed; | ||
| 605 | |||
| 606 | #define IWM_BSS_REMOVE_INDEX_MSK 0x0fff | ||
| 607 | #define IWM_BSS_REMOVE_FLAGS_MSK 0xfc00 | ||
| 608 | |||
| 609 | #define IWM_BSS_REMOVE_FLG_AGE 0x1000 | ||
| 610 | #define IWM_BSS_REMOVE_FLG_TIMEOUT 0x2000 | ||
| 611 | #define IWM_BSS_REMOVE_FLG_TABLE_FULL 0x4000 | ||
| 612 | |||
| 613 | struct iwm_umac_notif_bss_removed { | ||
| 614 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 615 | __le32 count; | ||
| 616 | __le16 entries[0]; | ||
| 617 | } __packed; | ||
| 618 | |||
| 619 | struct iwm_umac_notif_mgt_frame { | ||
| 620 | struct iwm_umac_notif_wifi_if mlme_hdr; | ||
| 621 | __le16 len; | ||
| 622 | u8 frame[1]; | ||
| 623 | } __packed; | ||
| 624 | |||
| 625 | struct iwm_umac_notif_alive { | ||
| 626 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 627 | __le16 status; | ||
| 628 | __le16 reserved1; | ||
| 629 | struct iwm_fw_alive_hdr alive_data; | ||
| 630 | __le16 reserved2; | ||
| 631 | __le16 page_grp_count; | ||
| 632 | __le32 page_grp_state[IWM_MACS_OUT_GROUPS]; | ||
| 633 | } __packed; | ||
| 634 | |||
| 635 | struct iwm_umac_notif_init_complete { | ||
| 636 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 637 | __le16 status; | ||
| 638 | __le16 reserved; | ||
| 639 | } __packed; | ||
| 640 | |||
| 641 | /* error categories */ | ||
| 642 | enum { | ||
| 643 | UMAC_SYS_ERR_CAT_NONE = 0, | ||
| 644 | UMAC_SYS_ERR_CAT_BOOT, | ||
| 645 | UMAC_SYS_ERR_CAT_UMAC, | ||
| 646 | UMAC_SYS_ERR_CAT_UAXM, | ||
| 647 | UMAC_SYS_ERR_CAT_LMAC, | ||
| 648 | UMAC_SYS_ERR_CAT_MAX | ||
| 649 | }; | ||
| 650 | |||
| 651 | struct iwm_fw_error_hdr { | ||
| 652 | __le32 category; | ||
| 653 | __le32 status; | ||
| 654 | __le32 pc; | ||
| 655 | __le32 blink1; | ||
| 656 | __le32 blink2; | ||
| 657 | __le32 ilink1; | ||
| 658 | __le32 ilink2; | ||
| 659 | __le32 data1; | ||
| 660 | __le32 data2; | ||
| 661 | __le32 line_num; | ||
| 662 | __le32 umac_status; | ||
| 663 | __le32 lmac_status; | ||
| 664 | __le32 sdio_status; | ||
| 665 | __le32 dbm_sample_ctrl; | ||
| 666 | __le32 dbm_buf_base; | ||
| 667 | __le32 dbm_buf_end; | ||
| 668 | __le32 dbm_buf_write_ptr; | ||
| 669 | __le32 dbm_buf_cycle_cnt; | ||
| 670 | } __packed; | ||
| 671 | |||
| 672 | struct iwm_umac_notif_error { | ||
| 673 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 674 | struct iwm_fw_error_hdr err; | ||
| 675 | } __packed; | ||
| 676 | |||
| 677 | #define UMAC_DEALLOC_NTFY_CHANGES_CNT_POS 0 | ||
| 678 | #define UMAC_DEALLOC_NTFY_CHANGES_CNT_SEED 0xff | ||
| 679 | #define UMAC_DEALLOC_NTFY_CHANGES_MSK_POS 8 | ||
| 680 | #define UMAC_DEALLOC_NTFY_CHANGES_MSK_SEED 0xffffff | ||
| 681 | #define UMAC_DEALLOC_NTFY_PAGE_CNT_POS 0 | ||
| 682 | #define UMAC_DEALLOC_NTFY_PAGE_CNT_SEED 0xffffff | ||
| 683 | #define UMAC_DEALLOC_NTFY_GROUP_NUM_POS 24 | ||
| 684 | #define UMAC_DEALLOC_NTFY_GROUP_NUM_SEED 0xf | ||
| 685 | |||
| 686 | struct iwm_umac_notif_page_dealloc { | ||
| 687 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 688 | __le32 changes; | ||
| 689 | __le32 grp_info[IWM_MACS_OUT_GROUPS]; | ||
| 690 | } __packed; | ||
| 691 | |||
| 692 | struct iwm_umac_notif_wifi_status { | ||
| 693 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 694 | __le16 status; | ||
| 695 | __le16 reserved; | ||
| 696 | } __packed; | ||
| 697 | |||
| 698 | struct iwm_umac_notif_rx_ticket { | ||
| 699 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 700 | u8 num_tickets; | ||
| 701 | u8 reserved[3]; | ||
| 702 | struct iwm_rx_ticket tickets[1]; | ||
| 703 | } __packed; | ||
| 704 | |||
| 705 | /* Tx/Rx rates window (number of max of last update window per second) */ | ||
| 706 | #define UMAC_NTF_RATE_SAMPLE_NR 4 | ||
| 707 | |||
| 708 | /* Max numbers of bits required to go through all antennae in bitmasks */ | ||
| 709 | #define UMAC_PHY_NUM_CHAINS 3 | ||
| 710 | |||
| 711 | #define IWM_UMAC_MGMT_TID 8 | ||
| 712 | #define IWM_UMAC_TID_NR 9 /* 8 TIDs + MGMT */ | ||
| 713 | |||
| 714 | struct iwm_umac_notif_stats { | ||
| 715 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 716 | __le32 flags; | ||
| 717 | __le32 timestamp; | ||
| 718 | __le16 tid_load[IWM_UMAC_TID_NR + 1]; /* 1 non-QoS + 1 dword align */ | ||
| 719 | __le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR]; | ||
| 720 | __le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR]; | ||
| 721 | __le32 chain_energy[UMAC_PHY_NUM_CHAINS]; | ||
| 722 | s32 rssi_dbm; | ||
| 723 | s32 noise_dbm; | ||
| 724 | __le32 supp_rates; | ||
| 725 | __le32 supp_ht_rates; | ||
| 726 | __le32 missed_beacons; | ||
| 727 | __le32 rx_beacons; | ||
| 728 | __le32 rx_dir_pkts; | ||
| 729 | __le32 rx_nondir_pkts; | ||
| 730 | __le32 rx_multicast; | ||
| 731 | __le32 rx_errors; | ||
| 732 | __le32 rx_drop_other_bssid; | ||
| 733 | __le32 rx_drop_decode; | ||
| 734 | __le32 rx_drop_reassembly; | ||
| 735 | __le32 rx_drop_bad_len; | ||
| 736 | __le32 rx_drop_overflow; | ||
| 737 | __le32 rx_drop_crc; | ||
| 738 | __le32 rx_drop_missed; | ||
| 739 | __le32 tx_dir_pkts; | ||
| 740 | __le32 tx_nondir_pkts; | ||
| 741 | __le32 tx_failure; | ||
| 742 | __le32 tx_errors; | ||
| 743 | __le32 tx_drop_max_retry; | ||
| 744 | __le32 tx_err_abort; | ||
| 745 | __le32 tx_err_carrier; | ||
| 746 | __le32 rx_bytes; | ||
| 747 | __le32 tx_bytes; | ||
| 748 | __le32 tx_power; | ||
| 749 | __le32 tx_max_power; | ||
| 750 | __le32 roam_threshold; | ||
| 751 | __le32 ap_assoc_nr; | ||
| 752 | __le32 scan_full; | ||
| 753 | __le32 scan_abort; | ||
| 754 | __le32 ap_nr; | ||
| 755 | __le32 roam_nr; | ||
| 756 | __le32 roam_missed_beacons; | ||
| 757 | __le32 roam_rssi; | ||
| 758 | __le32 roam_unassoc; | ||
| 759 | __le32 roam_deauth; | ||
| 760 | __le32 roam_ap_loadblance; | ||
| 761 | } __packed; | ||
| 762 | |||
| 763 | #define UMAC_STOP_TX_FLAG 0x1 | ||
| 764 | #define UMAC_RESUME_TX_FLAG 0x2 | ||
| 765 | |||
| 766 | #define LAST_SEQ_NUM_INVALID 0xFFFF | ||
| 767 | |||
| 768 | struct iwm_umac_notif_stop_resume_tx { | ||
| 769 | struct iwm_umac_wifi_in_hdr hdr; | ||
| 770 | u8 flags; /* UMAC_*_TX_FLAG_* */ | ||
| 771 | u8 sta_id; | ||
| 772 | __le16 stop_resume_tid_msk; /* tid bitmask */ | ||
| 773 | } __packed; | ||
| 774 | |||
| 775 | #define UMAC_MAX_NUM_PMKIDS 4 | ||
| 776 | |||
| 777 | /* WiFi interface wrapper header */ | ||
| 778 | struct iwm_umac_wifi_if { | ||
| 779 | u8 oid; | ||
| 780 | u8 flags; | ||
| 781 | __le16 buf_size; | ||
| 782 | } __packed; | ||
| 783 | |||
| 784 | #define IWM_SEQ_NUM_HOST_MSK 0x0000 | ||
| 785 | #define IWM_SEQ_NUM_UMAC_MSK 0x4000 | ||
| 786 | #define IWM_SEQ_NUM_LMAC_MSK 0x8000 | ||
| 787 | #define IWM_SEQ_NUM_MSK 0xC000 | ||
| 788 | |||
| 789 | #endif | ||
