aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorStefano Brivio <stefano.brivio@polimi.it>2008-02-08 00:31:25 -0500
committerJohn W. Linville <linville@tuxdriver.com>2008-02-29 15:37:06 -0500
commita297170dae2595f31b02e4553a3b217e115a15cf (patch)
treecd066108d463275e7f5fca4c1de9609732f28a16 /drivers
parenteed0fd2102206bf6108460274c40ee6b8e863369 (diff)
b43legacy: fix upload of beacon packets to the hardware
This fixes uploading of the beacon data and writing of the TIM and DTIM offsets. The patch by Michael Buesch has been ported to b43legacy. Signed-off-by: Stefano Brivio <stefano.brivio@polimi.it> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/b43legacy/b43legacy.h19
-rw-r--r--drivers/net/wireless/b43legacy/main.c257
2 files changed, 174 insertions, 102 deletions
diff --git a/drivers/net/wireless/b43legacy/b43legacy.h b/drivers/net/wireless/b43legacy/b43legacy.h
index ba08574a2470..17abf3cb2b1f 100644
--- a/drivers/net/wireless/b43legacy/b43legacy.h
+++ b/drivers/net/wireless/b43legacy/b43legacy.h
@@ -130,13 +130,19 @@
130#define B43legacy_SHM_SH_HOSTFHI 0x0060 /* Hostflags ucode opts (high) */ 130#define B43legacy_SHM_SH_HOSTFHI 0x0060 /* Hostflags ucode opts (high) */
131/* SHM_SHARED crypto engine */ 131/* SHM_SHARED crypto engine */
132#define B43legacy_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block */ 132#define B43legacy_SHM_SH_KEYIDXBLOCK 0x05D4 /* Key index/algorithm block */
133/* SHM_SHARED beacon variables */ 133/* SHM_SHARED beacon/AP variables */
134#define B43legacy_SHM_SH_DTIMP 0x0012 /* DTIM period */
135#define B43legacy_SHM_SH_BTL0 0x0018 /* Beacon template length 0 */
136#define B43legacy_SHM_SH_BTL1 0x001A /* Beacon template length 1 */
137#define B43legacy_SHM_SH_BTSFOFF 0x001C /* Beacon TSF offset */
138#define B43legacy_SHM_SH_TIMPOS 0x001E /* TIM position in beacon */
134#define B43legacy_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word */ 139#define B43legacy_SHM_SH_BEACPHYCTL 0x0054 /* Beacon PHY TX control word */
135/* SHM_SHARED ACK/CTS control */ 140/* SHM_SHARED ACK/CTS control */
136#define B43legacy_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word */ 141#define B43legacy_SHM_SH_ACKCTSPHYCTL 0x0022 /* ACK/CTS PHY control word */
137/* SHM_SHARED probe response variables */ 142/* SHM_SHARED probe response variables */
138#define B43legacy_SHM_SH_PRPHYCTL 0x0188 /* Probe Resp PHY TX control */ 143#define B43legacy_SHM_SH_PRTLEN 0x004A /* Probe Response template length */
139#define B43legacy_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */ 144#define B43legacy_SHM_SH_PRMAXTIME 0x0074 /* Probe Response max time */
145#define B43legacy_SHM_SH_PRPHYCTL 0x0188 /* Probe Resp PHY TX control */
140/* SHM_SHARED rate tables */ 146/* SHM_SHARED rate tables */
141/* SHM_SHARED microcode soft registers */ 147/* SHM_SHARED microcode soft registers */
142#define B43legacy_SHM_SH_UCODEREV 0x0000 /* Microcode revision */ 148#define B43legacy_SHM_SH_UCODEREV 0x0000 /* Microcode revision */
@@ -601,6 +607,12 @@ struct b43legacy_wl {
601 u8 nr_devs; 607 u8 nr_devs;
602 608
603 bool radiotap_enabled; 609 bool radiotap_enabled;
610
611 /* The beacon we are currently using (AP or IBSS mode).
612 * This beacon stuff is protected by the irq_lock. */
613 struct sk_buff *current_beacon;
614 bool beacon0_uploaded;
615 bool beacon1_uploaded;
604}; 616};
605 617
606/* Pointers to the firmware data and meta information about it. */ 618/* Pointers to the firmware data and meta information about it. */
@@ -699,9 +711,6 @@ struct b43legacy_wldev {
699 u8 max_nr_keys; 711 u8 max_nr_keys;
700 struct b43legacy_key key[58]; 712 struct b43legacy_key key[58];
701 713
702 /* Cached beacon template while uploading the template. */
703 struct sk_buff *cached_beacon;
704
705 /* Firmware data */ 714 /* Firmware data */
706 struct b43legacy_firmware fw; 715 struct b43legacy_firmware fw;
707 716
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c
index 9db949ed6519..62e679a1c05f 100644
--- a/drivers/net/wireless/b43legacy/main.c
+++ b/drivers/net/wireless/b43legacy/main.c
@@ -962,16 +962,61 @@ static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev,
962 u16 ram_offset, 962 u16 ram_offset,
963 u16 shm_size_offset, u8 rate) 963 u16 shm_size_offset, u8 rate)
964{ 964{
965 int len;
966 const u8 *data;
967 965
968 B43legacy_WARN_ON(!dev->cached_beacon); 966 unsigned int i, len, variable_len;
969 len = min((size_t)dev->cached_beacon->len, 967 const struct ieee80211_mgmt *bcn;
968 const u8 *ie;
969 bool tim_found = 0;
970
971 bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
972 len = min((size_t)dev->wl->current_beacon->len,
970 0x200 - sizeof(struct b43legacy_plcp_hdr6)); 973 0x200 - sizeof(struct b43legacy_plcp_hdr6));
971 data = (const u8 *)(dev->cached_beacon->data); 974
972 b43legacy_write_template_common(dev, data, 975 b43legacy_write_template_common(dev, (const u8 *)bcn, len, ram_offset,
973 len, ram_offset,
974 shm_size_offset, rate); 976 shm_size_offset, rate);
977
978 /* Find the position of the TIM and the DTIM_period value
979 * and write them to SHM. */
980 ie = bcn->u.beacon.variable;
981 variable_len = len - offsetof(struct ieee80211_mgmt, u.beacon.variable);
982 for (i = 0; i < variable_len - 2; ) {
983 uint8_t ie_id, ie_len;
984
985 ie_id = ie[i];
986 ie_len = ie[i + 1];
987 if (ie_id == 5) {
988 u16 tim_position;
989 u16 dtim_period;
990 /* This is the TIM Information Element */
991
992 /* Check whether the ie_len is in the beacon data range. */
993 if (variable_len < ie_len + 2 + i)
994 break;
995 /* A valid TIM is at least 4 bytes long. */
996 if (ie_len < 4)
997 break;
998 tim_found = 1;
999
1000 tim_position = sizeof(struct b43legacy_plcp_hdr6);
1001 tim_position += offsetof(struct ieee80211_mgmt,
1002 u.beacon.variable);
1003 tim_position += i;
1004
1005 dtim_period = ie[i + 3];
1006
1007 b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
1008 B43legacy_SHM_SH_TIMPOS, tim_position);
1009 b43legacy_shm_write16(dev, B43legacy_SHM_SHARED,
1010 B43legacy_SHM_SH_DTIMP, dtim_period);
1011 break;
1012 }
1013 i += ie_len + 2;
1014 }
1015 if (!tim_found) {
1016 b43legacywarn(dev->wl, "Did not find a valid TIM IE in the "
1017 "beacon template packet. AP or IBSS operation "
1018 "may be broken.\n");
1019 }
975} 1020}
976 1021
977static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev, 1022static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev,
@@ -1004,26 +1049,27 @@ static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev,
1004 * 2) Patching duration field 1049 * 2) Patching duration field
1005 * 3) Stripping TIM 1050 * 3) Stripping TIM
1006 */ 1051 */
1007static u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev, 1052static const u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
1008 u16 *dest_size, 1053 u16 *dest_size,
1009 struct ieee80211_rate *rate) 1054 struct ieee80211_rate *rate)
1010{ 1055{
1011 const u8 *src_data; 1056 const u8 *src_data;
1012 u8 *dest_data; 1057 u8 *dest_data;
1013 u16 src_size; 1058 u16 src_size, elem_size, src_pos, dest_pos;
1014 u16 elem_size;
1015 u16 src_pos;
1016 u16 dest_pos;
1017 __le16 dur; 1059 __le16 dur;
1018 struct ieee80211_hdr *hdr; 1060 struct ieee80211_hdr *hdr;
1061 size_t ie_start;
1062
1063 src_size = dev->wl->current_beacon->len;
1064 src_data = (const u8 *)dev->wl->current_beacon->data;
1019 1065
1020 B43legacy_WARN_ON(!dev->cached_beacon); 1066 /* Get the start offset of the variable IEs in the packet. */
1021 src_size = dev->cached_beacon->len; 1067 ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
1022 src_data = (const u8 *)dev->cached_beacon->data; 1068 B43legacy_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt,
1069 u.beacon.variable));
1023 1070
1024 if (unlikely(src_size < 0x24)) { 1071 if (src_size < ie_start) {
1025 b43legacydbg(dev->wl, "b43legacy_generate_probe_resp: " 1072 B43legacy_WARN_ON(1);
1026 "invalid beacon\n");
1027 return NULL; 1073 return NULL;
1028 } 1074 }
1029 1075
@@ -1031,19 +1077,18 @@ static u8 *b43legacy_generate_probe_resp(struct b43legacy_wldev *dev,
1031 if (unlikely(!dest_data)) 1077 if (unlikely(!dest_data))
1032 return NULL; 1078 return NULL;
1033 1079
1034 /* 0x24 is offset of first variable-len Information-Element 1080 /* Copy the static data and all Information Elements, except the TIM. */
1035 * in beacon frame. 1081 memcpy(dest_data, src_data, ie_start);
1036 */ 1082 src_pos = ie_start;
1037 memcpy(dest_data, src_data, 0x24); 1083 dest_pos = ie_start;
1038 src_pos = 0x24; 1084 for ( ; src_pos < src_size - 2; src_pos += elem_size) {
1039 dest_pos = 0x24;
1040 for (; src_pos < src_size - 2; src_pos += elem_size) {
1041 elem_size = src_data[src_pos + 1] + 2; 1085 elem_size = src_data[src_pos + 1] + 2;
1042 if (src_data[src_pos] != 0x05) { /* TIM */ 1086 if (src_data[src_pos] == 5) {
1043 memcpy(dest_data + dest_pos, src_data + src_pos, 1087 /* This is the TIM. */
1044 elem_size); 1088 continue;
1045 dest_pos += elem_size;
1046 } 1089 }
1090 memcpy(dest_data + dest_pos, src_data + src_pos, elem_size);
1091 dest_pos += elem_size;
1047 } 1092 }
1048 *dest_size = dest_pos; 1093 *dest_size = dest_pos;
1049 hdr = (struct ieee80211_hdr *)dest_data; 1094 hdr = (struct ieee80211_hdr *)dest_data;
@@ -1065,11 +1110,10 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev,
1065 u16 shm_size_offset, 1110 u16 shm_size_offset,
1066 struct ieee80211_rate *rate) 1111 struct ieee80211_rate *rate)
1067{ 1112{
1068 u8 *probe_resp_data; 1113 const u8 *probe_resp_data;
1069 u16 size; 1114 u16 size;
1070 1115
1071 B43legacy_WARN_ON(!dev->cached_beacon); 1116 size = dev->wl->current_beacon->len;
1072 size = dev->cached_beacon->len;
1073 probe_resp_data = b43legacy_generate_probe_resp(dev, &size, rate); 1117 probe_resp_data = b43legacy_generate_probe_resp(dev, &size, rate);
1074 if (unlikely(!probe_resp_data)) 1118 if (unlikely(!probe_resp_data))
1075 return; 1119 return;
@@ -1094,43 +1138,21 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev,
1094 kfree(probe_resp_data); 1138 kfree(probe_resp_data);
1095} 1139}
1096 1140
1097static int b43legacy_refresh_cached_beacon(struct b43legacy_wldev *dev, 1141/* Asynchronously update the packet templates in template RAM.
1098 struct sk_buff *beacon) 1142 * Locking: Requires wl->irq_lock to be locked. */
1143static void b43legacy_update_templates(struct b43legacy_wl *wl,
1144 struct sk_buff *beacon)
1099{ 1145{
1100 if (dev->cached_beacon) 1146 /* This is the top half of the ansynchronous beacon update. The bottom
1101 kfree_skb(dev->cached_beacon); 1147 * half is the beacon IRQ. Beacon update must be asynchronous to avoid
1102 dev->cached_beacon = beacon; 1148 * sending an invalid beacon. This can happen for example, if the
1149 * firmware transmits a beacon while we are updating it. */
1103 1150
1104 return 0; 1151 if (wl->current_beacon)
1105} 1152 dev_kfree_skb_any(wl->current_beacon);
1106 1153 wl->current_beacon = beacon;
1107static void b43legacy_update_templates(struct b43legacy_wldev *dev) 1154 wl->beacon0_uploaded = 0;
1108{ 1155 wl->beacon1_uploaded = 0;
1109 u32 cmd;
1110
1111 B43legacy_WARN_ON(!dev->cached_beacon);
1112
1113 b43legacy_write_beacon_template(dev, 0x68, 0x18,
1114 B43legacy_CCK_RATE_1MB);
1115 b43legacy_write_beacon_template(dev, 0x468, 0x1A,
1116 B43legacy_CCK_RATE_1MB);
1117 b43legacy_write_probe_resp_template(dev, 0x268, 0x4A,
1118 &b43legacy_b_ratetable[0]);
1119
1120 cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
1121 cmd |= B43legacy_MACCMD_BEACON0_VALID | B43legacy_MACCMD_BEACON1_VALID;
1122 b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd);
1123}
1124
1125static void b43legacy_refresh_templates(struct b43legacy_wldev *dev,
1126 struct sk_buff *beacon)
1127{
1128 int err;
1129
1130 err = b43legacy_refresh_cached_beacon(dev, beacon);
1131 if (unlikely(err))
1132 return;
1133 b43legacy_update_templates(dev);
1134} 1156}
1135 1157
1136static void b43legacy_set_ssid(struct b43legacy_wldev *dev, 1158static void b43legacy_set_ssid(struct b43legacy_wldev *dev,
@@ -1171,38 +1193,37 @@ static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev,
1171 1193
1172static void handle_irq_beacon(struct b43legacy_wldev *dev) 1194static void handle_irq_beacon(struct b43legacy_wldev *dev)
1173{ 1195{
1174 u32 status; 1196 struct b43legacy_wl *wl = dev->wl;
1197 u32 cmd;
1175 1198
1176 if (!b43legacy_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) 1199 if (!b43legacy_is_mode(wl, IEEE80211_IF_TYPE_AP))
1177 return; 1200 return;
1178 1201
1179 dev->irq_savedstate &= ~B43legacy_IRQ_BEACON; 1202 /* This is the bottom half of the asynchronous beacon update. */
1180 status = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
1181 1203
1182 if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) { 1204 cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD);
1183 /* ACK beacon IRQ. */ 1205 if (!(cmd & B43legacy_MACCMD_BEACON0_VALID)) {
1184 b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, 1206 if (!wl->beacon0_uploaded) {
1185 B43legacy_IRQ_BEACON); 1207 b43legacy_write_beacon_template(dev, 0x68,
1186 dev->irq_savedstate |= B43legacy_IRQ_BEACON; 1208 B43legacy_SHM_SH_BTL0,
1187 if (dev->cached_beacon) 1209 B43legacy_CCK_RATE_1MB);
1188 kfree_skb(dev->cached_beacon); 1210 b43legacy_write_probe_resp_template(dev, 0x268,
1189 dev->cached_beacon = NULL; 1211 B43legacy_SHM_SH_PRTLEN,
1190 return; 1212 &__b43legacy_ratetable[3]);
1191 } 1213 wl->beacon0_uploaded = 1;
1192 if (!(status & 0x1)) { 1214 }
1193 b43legacy_write_beacon_template(dev, 0x68, 0x18, 1215 cmd |= B43legacy_MACCMD_BEACON0_VALID;
1194 B43legacy_CCK_RATE_1MB); 1216 }
1195 status |= 0x1; 1217 if (!(cmd & B43legacy_MACCMD_BEACON1_VALID)) {
1196 b43legacy_write32(dev, B43legacy_MMIO_MACCMD, 1218 if (!wl->beacon1_uploaded) {
1197 status); 1219 b43legacy_write_beacon_template(dev, 0x468,
1198 } 1220 B43legacy_SHM_SH_BTL1,
1199 if (!(status & 0x2)) { 1221 B43legacy_CCK_RATE_1MB);
1200 b43legacy_write_beacon_template(dev, 0x468, 0x1A, 1222 wl->beacon1_uploaded = 1;
1201 B43legacy_CCK_RATE_1MB); 1223 }
1202 status |= 0x2; 1224 cmd |= B43legacy_MACCMD_BEACON1_VALID;
1203 b43legacy_write32(dev, B43legacy_MMIO_MACCMD,
1204 status);
1205 } 1225 }
1226 b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd);
1206} 1227}
1207 1228
1208static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) 1229static void handle_irq_ucode_debug(struct b43legacy_wldev *dev)
@@ -2709,7 +2730,7 @@ static int b43legacy_op_config_interface(struct ieee80211_hw *hw,
2709 B43legacy_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP); 2730 B43legacy_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP);
2710 b43legacy_set_ssid(dev, conf->ssid, conf->ssid_len); 2731 b43legacy_set_ssid(dev, conf->ssid, conf->ssid_len);
2711 if (conf->beacon) 2732 if (conf->beacon)
2712 b43legacy_refresh_templates(dev, conf->beacon); 2733 b43legacy_update_templates(wl, conf->beacon);
2713 } 2734 }
2714 b43legacy_write_mac_bssid_templates(dev); 2735 b43legacy_write_mac_bssid_templates(dev);
2715 } 2736 }
@@ -3022,6 +3043,11 @@ static void b43legacy_wireless_core_exit(struct b43legacy_wldev *dev)
3022 kfree(phy->tssi2dbm); 3043 kfree(phy->tssi2dbm);
3023 kfree(phy->lo_control); 3044 kfree(phy->lo_control);
3024 phy->lo_control = NULL; 3045 phy->lo_control = NULL;
3046 if (dev->wl->current_beacon) {
3047 dev_kfree_skb_any(dev->wl->current_beacon);
3048 dev->wl->current_beacon = NULL;
3049 }
3050
3025 ssb_device_disable(dev->dev, 0); 3051 ssb_device_disable(dev->dev, 0);
3026 ssb_bus_may_powerdown(dev->dev->bus); 3052 ssb_bus_may_powerdown(dev->dev->bus);
3027} 3053}
@@ -3346,6 +3372,41 @@ out_unlock:
3346 return err; 3372 return err;
3347} 3373}
3348 3374
3375static int b43legacy_op_beacon_set_tim(struct ieee80211_hw *hw,
3376 int aid, int set)
3377{
3378 struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
3379 struct sk_buff *beacon;
3380 unsigned long flags;
3381
3382 /* We could modify the existing beacon and set the aid bit in the TIM
3383 * field, but that would probably require resizing and moving of data
3384 * within the beacon template. Simply request a new beacon and let
3385 * mac80211 do the hard work. */
3386 beacon = ieee80211_beacon_get(hw, wl->vif, NULL);
3387 if (unlikely(!beacon))
3388 return -ENOMEM;
3389 spin_lock_irqsave(&wl->irq_lock, flags);
3390 b43legacy_update_templates(wl, beacon);
3391 spin_unlock_irqrestore(&wl->irq_lock, flags);
3392
3393 return 0;
3394}
3395
3396static int b43legacy_op_ibss_beacon_update(struct ieee80211_hw *hw,
3397 struct sk_buff *beacon,
3398 struct ieee80211_tx_control *ctl)
3399{
3400 struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw);
3401 unsigned long flags;
3402
3403 spin_lock_irqsave(&wl->irq_lock, flags);
3404 b43legacy_update_templates(wl, beacon);
3405 spin_unlock_irqrestore(&wl->irq_lock, flags);
3406
3407 return 0;
3408}
3409
3349static const struct ieee80211_ops b43legacy_hw_ops = { 3410static const struct ieee80211_ops b43legacy_hw_ops = {
3350 .tx = b43legacy_op_tx, 3411 .tx = b43legacy_op_tx,
3351 .conf_tx = b43legacy_op_conf_tx, 3412 .conf_tx = b43legacy_op_conf_tx,
@@ -3359,6 +3420,8 @@ static const struct ieee80211_ops b43legacy_hw_ops = {
3359 .start = b43legacy_op_start, 3420 .start = b43legacy_op_start,
3360 .stop = b43legacy_op_stop, 3421 .stop = b43legacy_op_stop,
3361 .set_retry_limit = b43legacy_op_set_retry_limit, 3422 .set_retry_limit = b43legacy_op_set_retry_limit,
3423 .set_tim = b43legacy_op_beacon_set_tim,
3424 .beacon_update = b43legacy_op_ibss_beacon_update,
3362}; 3425};
3363 3426
3364/* Hard-reset the chip. Do not call this directly. 3427/* Hard-reset the chip. Do not call this directly.