diff options
Diffstat (limited to 'drivers/net/wireless/iwmc3200wifi')
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/cfg80211.c | 35 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/commands.c | 77 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/commands.h | 23 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/debugfs.c | 23 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/eeprom.c | 50 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/eeprom.h | 29 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/iwm.h | 11 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/main.c | 44 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/netdev.c | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/rx.c | 66 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/tx.c | 66 | ||||
-rw-r--r-- | drivers/net/wireless/iwmc3200wifi/umac.h | 36 |
12 files changed, 428 insertions, 40 deletions
diff --git a/drivers/net/wireless/iwmc3200wifi/cfg80211.c b/drivers/net/wireless/iwmc3200wifi/cfg80211.c index 2e00a4b389e6..7c4f44a9c3e6 100644 --- a/drivers/net/wireless/iwmc3200wifi/cfg80211.c +++ b/drivers/net/wireless/iwmc3200wifi/cfg80211.c | |||
@@ -678,6 +678,9 @@ static int iwm_cfg80211_set_txpower(struct wiphy *wiphy, | |||
678 | case TX_POWER_AUTOMATIC: | 678 | case TX_POWER_AUTOMATIC: |
679 | return 0; | 679 | return 0; |
680 | case TX_POWER_FIXED: | 680 | case TX_POWER_FIXED: |
681 | if (!test_bit(IWM_STATUS_READY, &iwm->status)) | ||
682 | return 0; | ||
683 | |||
681 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | 684 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, |
682 | CFG_TX_PWR_LIMIT_USR, dbm * 2); | 685 | CFG_TX_PWR_LIMIT_USR, dbm * 2); |
683 | if (ret < 0) | 686 | if (ret < 0) |
@@ -685,6 +688,7 @@ static int iwm_cfg80211_set_txpower(struct wiphy *wiphy, | |||
685 | 688 | ||
686 | return iwm_tx_power_trigger(iwm); | 689 | return iwm_tx_power_trigger(iwm); |
687 | default: | 690 | default: |
691 | IWM_ERR(iwm, "Unsupported power type: %d\n", type); | ||
688 | return -EOPNOTSUPP; | 692 | return -EOPNOTSUPP; |
689 | } | 693 | } |
690 | 694 | ||
@@ -721,6 +725,33 @@ static int iwm_cfg80211_set_power_mgmt(struct wiphy *wiphy, | |||
721 | CFG_POWER_INDEX, iwm->conf.power_index); | 725 | CFG_POWER_INDEX, iwm->conf.power_index); |
722 | } | 726 | } |
723 | 727 | ||
728 | int iwm_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *netdev, | ||
729 | struct cfg80211_pmksa *pmksa) | ||
730 | { | ||
731 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
732 | |||
733 | return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_ADD); | ||
734 | } | ||
735 | |||
736 | int iwm_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *netdev, | ||
737 | struct cfg80211_pmksa *pmksa) | ||
738 | { | ||
739 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
740 | |||
741 | return iwm_send_pmkid_update(iwm, pmksa, IWM_CMD_PMKID_DEL); | ||
742 | } | ||
743 | |||
744 | int iwm_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *netdev) | ||
745 | { | ||
746 | struct iwm_priv *iwm = wiphy_to_iwm(wiphy); | ||
747 | struct cfg80211_pmksa pmksa; | ||
748 | |||
749 | memset(&pmksa, 0, sizeof(struct cfg80211_pmksa)); | ||
750 | |||
751 | return iwm_send_pmkid_update(iwm, &pmksa, IWM_CMD_PMKID_FLUSH); | ||
752 | } | ||
753 | |||
754 | |||
724 | static struct cfg80211_ops iwm_cfg80211_ops = { | 755 | static struct cfg80211_ops iwm_cfg80211_ops = { |
725 | .change_virtual_intf = iwm_cfg80211_change_iface, | 756 | .change_virtual_intf = iwm_cfg80211_change_iface, |
726 | .add_key = iwm_cfg80211_add_key, | 757 | .add_key = iwm_cfg80211_add_key, |
@@ -737,6 +768,9 @@ static struct cfg80211_ops iwm_cfg80211_ops = { | |||
737 | .set_tx_power = iwm_cfg80211_set_txpower, | 768 | .set_tx_power = iwm_cfg80211_set_txpower, |
738 | .get_tx_power = iwm_cfg80211_get_txpower, | 769 | .get_tx_power = iwm_cfg80211_get_txpower, |
739 | .set_power_mgmt = iwm_cfg80211_set_power_mgmt, | 770 | .set_power_mgmt = iwm_cfg80211_set_power_mgmt, |
771 | .set_pmksa = iwm_cfg80211_set_pmksa, | ||
772 | .del_pmksa = iwm_cfg80211_del_pmksa, | ||
773 | .flush_pmksa = iwm_cfg80211_flush_pmksa, | ||
740 | }; | 774 | }; |
741 | 775 | ||
742 | static const u32 cipher_suites[] = { | 776 | static const u32 cipher_suites[] = { |
@@ -782,6 +816,7 @@ struct wireless_dev *iwm_wdev_alloc(int sizeof_bus, struct device *dev) | |||
782 | 816 | ||
783 | set_wiphy_dev(wdev->wiphy, dev); | 817 | set_wiphy_dev(wdev->wiphy, dev); |
784 | wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX; | 818 | wdev->wiphy->max_scan_ssids = UMAC_WIFI_IF_PROBE_OPTION_MAX; |
819 | wdev->wiphy->max_num_pmkids = UMAC_MAX_NUM_PMKIDS; | ||
785 | wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | 820 | wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | |
786 | BIT(NL80211_IFTYPE_ADHOC); | 821 | BIT(NL80211_IFTYPE_ADHOC); |
787 | wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz; | 822 | wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &iwm_band_2ghz; |
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.c b/drivers/net/wireless/iwmc3200wifi/commands.c index 7e12438551ba..777584d76a88 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.c +++ b/drivers/net/wireless/iwmc3200wifi/commands.c | |||
@@ -99,6 +99,10 @@ int iwm_send_wifi_if_cmd(struct iwm_priv *iwm, void *payload, u16 payload_size, | |||
99 | return ret; | 99 | return ret; |
100 | } | 100 | } |
101 | 101 | ||
102 | static int modparam_wiwi = COEX_MODE_CM; | ||
103 | module_param_named(wiwi, modparam_wiwi, int, 0644); | ||
104 | MODULE_PARM_DESC(wiwi, "Wifi-WiMAX coexistence: 1=SA, 2=XOR, 3=CM (default)"); | ||
105 | |||
102 | static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = | 106 | static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = |
103 | { | 107 | { |
104 | {4, 3, 0, COEX_UNASSOC_IDLE_FLAGS}, | 108 | {4, 3, 0, COEX_UNASSOC_IDLE_FLAGS}, |
@@ -122,18 +126,18 @@ static struct coex_event iwm_sta_xor_prio_tbl[COEX_EVENTS_NUM] = | |||
122 | static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] = | 126 | static struct coex_event iwm_sta_cm_prio_tbl[COEX_EVENTS_NUM] = |
123 | { | 127 | { |
124 | {1, 1, 0, COEX_UNASSOC_IDLE_FLAGS}, | 128 | {1, 1, 0, COEX_UNASSOC_IDLE_FLAGS}, |
125 | {4, 3, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, | 129 | {4, 4, 0, COEX_UNASSOC_MANUAL_SCAN_FLAGS}, |
126 | {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, | 130 | {3, 3, 0, COEX_UNASSOC_AUTO_SCAN_FLAGS}, |
127 | {5, 5, 0, COEX_CALIBRATION_FLAGS}, | 131 | {6, 6, 0, COEX_CALIBRATION_FLAGS}, |
128 | {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, | 132 | {3, 3, 0, COEX_PERIODIC_CALIBRATION_FLAGS}, |
129 | {5, 4, 0, COEX_CONNECTION_ESTAB_FLAGS}, | 133 | {6, 5, 0, COEX_CONNECTION_ESTAB_FLAGS}, |
130 | {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS}, | 134 | {4, 4, 0, COEX_ASSOCIATED_IDLE_FLAGS}, |
131 | {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, | 135 | {4, 4, 0, COEX_ASSOC_MANUAL_SCAN_FLAGS}, |
132 | {4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, | 136 | {4, 4, 0, COEX_ASSOC_AUTO_SCAN_FLAGS}, |
133 | {4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, | 137 | {4, 4, 0, COEX_ASSOC_ACTIVE_LEVEL_FLAGS}, |
134 | {1, 1, 0, COEX_RF_ON_FLAGS}, | 138 | {1, 1, 0, COEX_RF_ON_FLAGS}, |
135 | {1, 1, 0, COEX_RF_OFF_FLAGS}, | 139 | {1, 1, 0, COEX_RF_OFF_FLAGS}, |
136 | {6, 6, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, | 140 | {7, 7, 0, COEX_STAND_ALONE_DEBUG_FLAGS}, |
137 | {5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, | 141 | {5, 4, 0, COEX_IPAN_ASSOC_LEVEL_FLAGS}, |
138 | {1, 1, 0, COEX_RSRVD1_FLAGS}, | 142 | {1, 1, 0, COEX_RSRVD1_FLAGS}, |
139 | {1, 1, 0, COEX_RSRVD2_FLAGS} | 143 | {1, 1, 0, COEX_RSRVD2_FLAGS} |
@@ -148,7 +152,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) | |||
148 | 152 | ||
149 | coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK; | 153 | coex_table_cmd.flags = COEX_FLAGS_STA_TABLE_VALID_MSK; |
150 | 154 | ||
151 | switch (iwm->conf.coexist_mode) { | 155 | switch (modparam_wiwi) { |
152 | case COEX_MODE_XOR: | 156 | case COEX_MODE_XOR: |
153 | case COEX_MODE_CM: | 157 | case COEX_MODE_CM: |
154 | coex_enabled = 1; | 158 | coex_enabled = 1; |
@@ -173,7 +177,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) | |||
173 | COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK | | 177 | COEX_FLAGS_ASSOC_WAKEUP_UMASK_MSK | |
174 | COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK; | 178 | COEX_FLAGS_UNASSOC_WAKEUP_UMASK_MSK; |
175 | 179 | ||
176 | switch (iwm->conf.coexist_mode) { | 180 | switch (modparam_wiwi) { |
177 | case COEX_MODE_XOR: | 181 | case COEX_MODE_XOR: |
178 | memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl, | 182 | memcpy(coex_table_cmd.sta_prio, iwm_sta_xor_prio_tbl, |
179 | sizeof(iwm_sta_xor_prio_tbl)); | 183 | sizeof(iwm_sta_xor_prio_tbl)); |
@@ -184,7 +188,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) | |||
184 | break; | 188 | break; |
185 | default: | 189 | default: |
186 | IWM_ERR(iwm, "Invalid coex_mode 0x%x\n", | 190 | IWM_ERR(iwm, "Invalid coex_mode 0x%x\n", |
187 | iwm->conf.coexist_mode); | 191 | modparam_wiwi); |
188 | break; | 192 | break; |
189 | } | 193 | } |
190 | } else | 194 | } else |
@@ -192,7 +196,7 @@ int iwm_send_prio_table(struct iwm_priv *iwm) | |||
192 | 196 | ||
193 | return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD, | 197 | return iwm_send_lmac_ptrough_cmd(iwm, COEX_PRIORITY_TABLE_CMD, |
194 | &coex_table_cmd, | 198 | &coex_table_cmd, |
195 | sizeof(struct iwm_coex_prio_table_cmd), 1); | 199 | sizeof(struct iwm_coex_prio_table_cmd), 0); |
196 | } | 200 | } |
197 | 201 | ||
198 | int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested) | 202 | int iwm_send_init_calib_cfg(struct iwm_priv *iwm, u8 calib_requested) |
@@ -396,7 +400,7 @@ int iwm_send_umac_config(struct iwm_priv *iwm, __le32 reset_flags) | |||
396 | return ret; | 400 | return ret; |
397 | 401 | ||
398 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, | 402 | ret = iwm_umac_set_config_fix(iwm, UMAC_PARAM_TBL_CFG_FIX, |
399 | CFG_COEX_MODE, iwm->conf.coexist_mode); | 403 | CFG_COEX_MODE, modparam_wiwi); |
400 | if (ret < 0) | 404 | if (ret < 0) |
401 | return ret; | 405 | return ret; |
402 | 406 | ||
@@ -929,3 +933,58 @@ int iwm_target_reset(struct iwm_priv *iwm) | |||
929 | 933 | ||
930 | return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); | 934 | return iwm_hal_send_target_cmd(iwm, &target_cmd, NULL); |
931 | } | 935 | } |
936 | |||
937 | int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, | ||
938 | struct iwm_umac_notif_stop_resume_tx *ntf) | ||
939 | { | ||
940 | struct iwm_udma_wifi_cmd udma_cmd = UDMA_UMAC_INIT; | ||
941 | struct iwm_umac_cmd umac_cmd; | ||
942 | struct iwm_umac_cmd_stop_resume_tx stp_res_cmd; | ||
943 | struct iwm_sta_info *sta_info; | ||
944 | u8 sta_id = STA_ID_N_COLOR_ID(ntf->sta_id); | ||
945 | int i; | ||
946 | |||
947 | sta_info = &iwm->sta_table[sta_id]; | ||
948 | if (!sta_info->valid) { | ||
949 | IWM_ERR(iwm, "Invalid STA: %d\n", sta_id); | ||
950 | return -EINVAL; | ||
951 | } | ||
952 | |||
953 | umac_cmd.id = UMAC_CMD_OPCODE_STOP_RESUME_STA_TX; | ||
954 | umac_cmd.resp = 0; | ||
955 | |||
956 | stp_res_cmd.flags = ntf->flags; | ||
957 | stp_res_cmd.sta_id = ntf->sta_id; | ||
958 | stp_res_cmd.stop_resume_tid_msk = ntf->stop_resume_tid_msk; | ||
959 | for (i = 0; i < IWM_UMAC_TID_NR; i++) | ||
960 | stp_res_cmd.last_seq_num[i] = | ||
961 | sta_info->tid_info[i].last_seq_num; | ||
962 | |||
963 | return iwm_hal_send_umac_cmd(iwm, &udma_cmd, &umac_cmd, &stp_res_cmd, | ||
964 | sizeof(struct iwm_umac_cmd_stop_resume_tx)); | ||
965 | |||
966 | } | ||
967 | |||
968 | int iwm_send_pmkid_update(struct iwm_priv *iwm, | ||
969 | struct cfg80211_pmksa *pmksa, u32 command) | ||
970 | { | ||
971 | struct iwm_umac_pmkid_update update; | ||
972 | int ret; | ||
973 | |||
974 | memset(&update, 0, sizeof(struct iwm_umac_pmkid_update)); | ||
975 | |||
976 | update.command = cpu_to_le32(command); | ||
977 | if (pmksa->bssid) | ||
978 | memcpy(&update.bssid, pmksa->bssid, ETH_ALEN); | ||
979 | if (pmksa->pmkid) | ||
980 | memcpy(&update.pmkid, pmksa->pmkid, WLAN_PMKID_LEN); | ||
981 | |||
982 | ret = iwm_send_wifi_if_cmd(iwm, &update, | ||
983 | sizeof(struct iwm_umac_pmkid_update), 0); | ||
984 | if (ret) { | ||
985 | IWM_ERR(iwm, "PMKID update command failed\n"); | ||
986 | return ret; | ||
987 | } | ||
988 | |||
989 | return 0; | ||
990 | } | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/commands.h b/drivers/net/wireless/iwmc3200wifi/commands.h index b36be2b23a3c..06af0552cd75 100644 --- a/drivers/net/wireless/iwmc3200wifi/commands.h +++ b/drivers/net/wireless/iwmc3200wifi/commands.h | |||
@@ -450,6 +450,25 @@ struct iwm_umac_cmd_stats_req { | |||
450 | __le32 flags; | 450 | __le32 flags; |
451 | } __attribute__ ((packed)); | 451 | } __attribute__ ((packed)); |
452 | 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 | } __attribute__ ((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 | __le32 command; | ||
467 | u8 bssid[ETH_ALEN]; | ||
468 | __le16 reserved; | ||
469 | u8 pmkid[WLAN_PMKID_LEN]; | ||
470 | } __attribute__ ((packed)); | ||
471 | |||
453 | /* LMAC commands */ | 472 | /* LMAC commands */ |
454 | int iwm_read_mac(struct iwm_priv *iwm, u8 *mac); | 473 | int iwm_read_mac(struct iwm_priv *iwm, u8 *mac); |
455 | int iwm_send_prio_table(struct iwm_priv *iwm); | 474 | int iwm_send_prio_table(struct iwm_priv *iwm); |
@@ -478,6 +497,10 @@ int iwm_send_umac_channel_list(struct iwm_priv *iwm); | |||
478 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, | 497 | int iwm_scan_ssids(struct iwm_priv *iwm, struct cfg80211_ssid *ssids, |
479 | int ssid_num); | 498 | int ssid_num); |
480 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len); | 499 | int iwm_scan_one_ssid(struct iwm_priv *iwm, u8 *ssid, int ssid_len); |
500 | int iwm_send_umac_stop_resume_tx(struct iwm_priv *iwm, | ||
501 | struct iwm_umac_notif_stop_resume_tx *ntf); | ||
502 | int iwm_send_pmkid_update(struct iwm_priv *iwm, | ||
503 | struct cfg80211_pmksa *pmksa, u32 command); | ||
481 | 504 | ||
482 | /* UDMA commands */ | 505 | /* UDMA commands */ |
483 | int iwm_target_reset(struct iwm_priv *iwm); | 506 | int iwm_target_reset(struct iwm_priv *iwm); |
diff --git a/drivers/net/wireless/iwmc3200wifi/debugfs.c b/drivers/net/wireless/iwmc3200wifi/debugfs.c index 1465379f900a..be992ca41cf1 100644 --- a/drivers/net/wireless/iwmc3200wifi/debugfs.c +++ b/drivers/net/wireless/iwmc3200wifi/debugfs.c | |||
@@ -158,6 +158,29 @@ static ssize_t iwm_debugfs_txq_read(struct file *filp, char __user *buffer, | |||
158 | } | 158 | } |
159 | 159 | ||
160 | spin_unlock_irqrestore(&txq->queue.lock, flags); | 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); | ||
161 | } | 184 | } |
162 | 185 | ||
163 | ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); | 186 | ret = simple_read_from_buffer(buffer, len, ppos, buf, buf_len); |
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.c b/drivers/net/wireless/iwmc3200wifi/eeprom.c index 365910fbe01e..8091421ee5e5 100644 --- a/drivers/net/wireless/iwmc3200wifi/eeprom.c +++ b/drivers/net/wireless/iwmc3200wifi/eeprom.c | |||
@@ -66,6 +66,10 @@ static struct iwm_eeprom_entry eeprom_map[] = { | |||
66 | [IWM_EEPROM_SKU_CAP] = | 66 | [IWM_EEPROM_SKU_CAP] = |
67 | {"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN}, | 67 | {"SKU capabilities", IWM_EEPROM_SKU_CAP_OFF, IWM_EEPROM_SKU_CAP_LEN}, |
68 | 68 | ||
69 | [IWM_EEPROM_FAT_CHANNELS_CAP] = | ||
70 | {"HT channels capabilities", IWM_EEPROM_FAT_CHANNELS_CAP_OFF, | ||
71 | IWM_EEPROM_FAT_CHANNELS_CAP_LEN}, | ||
72 | |||
69 | [IWM_EEPROM_CALIB_RXIQ_OFFSET] = | 73 | [IWM_EEPROM_CALIB_RXIQ_OFFSET] = |
70 | {"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN}, | 74 | {"RX IQ offset", IWM_EEPROM_CALIB_RXIQ_OFF, IWM_EEPROM_INDIRECT_LEN}, |
71 | 75 | ||
@@ -146,6 +150,52 @@ u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id) | |||
146 | return iwm->eeprom + eeprom_map[eeprom_id].offset; | 150 | return iwm->eeprom + eeprom_map[eeprom_id].offset; |
147 | } | 151 | } |
148 | 152 | ||
153 | int iwm_eeprom_fat_channels(struct iwm_priv *iwm) | ||
154 | { | ||
155 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | ||
156 | struct ieee80211_supported_band *band; | ||
157 | u16 *channels, i; | ||
158 | |||
159 | channels = (u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_FAT_CHANNELS_CAP); | ||
160 | if (IS_ERR(channels)) | ||
161 | return PTR_ERR(channels); | ||
162 | |||
163 | band = wiphy->bands[IEEE80211_BAND_2GHZ]; | ||
164 | band->ht_cap.ht_supported = true; | ||
165 | |||
166 | for (i = 0; i < IWM_EEPROM_FAT_CHANNELS_24; i++) | ||
167 | if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) | ||
168 | band->ht_cap.ht_supported = false; | ||
169 | |||
170 | band = wiphy->bands[IEEE80211_BAND_5GHZ]; | ||
171 | band->ht_cap.ht_supported = true; | ||
172 | for (i = IWM_EEPROM_FAT_CHANNELS_24; i < IWM_EEPROM_FAT_CHANNELS; i++) | ||
173 | if (!(channels[i] & IWM_EEPROM_FAT_CHANNEL_ENABLED)) | ||
174 | band->ht_cap.ht_supported = false; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | u32 iwm_eeprom_wireless_mode(struct iwm_priv *iwm) | ||
180 | { | ||
181 | u16 sku_cap; | ||
182 | u32 wireless_mode = 0; | ||
183 | |||
184 | sku_cap = *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP)); | ||
185 | |||
186 | if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_24GHZ) | ||
187 | wireless_mode |= WIRELESS_MODE_11G; | ||
188 | |||
189 | if (sku_cap & IWM_EEPROM_SKU_CAP_BAND_52GHZ) | ||
190 | wireless_mode |= WIRELESS_MODE_11A; | ||
191 | |||
192 | if (sku_cap & IWM_EEPROM_SKU_CAP_11N_ENABLE) | ||
193 | wireless_mode |= WIRELESS_MODE_11N; | ||
194 | |||
195 | return wireless_mode; | ||
196 | } | ||
197 | |||
198 | |||
149 | int iwm_eeprom_init(struct iwm_priv *iwm) | 199 | int iwm_eeprom_init(struct iwm_priv *iwm) |
150 | { | 200 | { |
151 | int i, ret = 0; | 201 | int i, ret = 0; |
diff --git a/drivers/net/wireless/iwmc3200wifi/eeprom.h b/drivers/net/wireless/iwmc3200wifi/eeprom.h index cdb31a6a1f5f..4e3a3fdab0d3 100644 --- a/drivers/net/wireless/iwmc3200wifi/eeprom.h +++ b/drivers/net/wireless/iwmc3200wifi/eeprom.h | |||
@@ -48,6 +48,7 @@ enum { | |||
48 | IWM_EEPROM_CARD_ID, | 48 | IWM_EEPROM_CARD_ID, |
49 | IWM_EEPROM_RADIO_CONF, | 49 | IWM_EEPROM_RADIO_CONF, |
50 | IWM_EEPROM_SKU_CAP, | 50 | IWM_EEPROM_SKU_CAP, |
51 | IWM_EEPROM_FAT_CHANNELS_CAP, | ||
51 | 52 | ||
52 | IWM_EEPROM_INDIRECT_OFFSET, | 53 | IWM_EEPROM_INDIRECT_OFFSET, |
53 | IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET, | 54 | IWM_EEPROM_CALIB_RXIQ_OFFSET = IWM_EEPROM_INDIRECT_OFFSET, |
@@ -58,14 +59,15 @@ enum { | |||
58 | IWM_EEPROM_LAST, | 59 | IWM_EEPROM_LAST, |
59 | }; | 60 | }; |
60 | 61 | ||
61 | #define IWM_EEPROM_SIG_OFF 0x00 | 62 | #define IWM_EEPROM_SIG_OFF 0x00 |
62 | #define IWM_EEPROM_VERSION_OFF (0x54 << 1) | 63 | #define IWM_EEPROM_VERSION_OFF (0x54 << 1) |
63 | #define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1) | 64 | #define IWM_EEPROM_OEM_HW_VERSION_OFF (0x56 << 1) |
64 | #define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1) | 65 | #define IWM_EEPROM_MAC_VERSION_OFF (0x30 << 1) |
65 | #define IWM_EEPROM_CARD_ID_OFF (0x5d << 1) | 66 | #define IWM_EEPROM_CARD_ID_OFF (0x5d << 1) |
66 | #define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1) | 67 | #define IWM_EEPROM_RADIO_CONF_OFF (0x58 << 1) |
67 | #define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1) | 68 | #define IWM_EEPROM_SKU_CAP_OFF (0x55 << 1) |
68 | #define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1) | 69 | #define IWM_EEPROM_CALIB_CONFIG_OFF (0x7c << 1) |
70 | #define IWM_EEPROM_FAT_CHANNELS_CAP_OFF (0xde << 1) | ||
69 | 71 | ||
70 | #define IWM_EEPROM_SIG_LEN 4 | 72 | #define IWM_EEPROM_SIG_LEN 4 |
71 | #define IWM_EEPROM_VERSION_LEN 2 | 73 | #define IWM_EEPROM_VERSION_LEN 2 |
@@ -74,6 +76,7 @@ enum { | |||
74 | #define IWM_EEPROM_CARD_ID_LEN 2 | 76 | #define IWM_EEPROM_CARD_ID_LEN 2 |
75 | #define IWM_EEPROM_RADIO_CONF_LEN 2 | 77 | #define IWM_EEPROM_RADIO_CONF_LEN 2 |
76 | #define IWM_EEPROM_SKU_CAP_LEN 2 | 78 | #define IWM_EEPROM_SKU_CAP_LEN 2 |
79 | #define IWM_EEPROM_FAT_CHANNELS_CAP_LEN 40 | ||
77 | #define IWM_EEPROM_INDIRECT_LEN 2 | 80 | #define IWM_EEPROM_INDIRECT_LEN 2 |
78 | 81 | ||
79 | #define IWM_MAX_EEPROM_DATA_LEN 240 | 82 | #define IWM_MAX_EEPROM_DATA_LEN 240 |
@@ -87,6 +90,14 @@ enum { | |||
87 | #define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) | 90 | #define IWM_EEPROM_SKU_CAP_BAND_52GHZ (1 << 5) |
88 | #define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6) | 91 | #define IWM_EEPROM_SKU_CAP_11N_ENABLE (1 << 6) |
89 | 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 | |||
90 | enum { | 101 | enum { |
91 | IWM_EEPROM_CALIB_CAL_HDR, | 102 | IWM_EEPROM_CALIB_CAL_HDR, |
92 | IWM_EEPROM_CALIB_TX_POWER, | 103 | IWM_EEPROM_CALIB_TX_POWER, |
@@ -110,5 +121,7 @@ struct iwm_eeprom_entry { | |||
110 | int iwm_eeprom_init(struct iwm_priv *iwm); | 121 | int iwm_eeprom_init(struct iwm_priv *iwm); |
111 | void iwm_eeprom_exit(struct iwm_priv *iwm); | 122 | void iwm_eeprom_exit(struct iwm_priv *iwm); |
112 | u8 *iwm_eeprom_access(struct iwm_priv *iwm, u8 eeprom_id); | 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); | ||
113 | 126 | ||
114 | #endif | 127 | #endif |
diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index a9bf6bc97bea..5a26bb05a33a 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h | |||
@@ -81,7 +81,6 @@ struct iwm_conf { | |||
81 | u32 assoc_timeout; | 81 | u32 assoc_timeout; |
82 | u32 roam_timeout; | 82 | u32 roam_timeout; |
83 | u32 wireless_mode; | 83 | u32 wireless_mode; |
84 | u32 coexist_mode; | ||
85 | 84 | ||
86 | u8 ibss_band; | 85 | u8 ibss_band; |
87 | u8 ibss_channel; | 86 | u8 ibss_channel; |
@@ -131,11 +130,18 @@ struct iwm_notif { | |||
131 | unsigned long buf_size; | 130 | unsigned long buf_size; |
132 | }; | 131 | }; |
133 | 132 | ||
133 | struct iwm_tid_info { | ||
134 | __le16 last_seq_num; | ||
135 | bool stopped; | ||
136 | struct mutex mutex; | ||
137 | }; | ||
138 | |||
134 | struct iwm_sta_info { | 139 | struct iwm_sta_info { |
135 | u8 addr[ETH_ALEN]; | 140 | u8 addr[ETH_ALEN]; |
136 | bool valid; | 141 | bool valid; |
137 | bool qos; | 142 | bool qos; |
138 | u8 color; | 143 | u8 color; |
144 | struct iwm_tid_info tid_info[IWM_UMAC_TID_NR]; | ||
139 | }; | 145 | }; |
140 | 146 | ||
141 | struct iwm_tx_info { | 147 | struct iwm_tx_info { |
@@ -185,6 +191,8 @@ struct iwm_key { | |||
185 | struct iwm_tx_queue { | 191 | struct iwm_tx_queue { |
186 | int id; | 192 | int id; |
187 | struct sk_buff_head queue; | 193 | struct sk_buff_head queue; |
194 | struct sk_buff_head stopped_queue; | ||
195 | spinlock_t lock; | ||
188 | struct workqueue_struct *wq; | 196 | struct workqueue_struct *wq; |
189 | struct work_struct worker; | 197 | struct work_struct worker; |
190 | u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE]; | 198 | u8 concat_buf[IWM_HAL_CONCATENATE_BUF_SIZE]; |
@@ -341,6 +349,7 @@ int iwm_up(struct iwm_priv *iwm); | |||
341 | int iwm_down(struct iwm_priv *iwm); | 349 | int iwm_down(struct iwm_priv *iwm); |
342 | 350 | ||
343 | /* TX API */ | 351 | /* TX API */ |
352 | u16 iwm_tid_to_queue(u16 tid); | ||
344 | void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages); | 353 | void iwm_tx_credit_inc(struct iwm_priv *iwm, int id, int total_freed_pages); |
345 | void iwm_tx_worker(struct work_struct *work); | 354 | void iwm_tx_worker(struct work_struct *work); |
346 | int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev); | 355 | int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev); |
diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index 75f105a59543..7f34d6dd3c41 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c | |||
@@ -68,7 +68,6 @@ static struct iwm_conf def_iwm_conf = { | |||
68 | .ct_kill_exit = 110, | 68 | .ct_kill_exit = 110, |
69 | .reset_on_fatal_err = 1, | 69 | .reset_on_fatal_err = 1, |
70 | .auto_connect = 1, | 70 | .auto_connect = 1, |
71 | .wimax_not_present = 0, | ||
72 | .enable_qos = 1, | 71 | .enable_qos = 1, |
73 | .mode = UMAC_MODE_BSS, | 72 | .mode = UMAC_MODE_BSS, |
74 | 73 | ||
@@ -80,8 +79,8 @@ static struct iwm_conf def_iwm_conf = { | |||
80 | 79 | ||
81 | .assoc_timeout = 2, | 80 | .assoc_timeout = 2, |
82 | .roam_timeout = 10, | 81 | .roam_timeout = 10, |
83 | .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G, | 82 | .wireless_mode = WIRELESS_MODE_11A | WIRELESS_MODE_11G | |
84 | .coexist_mode = COEX_MODE_CM, | 83 | WIRELESS_MODE_11N, |
85 | 84 | ||
86 | /* IBSS */ | 85 | /* IBSS */ |
87 | .ibss_band = UMAC_BAND_2GHZ, | 86 | .ibss_band = UMAC_BAND_2GHZ, |
@@ -94,6 +93,10 @@ static int modparam_reset; | |||
94 | module_param_named(reset, modparam_reset, bool, 0644); | 93 | module_param_named(reset, modparam_reset, bool, 0644); |
95 | MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])"); | 94 | MODULE_PARM_DESC(reset, "reset on firmware errors (default 0 [not reset])"); |
96 | 95 | ||
96 | static int modparam_wimax_enable = 1; | ||
97 | module_param_named(wimax_enable, modparam_wimax_enable, bool, 0644); | ||
98 | MODULE_PARM_DESC(wimax_enable, "Enable wimax core (default 1 [wimax enabled])"); | ||
99 | |||
97 | int iwm_mode_to_nl80211_iftype(int mode) | 100 | int iwm_mode_to_nl80211_iftype(int mode) |
98 | { | 101 | { |
99 | switch (mode) { | 102 | switch (mode) { |
@@ -247,7 +250,7 @@ static void iwm_watchdog(unsigned long data) | |||
247 | 250 | ||
248 | int iwm_priv_init(struct iwm_priv *iwm) | 251 | int iwm_priv_init(struct iwm_priv *iwm) |
249 | { | 252 | { |
250 | int i; | 253 | int i, j; |
251 | char name[32]; | 254 | char name[32]; |
252 | 255 | ||
253 | iwm->status = 0; | 256 | iwm->status = 0; |
@@ -291,6 +294,8 @@ int iwm_priv_init(struct iwm_priv *iwm) | |||
291 | return -EAGAIN; | 294 | return -EAGAIN; |
292 | 295 | ||
293 | skb_queue_head_init(&iwm->txq[i].queue); | 296 | skb_queue_head_init(&iwm->txq[i].queue); |
297 | skb_queue_head_init(&iwm->txq[i].stopped_queue); | ||
298 | spin_lock_init(&iwm->txq[i].lock); | ||
294 | } | 299 | } |
295 | 300 | ||
296 | for (i = 0; i < IWM_NUM_KEYS; i++) | 301 | for (i = 0; i < IWM_NUM_KEYS; i++) |
@@ -298,6 +303,12 @@ int iwm_priv_init(struct iwm_priv *iwm) | |||
298 | 303 | ||
299 | iwm->default_key = -1; | 304 | iwm->default_key = -1; |
300 | 305 | ||
306 | for (i = 0; i < IWM_STA_TABLE_NUM; i++) | ||
307 | for (j = 0; j < IWM_UMAC_TID_NR; j++) { | ||
308 | mutex_init(&iwm->sta_table[i].tid_info[j].mutex); | ||
309 | iwm->sta_table[i].tid_info[j].stopped = false; | ||
310 | } | ||
311 | |||
301 | init_timer(&iwm->watchdog); | 312 | init_timer(&iwm->watchdog); |
302 | iwm->watchdog.function = iwm_watchdog; | 313 | iwm->watchdog.function = iwm_watchdog; |
303 | iwm->watchdog.data = (unsigned long)iwm; | 314 | iwm->watchdog.data = (unsigned long)iwm; |
@@ -478,7 +489,7 @@ static int iwm_config_boot_params(struct iwm_priv *iwm) | |||
478 | int ret; | 489 | int ret; |
479 | 490 | ||
480 | /* check Wimax is off and config debug monitor */ | 491 | /* check Wimax is off and config debug monitor */ |
481 | if (iwm->conf.wimax_not_present) { | 492 | if (!modparam_wimax_enable) { |
482 | u32 data1 = 0x1f; | 493 | u32 data1 = 0x1f; |
483 | u32 addr1 = 0x606BE258; | 494 | u32 addr1 = 0x606BE258; |
484 | 495 | ||
@@ -571,6 +582,7 @@ void iwm_link_off(struct iwm_priv *iwm) | |||
571 | 582 | ||
572 | for (i = 0; i < IWM_TX_QUEUES; i++) { | 583 | for (i = 0; i < IWM_TX_QUEUES; i++) { |
573 | skb_queue_purge(&iwm->txq[i].queue); | 584 | skb_queue_purge(&iwm->txq[i].queue); |
585 | skb_queue_purge(&iwm->txq[i].stopped_queue); | ||
574 | 586 | ||
575 | iwm->txq[i].concat_count = 0; | 587 | iwm->txq[i].concat_count = 0; |
576 | iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf; | 588 | iwm->txq[i].concat_ptr = iwm->txq[i].concat_buf; |
@@ -630,6 +642,7 @@ static int __iwm_up(struct iwm_priv *iwm) | |||
630 | int ret; | 642 | int ret; |
631 | struct iwm_notif *notif_reboot, *notif_ack = NULL; | 643 | struct iwm_notif *notif_reboot, *notif_ack = NULL; |
632 | struct wiphy *wiphy = iwm_to_wiphy(iwm); | 644 | struct wiphy *wiphy = iwm_to_wiphy(iwm); |
645 | u32 wireless_mode; | ||
633 | 646 | ||
634 | ret = iwm_bus_enable(iwm); | 647 | ret = iwm_bus_enable(iwm); |
635 | if (ret) { | 648 | if (ret) { |
@@ -691,6 +704,27 @@ static int __iwm_up(struct iwm_priv *iwm) | |||
691 | goto err_disable; | 704 | goto err_disable; |
692 | } | 705 | } |
693 | 706 | ||
707 | ret = iwm_eeprom_fat_channels(iwm); | ||
708 | if (ret) { | ||
709 | IWM_ERR(iwm, "Couldnt read HT channels EEPROM entries\n"); | ||
710 | goto err_fw; | ||
711 | } | ||
712 | |||
713 | /* | ||
714 | * Read our SKU capabilities. | ||
715 | * If it's valid, we AND the configured wireless mode with the | ||
716 | * device EEPROM value as the current profile wireless mode. | ||
717 | */ | ||
718 | wireless_mode = iwm_eeprom_wireless_mode(iwm); | ||
719 | if (wireless_mode) { | ||
720 | iwm->conf.wireless_mode &= wireless_mode; | ||
721 | if (iwm->umac_profile) | ||
722 | iwm->umac_profile->wireless_mode = | ||
723 | iwm->conf.wireless_mode; | ||
724 | } else | ||
725 | IWM_ERR(iwm, "Wrong SKU capabilities: 0x%x\n", | ||
726 | *((u16 *)iwm_eeprom_access(iwm, IWM_EEPROM_SKU_CAP))); | ||
727 | |||
694 | snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "L%s_U%s", | 728 | snprintf(wiphy->fw_version, sizeof(wiphy->fw_version), "L%s_U%s", |
695 | iwm->lmac_version, iwm->umac_version); | 729 | iwm->lmac_version, iwm->umac_version); |
696 | 730 | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index 4f8dbdd7b917..e4f0f8705f65 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c | |||
@@ -76,6 +76,14 @@ static int iwm_stop(struct net_device *ndev) | |||
76 | */ | 76 | */ |
77 | static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; | 77 | static const u16 iwm_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; |
78 | 78 | ||
79 | u16 iwm_tid_to_queue(u16 tid) | ||
80 | { | ||
81 | if (tid > IWM_UMAC_TID_NR - 2) | ||
82 | return -EINVAL; | ||
83 | |||
84 | return iwm_1d_to_queue[tid]; | ||
85 | } | ||
86 | |||
79 | static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb) | 87 | static u16 iwm_select_queue(struct net_device *dev, struct sk_buff *skb) |
80 | { | 88 | { |
81 | skb->priority = cfg80211_classify8021d(skb); | 89 | skb->priority = cfg80211_classify8021d(skb); |
diff --git a/drivers/net/wireless/iwmc3200wifi/rx.c b/drivers/net/wireless/iwmc3200wifi/rx.c index bdb1d7e7979d..72c27a3e5528 100644 --- a/drivers/net/wireless/iwmc3200wifi/rx.c +++ b/drivers/net/wireless/iwmc3200wifi/rx.c | |||
@@ -1087,6 +1087,71 @@ static int iwm_ntf_channel_info_list(struct iwm_priv *iwm, u8 *buf, | |||
1087 | return 0; | 1087 | return 0; |
1088 | } | 1088 | } |
1089 | 1089 | ||
1090 | static int iwm_ntf_stop_resume_tx(struct iwm_priv *iwm, u8 *buf, | ||
1091 | unsigned long buf_size, | ||
1092 | struct iwm_wifi_cmd *cmd) | ||
1093 | { | ||
1094 | struct iwm_umac_notif_stop_resume_tx *stp_res_tx = | ||
1095 | (struct iwm_umac_notif_stop_resume_tx *)buf; | ||
1096 | struct iwm_sta_info *sta_info; | ||
1097 | struct iwm_tid_info *tid_info; | ||
1098 | u8 sta_id = STA_ID_N_COLOR_ID(stp_res_tx->sta_id); | ||
1099 | u16 tid_msk = le16_to_cpu(stp_res_tx->stop_resume_tid_msk); | ||
1100 | int bit, ret = 0; | ||
1101 | bool stop = false; | ||
1102 | |||
1103 | IWM_DBG_NTF(iwm, DBG, "stop/resume notification:\n" | ||
1104 | "\tflags: 0x%x\n" | ||
1105 | "\tSTA id: %d\n" | ||
1106 | "\tTID bitmask: 0x%x\n", | ||
1107 | stp_res_tx->flags, stp_res_tx->sta_id, | ||
1108 | stp_res_tx->stop_resume_tid_msk); | ||
1109 | |||
1110 | if (stp_res_tx->flags & UMAC_STOP_TX_FLAG) | ||
1111 | stop = true; | ||
1112 | |||
1113 | sta_info = &iwm->sta_table[sta_id]; | ||
1114 | if (!sta_info->valid) { | ||
1115 | IWM_ERR(iwm, "Stoping an invalid STA: %d %d\n", | ||
1116 | sta_id, stp_res_tx->sta_id); | ||
1117 | return -EINVAL; | ||
1118 | } | ||
1119 | |||
1120 | for_each_bit(bit, (unsigned long *)&tid_msk, IWM_UMAC_TID_NR) { | ||
1121 | tid_info = &sta_info->tid_info[bit]; | ||
1122 | |||
1123 | mutex_lock(&tid_info->mutex); | ||
1124 | tid_info->stopped = stop; | ||
1125 | mutex_unlock(&tid_info->mutex); | ||
1126 | |||
1127 | if (!stop) { | ||
1128 | struct iwm_tx_queue *txq; | ||
1129 | u16 queue = iwm_tid_to_queue(bit); | ||
1130 | |||
1131 | if (queue < 0) | ||
1132 | continue; | ||
1133 | |||
1134 | txq = &iwm->txq[queue]; | ||
1135 | /* | ||
1136 | * If we resume, we have to move our SKBs | ||
1137 | * back to the tx queue and queue some work. | ||
1138 | */ | ||
1139 | spin_lock_bh(&txq->lock); | ||
1140 | skb_queue_splice_init(&txq->queue, &txq->stopped_queue); | ||
1141 | spin_unlock_bh(&txq->lock); | ||
1142 | |||
1143 | queue_work(txq->wq, &txq->worker); | ||
1144 | } | ||
1145 | |||
1146 | } | ||
1147 | |||
1148 | /* We send an ACK only for the stop case */ | ||
1149 | if (stop) | ||
1150 | ret = iwm_send_umac_stop_resume_tx(iwm, stp_res_tx); | ||
1151 | |||
1152 | return ret; | ||
1153 | } | ||
1154 | |||
1090 | static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, | 1155 | static int iwm_ntf_wifi_if_wrapper(struct iwm_priv *iwm, u8 *buf, |
1091 | unsigned long buf_size, | 1156 | unsigned long buf_size, |
1092 | struct iwm_wifi_cmd *cmd) | 1157 | struct iwm_wifi_cmd *cmd) |
@@ -1371,6 +1436,7 @@ static const iwm_handler iwm_umac_handlers[] = | |||
1371 | [UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics, | 1436 | [UMAC_NOTIFY_OPCODE_STATS] = iwm_ntf_statistics, |
1372 | [UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy, | 1437 | [UMAC_CMD_OPCODE_EEPROM_PROXY] = iwm_ntf_eeprom_proxy, |
1373 | [UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list, | 1438 | [UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST] = iwm_ntf_channel_info_list, |
1439 | [UMAC_CMD_OPCODE_STOP_RESUME_STA_TX] = iwm_ntf_stop_resume_tx, | ||
1374 | [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet, | 1440 | [REPLY_RX_MPDU_CMD] = iwm_ntf_rx_packet, |
1375 | [UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper, | 1441 | [UMAC_CMD_OPCODE_WIFI_IF_WRAPPER] = iwm_ntf_wifi_if_wrapper, |
1376 | }; | 1442 | }; |
diff --git a/drivers/net/wireless/iwmc3200wifi/tx.c b/drivers/net/wireless/iwmc3200wifi/tx.c index e3b4f7902daf..55905f02309c 100644 --- a/drivers/net/wireless/iwmc3200wifi/tx.c +++ b/drivers/net/wireless/iwmc3200wifi/tx.c | |||
@@ -329,7 +329,7 @@ static int iwm_tx_build_packet(struct iwm_priv *iwm, struct sk_buff *skb, | |||
329 | 329 | ||
330 | memcpy(buf + sizeof(*hdr), skb->data, skb->len); | 330 | memcpy(buf + sizeof(*hdr), skb->data, skb->len); |
331 | 331 | ||
332 | return 0; | 332 | return umac_cmd.seq_num; |
333 | } | 333 | } |
334 | 334 | ||
335 | static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, | 335 | static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, |
@@ -354,16 +354,15 @@ static int iwm_tx_send_concat_packets(struct iwm_priv *iwm, | |||
354 | return ret; | 354 | return ret; |
355 | } | 355 | } |
356 | 356 | ||
357 | #define CONFIG_IWM_TX_CONCATENATED 1 | ||
358 | |||
359 | void iwm_tx_worker(struct work_struct *work) | 357 | void iwm_tx_worker(struct work_struct *work) |
360 | { | 358 | { |
361 | struct iwm_priv *iwm; | 359 | struct iwm_priv *iwm; |
362 | struct iwm_tx_info *tx_info = NULL; | 360 | struct iwm_tx_info *tx_info = NULL; |
363 | struct sk_buff *skb; | 361 | struct sk_buff *skb; |
364 | int cmdlen, ret; | ||
365 | struct iwm_tx_queue *txq; | 362 | struct iwm_tx_queue *txq; |
366 | int pool_id; | 363 | struct iwm_sta_info *sta_info; |
364 | struct iwm_tid_info *tid_info; | ||
365 | int cmdlen, ret, pool_id; | ||
367 | 366 | ||
368 | txq = container_of(work, struct iwm_tx_queue, worker); | 367 | txq = container_of(work, struct iwm_tx_queue, worker); |
369 | iwm = container_of(txq, struct iwm_priv, txq[txq->id]); | 368 | iwm = container_of(txq, struct iwm_priv, txq[txq->id]); |
@@ -373,19 +372,46 @@ void iwm_tx_worker(struct work_struct *work) | |||
373 | while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) && | 372 | while (!test_bit(pool_id, &iwm->tx_credit.full_pools_map) && |
374 | !skb_queue_empty(&txq->queue)) { | 373 | !skb_queue_empty(&txq->queue)) { |
375 | 374 | ||
375 | spin_lock_bh(&txq->lock); | ||
376 | skb = skb_dequeue(&txq->queue); | 376 | skb = skb_dequeue(&txq->queue); |
377 | spin_unlock_bh(&txq->lock); | ||
378 | |||
377 | tx_info = skb_to_tx_info(skb); | 379 | tx_info = skb_to_tx_info(skb); |
380 | sta_info = &iwm->sta_table[tx_info->sta]; | ||
381 | if (!sta_info->valid) { | ||
382 | IWM_ERR(iwm, "Trying to send a frame to unknown STA\n"); | ||
383 | kfree_skb(skb); | ||
384 | continue; | ||
385 | } | ||
386 | |||
387 | tid_info = &sta_info->tid_info[tx_info->tid]; | ||
388 | |||
389 | mutex_lock(&tid_info->mutex); | ||
390 | |||
391 | /* | ||
392 | * If the RAxTID is stopped, we queue the skb to the stopped | ||
393 | * queue. | ||
394 | * Whenever we'll get a UMAC notification to resume the tx flow | ||
395 | * for this RAxTID, we'll merge back the stopped queue into the | ||
396 | * regular queue. See iwm_ntf_stop_resume_tx() from rx.c. | ||
397 | */ | ||
398 | if (tid_info->stopped) { | ||
399 | IWM_DBG_TX(iwm, DBG, "%dx%d stopped\n", | ||
400 | tx_info->sta, tx_info->tid); | ||
401 | spin_lock_bh(&txq->lock); | ||
402 | skb_queue_tail(&txq->stopped_queue, skb); | ||
403 | spin_unlock_bh(&txq->lock); | ||
404 | |||
405 | mutex_unlock(&tid_info->mutex); | ||
406 | continue; | ||
407 | } | ||
408 | |||
378 | cmdlen = IWM_UDMA_HDR_LEN + skb->len; | 409 | cmdlen = IWM_UDMA_HDR_LEN + skb->len; |
379 | 410 | ||
380 | IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: " | 411 | IWM_DBG_TX(iwm, DBG, "Tx frame on queue %d: skb: 0x%p, sta: " |
381 | "%d, color: %d\n", txq->id, skb, tx_info->sta, | 412 | "%d, color: %d\n", txq->id, skb, tx_info->sta, |
382 | tx_info->color); | 413 | tx_info->color); |
383 | 414 | ||
384 | #if !CONFIG_IWM_TX_CONCATENATED | ||
385 | /* temporarily keep this to comparing the performance */ | ||
386 | ret = iwm_send_packet(iwm, skb, pool_id); | ||
387 | #else | ||
388 | |||
389 | if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE) | 415 | if (txq->concat_count + cmdlen > IWM_HAL_CONCATENATE_BUF_SIZE) |
390 | iwm_tx_send_concat_packets(iwm, txq); | 416 | iwm_tx_send_concat_packets(iwm, txq); |
391 | 417 | ||
@@ -393,14 +419,21 @@ void iwm_tx_worker(struct work_struct *work) | |||
393 | if (ret) { | 419 | if (ret) { |
394 | IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue " | 420 | IWM_DBG_TX(iwm, DBG, "not enough tx_credit for queue " |
395 | "%d, Tx worker stopped\n", txq->id); | 421 | "%d, Tx worker stopped\n", txq->id); |
422 | spin_lock_bh(&txq->lock); | ||
396 | skb_queue_head(&txq->queue, skb); | 423 | skb_queue_head(&txq->queue, skb); |
424 | spin_unlock_bh(&txq->lock); | ||
425 | |||
426 | mutex_unlock(&tid_info->mutex); | ||
397 | break; | 427 | break; |
398 | } | 428 | } |
399 | 429 | ||
400 | txq->concat_ptr = txq->concat_buf + txq->concat_count; | 430 | txq->concat_ptr = txq->concat_buf + txq->concat_count; |
401 | iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr); | 431 | tid_info->last_seq_num = |
432 | iwm_tx_build_packet(iwm, skb, pool_id, txq->concat_ptr); | ||
402 | txq->concat_count += ALIGN(cmdlen, 16); | 433 | txq->concat_count += ALIGN(cmdlen, 16); |
403 | #endif | 434 | |
435 | mutex_unlock(&tid_info->mutex); | ||
436 | |||
404 | kfree_skb(skb); | 437 | kfree_skb(skb); |
405 | } | 438 | } |
406 | 439 | ||
@@ -419,14 +452,14 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) | |||
419 | struct iwm_priv *iwm = ndev_to_iwm(netdev); | 452 | struct iwm_priv *iwm = ndev_to_iwm(netdev); |
420 | struct net_device *ndev = iwm_to_ndev(iwm); | 453 | struct net_device *ndev = iwm_to_ndev(iwm); |
421 | struct wireless_dev *wdev = iwm_to_wdev(iwm); | 454 | struct wireless_dev *wdev = iwm_to_wdev(iwm); |
422 | u8 *dst_addr; | ||
423 | struct iwm_tx_info *tx_info; | 455 | struct iwm_tx_info *tx_info; |
424 | struct iwm_tx_queue *txq; | 456 | struct iwm_tx_queue *txq; |
425 | struct iwm_sta_info *sta_info; | 457 | struct iwm_sta_info *sta_info; |
426 | u8 sta_id; | 458 | u8 *dst_addr, sta_id; |
427 | u16 queue; | 459 | u16 queue; |
428 | int ret; | 460 | int ret; |
429 | 461 | ||
462 | |||
430 | if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { | 463 | if (!test_bit(IWM_STATUS_ASSOCIATED, &iwm->status)) { |
431 | IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: " | 464 | IWM_DBG_TX(iwm, DBG, "LINK: stop netif_all_queues: " |
432 | "not associated\n"); | 465 | "not associated\n"); |
@@ -440,7 +473,8 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) | |||
440 | txq = &iwm->txq[queue]; | 473 | txq = &iwm->txq[queue]; |
441 | 474 | ||
442 | /* No free space for Tx, tx_worker is too slow */ | 475 | /* No free space for Tx, tx_worker is too slow */ |
443 | if (skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) { | 476 | if ((skb_queue_len(&txq->queue) > IWM_TX_LIST_SIZE) || |
477 | (skb_queue_len(&txq->stopped_queue) > IWM_TX_LIST_SIZE)) { | ||
444 | IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue); | 478 | IWM_DBG_TX(iwm, DBG, "LINK: stop netif_subqueue[%d]\n", queue); |
445 | netif_stop_subqueue(netdev, queue); | 479 | netif_stop_subqueue(netdev, queue); |
446 | return NETDEV_TX_BUSY; | 480 | return NETDEV_TX_BUSY; |
@@ -477,7 +511,9 @@ int iwm_xmit_frame(struct sk_buff *skb, struct net_device *netdev) | |||
477 | else | 511 | else |
478 | tx_info->tid = IWM_UMAC_MGMT_TID; | 512 | tx_info->tid = IWM_UMAC_MGMT_TID; |
479 | 513 | ||
514 | spin_lock_bh(&iwm->txq[queue].lock); | ||
480 | skb_queue_tail(&iwm->txq[queue].queue, skb); | 515 | skb_queue_tail(&iwm->txq[queue].queue, skb); |
516 | spin_unlock_bh(&iwm->txq[queue].lock); | ||
481 | 517 | ||
482 | queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); | 518 | queue_work(iwm->txq[queue].wq, &iwm->txq[queue].worker); |
483 | 519 | ||
diff --git a/drivers/net/wireless/iwmc3200wifi/umac.h b/drivers/net/wireless/iwmc3200wifi/umac.h index be903543bb47..7f54a145ca65 100644 --- a/drivers/net/wireless/iwmc3200wifi/umac.h +++ b/drivers/net/wireless/iwmc3200wifi/umac.h | |||
@@ -83,6 +83,20 @@ struct iwm_udma_out_wifi_hdr { | |||
83 | ((UMAC_HDI_ACT_TBL_IDX_RA_UMAC << UMAC_HDI_ACT_TBL_IDX_RA_POS) |\ | 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)) | 84 | (UMAC_HDI_ACT_TBL_IDX_TID_LMAC << UMAC_HDI_ACT_TBL_IDX_TID_POS)) |
85 | 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 | |||
86 | /* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */ | 100 | /* iwm_umac_notif_alive.page_grp_state Group number -- bits [3:0] */ |
87 | #define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0 | 101 | #define UMAC_ALIVE_PAGE_STS_GRP_NUM_POS 0 |
88 | #define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF | 102 | #define UMAC_ALIVE_PAGE_STS_GRP_NUM_SEED 0xF |
@@ -260,6 +274,9 @@ struct iwm_udma_out_wifi_hdr { | |||
260 | #define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16 | 274 | #define UMAC_CMD_OPCODE_GET_CHAN_INFO_LIST 0x16 |
261 | #define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17 | 275 | #define UMAC_CMD_OPCODE_SET_PARAM_LIST 0x17 |
262 | #define UMAC_CMD_OPCODE_GET_PARAM_LIST 0x18 | 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 | |||
263 | #define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA | 280 | #define UMAC_CMD_OPCODE_BASE_WRAPPER 0xFA |
264 | #define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB | 281 | #define UMAC_CMD_OPCODE_LMAC_WRAPPER 0xFB |
265 | #define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC | 282 | #define UMAC_CMD_OPCODE_HW_TEST_WRAPPER 0xFC |
@@ -281,6 +298,7 @@ struct iwm_udma_out_wifi_hdr { | |||
281 | #define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B | 298 | #define UMAC_WIFI_IF_CMD_GLOBAL_TX_KEY_ID 0x1B |
282 | #define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C | 299 | #define UMAC_WIFI_IF_CMD_SET_HOST_EXTENDED_IE 0x1C |
283 | #define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E | 300 | #define UMAC_WIFI_IF_CMD_GET_SUPPORTED_CHANNELS 0x1E |
301 | #define UMAC_WIFI_IF_CMD_PMKID_UPDATE 0x1F | ||
284 | #define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20 | 302 | #define UMAC_WIFI_IF_CMD_TX_PWR_TRIGGER 0x20 |
285 | 303 | ||
286 | /* UMAC WiFi interface ports */ | 304 | /* UMAC WiFi interface ports */ |
@@ -691,13 +709,13 @@ struct iwm_umac_notif_rx_ticket { | |||
691 | #define UMAC_PHY_NUM_CHAINS 3 | 709 | #define UMAC_PHY_NUM_CHAINS 3 |
692 | 710 | ||
693 | #define IWM_UMAC_MGMT_TID 8 | 711 | #define IWM_UMAC_MGMT_TID 8 |
694 | #define IWM_UMAC_TID_NR 8 | 712 | #define IWM_UMAC_TID_NR 9 /* 8 TIDs + MGMT */ |
695 | 713 | ||
696 | struct iwm_umac_notif_stats { | 714 | struct iwm_umac_notif_stats { |
697 | struct iwm_umac_wifi_in_hdr hdr; | 715 | struct iwm_umac_wifi_in_hdr hdr; |
698 | __le32 flags; | 716 | __le32 flags; |
699 | __le32 timestamp; | 717 | __le32 timestamp; |
700 | __le16 tid_load[IWM_UMAC_TID_NR + 2]; /* 1 non-QoS + 1 dword align */ | 718 | __le16 tid_load[IWM_UMAC_TID_NR + 1]; /* 1 non-QoS + 1 dword align */ |
701 | __le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR]; | 719 | __le16 tx_rate[UMAC_NTF_RATE_SAMPLE_NR]; |
702 | __le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR]; | 720 | __le16 rx_rate[UMAC_NTF_RATE_SAMPLE_NR]; |
703 | __le32 chain_energy[UMAC_PHY_NUM_CHAINS]; | 721 | __le32 chain_energy[UMAC_PHY_NUM_CHAINS]; |
@@ -742,6 +760,20 @@ struct iwm_umac_notif_stats { | |||
742 | __le32 roam_ap_loadblance; | 760 | __le32 roam_ap_loadblance; |
743 | } __attribute__ ((packed)); | 761 | } __attribute__ ((packed)); |
744 | 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 | } __attribute__ ((packed)); | ||
774 | |||
775 | #define UMAC_MAX_NUM_PMKIDS 4 | ||
776 | |||
745 | /* WiFi interface wrapper header */ | 777 | /* WiFi interface wrapper header */ |
746 | struct iwm_umac_wifi_if { | 778 | struct iwm_umac_wifi_if { |
747 | u8 oid; | 779 | u8 oid; |