aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/b43/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/b43/main.c')
-rw-r--r--drivers/net/wireless/b43/main.c222
1 files changed, 145 insertions, 77 deletions
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index af3d24c559c0..9f6647ccc6d6 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -1148,15 +1148,58 @@ static void b43_write_beacon_template(struct b43_wldev *dev,
1148 u16 ram_offset, 1148 u16 ram_offset,
1149 u16 shm_size_offset, u8 rate) 1149 u16 shm_size_offset, u8 rate)
1150{ 1150{
1151 int len; 1151 int i, len;
1152 const u8 *data; 1152 const struct ieee80211_mgmt *bcn;
1153 const u8 *ie;
1154 bool tim_found = 0;
1153 1155
1154 B43_WARN_ON(!dev->cached_beacon); 1156 bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data);
1155 len = min((size_t) dev->cached_beacon->len, 1157 len = min((size_t) dev->wl->current_beacon->len,
1156 0x200 - sizeof(struct b43_plcp_hdr6)); 1158 0x200 - sizeof(struct b43_plcp_hdr6));
1157 data = (const u8 *)(dev->cached_beacon->data); 1159
1158 b43_write_template_common(dev, data, 1160 b43_write_template_common(dev, (const u8 *)bcn,
1159 len, ram_offset, shm_size_offset, rate); 1161 len, ram_offset, shm_size_offset, rate);
1162
1163 /* Find the position of the TIM and the DTIM_period value
1164 * and write them to SHM. */
1165 ie = bcn->u.beacon.variable;
1166 for (i = 0; i < len - 2; ) {
1167 uint8_t ie_id, ie_len;
1168
1169 ie_id = ie[i];
1170 ie_len = ie[i + 1];
1171 if (ie_id == 5) {
1172 u16 tim_position;
1173 u16 dtim_period;
1174 /* This is the TIM Information Element */
1175
1176 /* Check whether the ie_len is in the beacon data range. */
1177 if (len < ie_len + 2 + i)
1178 break;
1179 /* A valid TIM is at least 4 bytes long. */
1180 if (ie_len < 4)
1181 break;
1182 tim_found = 1;
1183
1184 tim_position = sizeof(struct b43_plcp_hdr6);
1185 tim_position += offsetof(struct ieee80211_mgmt, u.beacon.variable);
1186 tim_position += i;
1187
1188 dtim_period = ie[i + 3];
1189
1190 b43_shm_write16(dev, B43_SHM_SHARED,
1191 B43_SHM_SH_TIMBPOS, tim_position);
1192 b43_shm_write16(dev, B43_SHM_SHARED,
1193 B43_SHM_SH_DTIMPER, dtim_period);
1194 break;
1195 }
1196 i += ie_len + 2;
1197 }
1198 if (!tim_found) {
1199 b43warn(dev->wl, "Did not find a valid TIM IE in "
1200 "the beacon template packet. AP or IBSS operation "
1201 "may be broken.\n");
1202 }
1160} 1203}
1161 1204
1162static void b43_write_probe_resp_plcp(struct b43_wldev *dev, 1205static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
@@ -1184,40 +1227,43 @@ static void b43_write_probe_resp_plcp(struct b43_wldev *dev,
1184 * 2) Patching duration field 1227 * 2) Patching duration field
1185 * 3) Stripping TIM 1228 * 3) Stripping TIM
1186 */ 1229 */
1187static u8 *b43_generate_probe_resp(struct b43_wldev *dev, 1230static const u8 * b43_generate_probe_resp(struct b43_wldev *dev,
1188 u16 * dest_size, u8 rate) 1231 u16 *dest_size, u8 rate)
1189{ 1232{
1190 const u8 *src_data; 1233 const u8 *src_data;
1191 u8 *dest_data; 1234 u8 *dest_data;
1192 u16 src_size, elem_size, src_pos, dest_pos; 1235 u16 src_size, elem_size, src_pos, dest_pos;
1193 __le16 dur; 1236 __le16 dur;
1194 struct ieee80211_hdr *hdr; 1237 struct ieee80211_hdr *hdr;
1238 size_t ie_start;
1239
1240 src_size = dev->wl->current_beacon->len;
1241 src_data = (const u8 *)dev->wl->current_beacon->data;
1195 1242
1196 B43_WARN_ON(!dev->cached_beacon); 1243 /* Get the start offset of the variable IEs in the packet. */
1197 src_size = dev->cached_beacon->len; 1244 ie_start = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
1198 src_data = (const u8 *)dev->cached_beacon->data; 1245 B43_WARN_ON(ie_start != offsetof(struct ieee80211_mgmt, u.beacon.variable));
1199 1246
1200 if (unlikely(src_size < 0x24)) { 1247 if (B43_WARN_ON(src_size < ie_start))
1201 b43dbg(dev->wl, "b43_generate_probe_resp: " "invalid beacon\n");
1202 return NULL; 1248 return NULL;
1203 }
1204 1249
1205 dest_data = kmalloc(src_size, GFP_ATOMIC); 1250 dest_data = kmalloc(src_size, GFP_ATOMIC);
1206 if (unlikely(!dest_data)) 1251 if (unlikely(!dest_data))
1207 return NULL; 1252 return NULL;
1208 1253
1209 /* 0x24 is offset of first variable-len Information-Element 1254 /* Copy the static data and all Information Elements, except the TIM. */
1210 * in beacon frame. 1255 memcpy(dest_data, src_data, ie_start);
1211 */ 1256 src_pos = ie_start;
1212 memcpy(dest_data, src_data, 0x24); 1257 dest_pos = ie_start;
1213 src_pos = dest_pos = 0x24; 1258 for ( ; src_pos < src_size - 2; src_pos += elem_size) {
1214 for (; src_pos < src_size - 2; src_pos += elem_size) {
1215 elem_size = src_data[src_pos + 1] + 2; 1259 elem_size = src_data[src_pos + 1] + 2;
1216 if (src_data[src_pos] != 0x05) { /* TIM */ 1260 if (src_data[src_pos] == 5) {
1217 memcpy(dest_data + dest_pos, src_data + src_pos, 1261 /* This is the TIM. */
1218 elem_size); 1262 continue;
1219 dest_pos += elem_size;
1220 } 1263 }
1264 memcpy(dest_data + dest_pos, src_data + src_pos,
1265 elem_size);
1266 dest_pos += elem_size;
1221 } 1267 }
1222 *dest_size = dest_pos; 1268 *dest_size = dest_pos;
1223 hdr = (struct ieee80211_hdr *)dest_data; 1269 hdr = (struct ieee80211_hdr *)dest_data;
@@ -1237,11 +1283,10 @@ static void b43_write_probe_resp_template(struct b43_wldev *dev,
1237 u16 ram_offset, 1283 u16 ram_offset,
1238 u16 shm_size_offset, u8 rate) 1284 u16 shm_size_offset, u8 rate)
1239{ 1285{
1240 u8 *probe_resp_data; 1286 const u8 *probe_resp_data;
1241 u16 size; 1287 u16 size;
1242 1288
1243 B43_WARN_ON(!dev->cached_beacon); 1289 size = dev->wl->current_beacon->len;
1244 size = dev->cached_beacon->len;
1245 probe_resp_data = b43_generate_probe_resp(dev, &size, rate); 1290 probe_resp_data = b43_generate_probe_resp(dev, &size, rate);
1246 if (unlikely(!probe_resp_data)) 1291 if (unlikely(!probe_resp_data))
1247 return; 1292 return;
@@ -1260,39 +1305,26 @@ static void b43_write_probe_resp_template(struct b43_wldev *dev,
1260 kfree(probe_resp_data); 1305 kfree(probe_resp_data);
1261} 1306}
1262 1307
1263static int b43_refresh_cached_beacon(struct b43_wldev *dev, 1308/* Asynchronously update the packet templates in template RAM. */
1264 struct sk_buff *beacon) 1309static void b43_update_templates(struct b43_wl *wl, struct sk_buff *beacon)
1265{ 1310{
1266 if (dev->cached_beacon) 1311 unsigned long flags;
1267 kfree_skb(dev->cached_beacon);
1268 dev->cached_beacon = beacon;
1269
1270 return 0;
1271}
1272
1273static void b43_update_templates(struct b43_wldev *dev)
1274{
1275 u32 cmd;
1276 1312
1277 B43_WARN_ON(!dev->cached_beacon); 1313 /* This is the top half of the ansynchronous beacon update.
1314 * The bottom half is the beacon IRQ.
1315 * Beacon update must be asynchronous to avoid sending an
1316 * invalid beacon. This can happen for example, if the firmware
1317 * transmits a beacon while we are updating it. */
1278 1318
1279 b43_write_beacon_template(dev, 0x68, 0x18, B43_CCK_RATE_1MB); 1319 spin_lock_irqsave(&wl->irq_lock, flags);
1280 b43_write_beacon_template(dev, 0x468, 0x1A, B43_CCK_RATE_1MB);
1281 b43_write_probe_resp_template(dev, 0x268, 0x4A, B43_CCK_RATE_11MB);
1282 1320
1283 cmd = b43_read32(dev, B43_MMIO_MACCMD); 1321 if (wl->current_beacon)
1284 cmd |= B43_MACCMD_BEACON0_VALID | B43_MACCMD_BEACON1_VALID; 1322 dev_kfree_skb_any(wl->current_beacon);
1285 b43_write32(dev, B43_MMIO_MACCMD, cmd); 1323 wl->current_beacon = beacon;
1286} 1324 wl->beacon0_uploaded = 0;
1325 wl->beacon1_uploaded = 0;
1287 1326
1288static void b43_refresh_templates(struct b43_wldev *dev, struct sk_buff *beacon) 1327 spin_unlock_irqrestore(&wl->irq_lock, flags);
1289{
1290 int err;
1291
1292 err = b43_refresh_cached_beacon(dev, beacon);
1293 if (unlikely(err))
1294 return;
1295 b43_update_templates(dev);
1296} 1328}
1297 1329
1298static void b43_set_ssid(struct b43_wldev *dev, const u8 * ssid, u8 ssid_len) 1330static void b43_set_ssid(struct b43_wldev *dev, const u8 * ssid, u8 ssid_len)
@@ -1328,33 +1360,34 @@ static void b43_set_beacon_int(struct b43_wldev *dev, u16 beacon_int)
1328 1360
1329static void handle_irq_beacon(struct b43_wldev *dev) 1361static void handle_irq_beacon(struct b43_wldev *dev)
1330{ 1362{
1331 u32 status; 1363 struct b43_wl *wl = dev->wl;
1364 u32 cmd;
1332 1365
1333 if (!b43_is_mode(dev->wl, IEEE80211_IF_TYPE_AP)) 1366 if (!b43_is_mode(wl, IEEE80211_IF_TYPE_AP))
1334 return; 1367 return;
1335 1368
1336 dev->irq_savedstate &= ~B43_IRQ_BEACON; 1369 /* This is the bottom half of the asynchronous beacon update. */
1337 status = b43_read32(dev, B43_MMIO_MACCMD);
1338 1370
1339 if (!dev->cached_beacon || ((status & 0x1) && (status & 0x2))) { 1371 cmd = b43_read32(dev, B43_MMIO_MACCMD);
1340 /* ACK beacon IRQ. */ 1372 if (!(cmd & B43_MACCMD_BEACON0_VALID)) {
1341 b43_write32(dev, B43_MMIO_GEN_IRQ_REASON, B43_IRQ_BEACON); 1373 if (!wl->beacon0_uploaded) {
1342 dev->irq_savedstate |= B43_IRQ_BEACON; 1374 b43_write_beacon_template(dev, 0x68, 0x18,
1343 if (dev->cached_beacon) 1375 B43_CCK_RATE_1MB);
1344 kfree_skb(dev->cached_beacon); 1376 b43_write_probe_resp_template(dev, 0x268, 0x4A,
1345 dev->cached_beacon = NULL; 1377 B43_CCK_RATE_11MB);
1346 return; 1378 wl->beacon0_uploaded = 1;
1347 } 1379 }
1348 if (!(status & 0x1)) { 1380 cmd |= B43_MACCMD_BEACON0_VALID;
1349 b43_write_beacon_template(dev, 0x68, 0x18, B43_CCK_RATE_1MB);
1350 status |= 0x1;
1351 b43_write32(dev, B43_MMIO_MACCMD, status);
1352 } 1381 }
1353 if (!(status & 0x2)) { 1382 if (!(cmd & B43_MACCMD_BEACON1_VALID)) {
1354 b43_write_beacon_template(dev, 0x468, 0x1A, B43_CCK_RATE_1MB); 1383 if (!wl->beacon1_uploaded) {
1355 status |= 0x2; 1384 b43_write_beacon_template(dev, 0x468, 0x1A,
1356 b43_write32(dev, B43_MMIO_MACCMD, status); 1385 B43_CCK_RATE_1MB);
1386 wl->beacon1_uploaded = 1;
1387 }
1388 cmd |= B43_MACCMD_BEACON1_VALID;
1357 } 1389 }
1390 b43_write32(dev, B43_MMIO_MACCMD, cmd);
1358} 1391}
1359 1392
1360static void handle_irq_ucode_debug(struct b43_wldev *dev) 1393static void handle_irq_ucode_debug(struct b43_wldev *dev)
@@ -2949,7 +2982,7 @@ static int b43_op_config_interface(struct ieee80211_hw *hw,
2949 B43_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP); 2982 B43_WARN_ON(conf->type != IEEE80211_IF_TYPE_AP);
2950 b43_set_ssid(dev, conf->ssid, conf->ssid_len); 2983 b43_set_ssid(dev, conf->ssid, conf->ssid_len);
2951 if (conf->beacon) 2984 if (conf->beacon)
2952 b43_refresh_templates(dev, conf->beacon); 2985 b43_update_templates(wl, conf->beacon);
2953 } 2986 }
2954 b43_write_mac_bssid_templates(dev); 2987 b43_write_mac_bssid_templates(dev);
2955 } 2988 }
@@ -3295,6 +3328,11 @@ static void b43_wireless_core_exit(struct b43_wldev *dev)
3295 kfree(phy->tssi2dbm); 3328 kfree(phy->tssi2dbm);
3296 kfree(phy->lo_control); 3329 kfree(phy->lo_control);
3297 phy->lo_control = NULL; 3330 phy->lo_control = NULL;
3331 if (dev->wl->current_beacon) {
3332 dev_kfree_skb_any(dev->wl->current_beacon);
3333 dev->wl->current_beacon = NULL;
3334 }
3335
3298 ssb_device_disable(dev->dev, 0); 3336 ssb_device_disable(dev->dev, 0);
3299 ssb_bus_may_powerdown(dev->dev->bus); 3337 ssb_bus_may_powerdown(dev->dev->bus);
3300} 3338}
@@ -3556,6 +3594,34 @@ out_unlock:
3556 return err; 3594 return err;
3557} 3595}
3558 3596
3597static int b43_op_beacon_set_tim(struct ieee80211_hw *hw, int aid, int set)
3598{
3599 struct b43_wl *wl = hw_to_b43_wl(hw);
3600 struct sk_buff *beacon;
3601
3602 /* We could modify the existing beacon and set the aid bit in
3603 * the TIM field, but that would probably require resizing and
3604 * moving of data within the beacon template.
3605 * Simply request a new beacon and let mac80211 do the hard work. */
3606 beacon = ieee80211_beacon_get(hw, wl->vif, NULL);
3607 if (unlikely(!beacon))
3608 return -ENOMEM;
3609 b43_update_templates(wl, beacon);
3610
3611 return 0;
3612}
3613
3614static int b43_op_ibss_beacon_update(struct ieee80211_hw *hw,
3615 struct sk_buff *beacon,
3616 struct ieee80211_tx_control *ctl)
3617{
3618 struct b43_wl *wl = hw_to_b43_wl(hw);
3619
3620 b43_update_templates(wl, beacon);
3621
3622 return 0;
3623}
3624
3559static const struct ieee80211_ops b43_hw_ops = { 3625static const struct ieee80211_ops b43_hw_ops = {
3560 .tx = b43_op_tx, 3626 .tx = b43_op_tx,
3561 .conf_tx = b43_op_conf_tx, 3627 .conf_tx = b43_op_conf_tx,
@@ -3570,6 +3636,8 @@ static const struct ieee80211_ops b43_hw_ops = {
3570 .start = b43_op_start, 3636 .start = b43_op_start,
3571 .stop = b43_op_stop, 3637 .stop = b43_op_stop,
3572 .set_retry_limit = b43_op_set_retry_limit, 3638 .set_retry_limit = b43_op_set_retry_limit,
3639 .set_tim = b43_op_beacon_set_tim,
3640 .beacon_update = b43_op_ibss_beacon_update,
3573}; 3641};
3574 3642
3575/* Hard-reset the chip. Do not call this directly. 3643/* Hard-reset the chip. Do not call this directly.