diff options
author | Michael Buesch <mb@bu3sch.de> | 2007-12-26 11:47:10 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-01-28 18:09:43 -0500 |
commit | e66fee6aa04b27b6b6f812af0e4123eded5bf8ac (patch) | |
tree | d295309d66a657c76bd007384d13203480eb7d79 /drivers/net/wireless/b43/main.c | |
parent | 471b3efdfccc257591331724145f8ccf8b3217e1 (diff) |
b43: Fix upload of beacon packets to the hardware
This fixes uploading of the beacon data and writing of the
TIM and DTIM offsets.
Signed-off-by: Michael Buesch <mb@bu3sch.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers/net/wireless/b43/main.c')
-rw-r--r-- | drivers/net/wireless/b43/main.c | 222 |
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 | ||
1162 | static void b43_write_probe_resp_plcp(struct b43_wldev *dev, | 1205 | static 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 | */ |
1187 | static u8 *b43_generate_probe_resp(struct b43_wldev *dev, | 1230 | static 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 | ||
1263 | static int b43_refresh_cached_beacon(struct b43_wldev *dev, | 1308 | /* Asynchronously update the packet templates in template RAM. */ |
1264 | struct sk_buff *beacon) | 1309 | static 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 | |||
1273 | static 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 | ||
1288 | static 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 | ||
1298 | static void b43_set_ssid(struct b43_wldev *dev, const u8 * ssid, u8 ssid_len) | 1330 | static 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 | ||
1329 | static void handle_irq_beacon(struct b43_wldev *dev) | 1361 | static 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 | ||
1360 | static void handle_irq_ucode_debug(struct b43_wldev *dev) | 1393 | static 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 | ||
3597 | static 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 | |||
3614 | static 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 | |||
3559 | static const struct ieee80211_ops b43_hw_ops = { | 3625 | static 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. |