diff options
Diffstat (limited to 'drivers/net/wireless/b43legacy/main.c')
-rw-r--r-- | drivers/net/wireless/b43legacy/main.c | 193 |
1 files changed, 150 insertions, 43 deletions
diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 879edc786713..ee202b4f77b5 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c | |||
@@ -955,23 +955,54 @@ static void b43legacy_write_template_common(struct b43legacy_wldev *dev, | |||
955 | size + sizeof(struct b43legacy_plcp_hdr6)); | 955 | size + sizeof(struct b43legacy_plcp_hdr6)); |
956 | } | 956 | } |
957 | 957 | ||
958 | /* Convert a b43legacy antenna number value to the PHY TX control value. */ | ||
959 | static u16 b43legacy_antenna_to_phyctl(int antenna) | ||
960 | { | ||
961 | switch (antenna) { | ||
962 | case B43legacy_ANTENNA0: | ||
963 | return B43legacy_TX4_PHY_ANT0; | ||
964 | case B43legacy_ANTENNA1: | ||
965 | return B43legacy_TX4_PHY_ANT1; | ||
966 | } | ||
967 | return B43legacy_TX4_PHY_ANTLAST; | ||
968 | } | ||
969 | |||
958 | static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev, | 970 | static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev, |
959 | u16 ram_offset, | 971 | u16 ram_offset, |
960 | u16 shm_size_offset, u8 rate) | 972 | u16 shm_size_offset) |
961 | { | 973 | { |
962 | 974 | ||
963 | unsigned int i, len, variable_len; | 975 | unsigned int i, len, variable_len; |
964 | const struct ieee80211_mgmt *bcn; | 976 | const struct ieee80211_mgmt *bcn; |
965 | const u8 *ie; | 977 | const u8 *ie; |
966 | bool tim_found = 0; | 978 | bool tim_found = 0; |
979 | unsigned int rate; | ||
980 | u16 ctl; | ||
981 | int antenna; | ||
982 | struct ieee80211_tx_info *info = IEEE80211_SKB_CB(dev->wl->current_beacon); | ||
967 | 983 | ||
968 | bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); | 984 | bcn = (const struct ieee80211_mgmt *)(dev->wl->current_beacon->data); |
969 | len = min((size_t)dev->wl->current_beacon->len, | 985 | len = min((size_t)dev->wl->current_beacon->len, |
970 | 0x200 - sizeof(struct b43legacy_plcp_hdr6)); | 986 | 0x200 - sizeof(struct b43legacy_plcp_hdr6)); |
987 | rate = ieee80211_get_tx_rate(dev->wl->hw, info)->hw_value; | ||
971 | 988 | ||
972 | b43legacy_write_template_common(dev, (const u8 *)bcn, len, ram_offset, | 989 | b43legacy_write_template_common(dev, (const u8 *)bcn, len, ram_offset, |
973 | shm_size_offset, rate); | 990 | shm_size_offset, rate); |
974 | 991 | ||
992 | /* Write the PHY TX control parameters. */ | ||
993 | antenna = B43legacy_ANTENNA_DEFAULT; | ||
994 | antenna = b43legacy_antenna_to_phyctl(antenna); | ||
995 | ctl = b43legacy_shm_read16(dev, B43legacy_SHM_SHARED, | ||
996 | B43legacy_SHM_SH_BEACPHYCTL); | ||
997 | /* We can't send beacons with short preamble. Would get PHY errors. */ | ||
998 | ctl &= ~B43legacy_TX4_PHY_SHORTPRMBL; | ||
999 | ctl &= ~B43legacy_TX4_PHY_ANT; | ||
1000 | ctl &= ~B43legacy_TX4_PHY_ENC; | ||
1001 | ctl |= antenna; | ||
1002 | ctl |= B43legacy_TX4_PHY_ENC_CCK; | ||
1003 | b43legacy_shm_write16(dev, B43legacy_SHM_SHARED, | ||
1004 | B43legacy_SHM_SH_BEACPHYCTL, ctl); | ||
1005 | |||
975 | /* Find the position of the TIM and the DTIM_period value | 1006 | /* Find the position of the TIM and the DTIM_period value |
976 | * and write them to SHM. */ | 1007 | * and write them to SHM. */ |
977 | ie = bcn->u.beacon.variable; | 1008 | ie = bcn->u.beacon.variable; |
@@ -1013,7 +1044,8 @@ static void b43legacy_write_beacon_template(struct b43legacy_wldev *dev, | |||
1013 | b43legacywarn(dev->wl, "Did not find a valid TIM IE in the " | 1044 | b43legacywarn(dev->wl, "Did not find a valid TIM IE in the " |
1014 | "beacon template packet. AP or IBSS operation " | 1045 | "beacon template packet. AP or IBSS operation " |
1015 | "may be broken.\n"); | 1046 | "may be broken.\n"); |
1016 | } | 1047 | } else |
1048 | b43legacydbg(dev->wl, "Updated beacon template\n"); | ||
1017 | } | 1049 | } |
1018 | 1050 | ||
1019 | static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev, | 1051 | static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev, |
@@ -1025,7 +1057,7 @@ static void b43legacy_write_probe_resp_plcp(struct b43legacy_wldev *dev, | |||
1025 | __le16 dur; | 1057 | __le16 dur; |
1026 | 1058 | ||
1027 | plcp.data = 0; | 1059 | plcp.data = 0; |
1028 | b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate->bitrate); | 1060 | b43legacy_generate_plcp_hdr(&plcp, size + FCS_LEN, rate->hw_value); |
1029 | dur = ieee80211_generic_frame_duration(dev->wl->hw, | 1061 | dur = ieee80211_generic_frame_duration(dev->wl->hw, |
1030 | dev->wl->vif, | 1062 | dev->wl->vif, |
1031 | size, | 1063 | size, |
@@ -1129,10 +1161,104 @@ static void b43legacy_write_probe_resp_template(struct b43legacy_wldev *dev, | |||
1129 | 0x200 - sizeof(struct b43legacy_plcp_hdr6)); | 1161 | 0x200 - sizeof(struct b43legacy_plcp_hdr6)); |
1130 | b43legacy_write_template_common(dev, probe_resp_data, | 1162 | b43legacy_write_template_common(dev, probe_resp_data, |
1131 | size, ram_offset, | 1163 | size, ram_offset, |
1132 | shm_size_offset, rate->bitrate); | 1164 | shm_size_offset, rate->hw_value); |
1133 | kfree(probe_resp_data); | 1165 | kfree(probe_resp_data); |
1134 | } | 1166 | } |
1135 | 1167 | ||
1168 | static void b43legacy_upload_beacon0(struct b43legacy_wldev *dev) | ||
1169 | { | ||
1170 | struct b43legacy_wl *wl = dev->wl; | ||
1171 | |||
1172 | if (wl->beacon0_uploaded) | ||
1173 | return; | ||
1174 | b43legacy_write_beacon_template(dev, 0x68, 0x18); | ||
1175 | /* FIXME: Probe resp upload doesn't really belong here, | ||
1176 | * but we don't use that feature anyway. */ | ||
1177 | b43legacy_write_probe_resp_template(dev, 0x268, 0x4A, | ||
1178 | &__b43legacy_ratetable[3]); | ||
1179 | wl->beacon0_uploaded = 1; | ||
1180 | } | ||
1181 | |||
1182 | static void b43legacy_upload_beacon1(struct b43legacy_wldev *dev) | ||
1183 | { | ||
1184 | struct b43legacy_wl *wl = dev->wl; | ||
1185 | |||
1186 | if (wl->beacon1_uploaded) | ||
1187 | return; | ||
1188 | b43legacy_write_beacon_template(dev, 0x468, 0x1A); | ||
1189 | wl->beacon1_uploaded = 1; | ||
1190 | } | ||
1191 | |||
1192 | static void handle_irq_beacon(struct b43legacy_wldev *dev) | ||
1193 | { | ||
1194 | struct b43legacy_wl *wl = dev->wl; | ||
1195 | u32 cmd, beacon0_valid, beacon1_valid; | ||
1196 | |||
1197 | if (!b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) | ||
1198 | return; | ||
1199 | |||
1200 | /* This is the bottom half of the asynchronous beacon update. */ | ||
1201 | |||
1202 | /* Ignore interrupt in the future. */ | ||
1203 | dev->irq_savedstate &= ~B43legacy_IRQ_BEACON; | ||
1204 | |||
1205 | cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); | ||
1206 | beacon0_valid = (cmd & B43legacy_MACCMD_BEACON0_VALID); | ||
1207 | beacon1_valid = (cmd & B43legacy_MACCMD_BEACON1_VALID); | ||
1208 | |||
1209 | /* Schedule interrupt manually, if busy. */ | ||
1210 | if (beacon0_valid && beacon1_valid) { | ||
1211 | b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_REASON, B43legacy_IRQ_BEACON); | ||
1212 | dev->irq_savedstate |= B43legacy_IRQ_BEACON; | ||
1213 | return; | ||
1214 | } | ||
1215 | |||
1216 | if (unlikely(wl->beacon_templates_virgin)) { | ||
1217 | /* We never uploaded a beacon before. | ||
1218 | * Upload both templates now, but only mark one valid. */ | ||
1219 | wl->beacon_templates_virgin = 0; | ||
1220 | b43legacy_upload_beacon0(dev); | ||
1221 | b43legacy_upload_beacon1(dev); | ||
1222 | cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); | ||
1223 | cmd |= B43legacy_MACCMD_BEACON0_VALID; | ||
1224 | b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); | ||
1225 | } else { | ||
1226 | if (!beacon0_valid) { | ||
1227 | b43legacy_upload_beacon0(dev); | ||
1228 | cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); | ||
1229 | cmd |= B43legacy_MACCMD_BEACON0_VALID; | ||
1230 | b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); | ||
1231 | } else if (!beacon1_valid) { | ||
1232 | b43legacy_upload_beacon1(dev); | ||
1233 | cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); | ||
1234 | cmd |= B43legacy_MACCMD_BEACON1_VALID; | ||
1235 | b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); | ||
1236 | } | ||
1237 | } | ||
1238 | } | ||
1239 | |||
1240 | static void b43legacy_beacon_update_trigger_work(struct work_struct *work) | ||
1241 | { | ||
1242 | struct b43legacy_wl *wl = container_of(work, struct b43legacy_wl, | ||
1243 | beacon_update_trigger); | ||
1244 | struct b43legacy_wldev *dev; | ||
1245 | |||
1246 | mutex_lock(&wl->mutex); | ||
1247 | dev = wl->current_dev; | ||
1248 | if (likely(dev && (b43legacy_status(dev) >= B43legacy_STAT_INITIALIZED))) { | ||
1249 | spin_lock_irq(&wl->irq_lock); | ||
1250 | /* update beacon right away or defer to irq */ | ||
1251 | dev->irq_savedstate = b43legacy_read32(dev, B43legacy_MMIO_GEN_IRQ_MASK); | ||
1252 | handle_irq_beacon(dev); | ||
1253 | /* The handler might have updated the IRQ mask. */ | ||
1254 | b43legacy_write32(dev, B43legacy_MMIO_GEN_IRQ_MASK, | ||
1255 | dev->irq_savedstate); | ||
1256 | mmiowb(); | ||
1257 | spin_unlock_irq(&wl->irq_lock); | ||
1258 | } | ||
1259 | mutex_unlock(&wl->mutex); | ||
1260 | } | ||
1261 | |||
1136 | /* Asynchronously update the packet templates in template RAM. | 1262 | /* Asynchronously update the packet templates in template RAM. |
1137 | * Locking: Requires wl->irq_lock to be locked. */ | 1263 | * Locking: Requires wl->irq_lock to be locked. */ |
1138 | static void b43legacy_update_templates(struct b43legacy_wl *wl) | 1264 | static void b43legacy_update_templates(struct b43legacy_wl *wl) |
@@ -1156,54 +1282,24 @@ static void b43legacy_update_templates(struct b43legacy_wl *wl) | |||
1156 | wl->current_beacon = beacon; | 1282 | wl->current_beacon = beacon; |
1157 | wl->beacon0_uploaded = 0; | 1283 | wl->beacon0_uploaded = 0; |
1158 | wl->beacon1_uploaded = 0; | 1284 | wl->beacon1_uploaded = 0; |
1285 | queue_work(wl->hw->workqueue, &wl->beacon_update_trigger); | ||
1159 | } | 1286 | } |
1160 | 1287 | ||
1161 | static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev, | 1288 | static void b43legacy_set_beacon_int(struct b43legacy_wldev *dev, |
1162 | u16 beacon_int) | 1289 | u16 beacon_int) |
1163 | { | 1290 | { |
1164 | b43legacy_time_lock(dev); | 1291 | b43legacy_time_lock(dev); |
1165 | if (dev->dev->id.revision >= 3) | 1292 | if (dev->dev->id.revision >= 3) { |
1166 | b43legacy_write32(dev, 0x188, (beacon_int << 16)); | 1293 | b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_REP, |
1167 | else { | 1294 | (beacon_int << 16)); |
1295 | b43legacy_write32(dev, B43legacy_MMIO_TSF_CFP_START, | ||
1296 | (beacon_int << 10)); | ||
1297 | } else { | ||
1168 | b43legacy_write16(dev, 0x606, (beacon_int >> 6)); | 1298 | b43legacy_write16(dev, 0x606, (beacon_int >> 6)); |
1169 | b43legacy_write16(dev, 0x610, beacon_int); | 1299 | b43legacy_write16(dev, 0x610, beacon_int); |
1170 | } | 1300 | } |
1171 | b43legacy_time_unlock(dev); | 1301 | b43legacy_time_unlock(dev); |
1172 | } | 1302 | b43legacydbg(dev->wl, "Set beacon interval to %u\n", beacon_int); |
1173 | |||
1174 | static void handle_irq_beacon(struct b43legacy_wldev *dev) | ||
1175 | { | ||
1176 | struct b43legacy_wl *wl = dev->wl; | ||
1177 | u32 cmd; | ||
1178 | |||
1179 | if (!b43legacy_is_mode(wl, NL80211_IFTYPE_AP)) | ||
1180 | return; | ||
1181 | |||
1182 | /* This is the bottom half of the asynchronous beacon update. */ | ||
1183 | |||
1184 | cmd = b43legacy_read32(dev, B43legacy_MMIO_MACCMD); | ||
1185 | if (!(cmd & B43legacy_MACCMD_BEACON0_VALID)) { | ||
1186 | if (!wl->beacon0_uploaded) { | ||
1187 | b43legacy_write_beacon_template(dev, 0x68, | ||
1188 | B43legacy_SHM_SH_BTL0, | ||
1189 | B43legacy_CCK_RATE_1MB); | ||
1190 | b43legacy_write_probe_resp_template(dev, 0x268, | ||
1191 | B43legacy_SHM_SH_PRTLEN, | ||
1192 | &__b43legacy_ratetable[3]); | ||
1193 | wl->beacon0_uploaded = 1; | ||
1194 | } | ||
1195 | cmd |= B43legacy_MACCMD_BEACON0_VALID; | ||
1196 | } | ||
1197 | if (!(cmd & B43legacy_MACCMD_BEACON1_VALID)) { | ||
1198 | if (!wl->beacon1_uploaded) { | ||
1199 | b43legacy_write_beacon_template(dev, 0x468, | ||
1200 | B43legacy_SHM_SH_BTL1, | ||
1201 | B43legacy_CCK_RATE_1MB); | ||
1202 | wl->beacon1_uploaded = 1; | ||
1203 | } | ||
1204 | cmd |= B43legacy_MACCMD_BEACON1_VALID; | ||
1205 | } | ||
1206 | b43legacy_write32(dev, B43legacy_MMIO_MACCMD, cmd); | ||
1207 | } | 1303 | } |
1208 | 1304 | ||
1209 | static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) | 1305 | static void handle_irq_ucode_debug(struct b43legacy_wldev *dev) |
@@ -2297,6 +2393,7 @@ static void b43legacy_security_init(struct b43legacy_wldev *dev) | |||
2297 | dev->max_nr_keys - 8); | 2393 | dev->max_nr_keys - 8); |
2298 | } | 2394 | } |
2299 | 2395 | ||
2396 | #ifdef CONFIG_B43LEGACY_HWRNG | ||
2300 | static int b43legacy_rng_read(struct hwrng *rng, u32 *data) | 2397 | static int b43legacy_rng_read(struct hwrng *rng, u32 *data) |
2301 | { | 2398 | { |
2302 | struct b43legacy_wl *wl = (struct b43legacy_wl *)rng->priv; | 2399 | struct b43legacy_wl *wl = (struct b43legacy_wl *)rng->priv; |
@@ -2312,17 +2409,21 @@ static int b43legacy_rng_read(struct hwrng *rng, u32 *data) | |||
2312 | 2409 | ||
2313 | return (sizeof(u16)); | 2410 | return (sizeof(u16)); |
2314 | } | 2411 | } |
2412 | #endif | ||
2315 | 2413 | ||
2316 | static void b43legacy_rng_exit(struct b43legacy_wl *wl) | 2414 | static void b43legacy_rng_exit(struct b43legacy_wl *wl) |
2317 | { | 2415 | { |
2416 | #ifdef CONFIG_B43LEGACY_HWRNG | ||
2318 | if (wl->rng_initialized) | 2417 | if (wl->rng_initialized) |
2319 | hwrng_unregister(&wl->rng); | 2418 | hwrng_unregister(&wl->rng); |
2419 | #endif | ||
2320 | } | 2420 | } |
2321 | 2421 | ||
2322 | static int b43legacy_rng_init(struct b43legacy_wl *wl) | 2422 | static int b43legacy_rng_init(struct b43legacy_wl *wl) |
2323 | { | 2423 | { |
2324 | int err; | 2424 | int err = 0; |
2325 | 2425 | ||
2426 | #ifdef CONFIG_B43LEGACY_HWRNG | ||
2326 | snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), | 2427 | snprintf(wl->rng_name, ARRAY_SIZE(wl->rng_name), |
2327 | "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); | 2428 | "%s_%s", KBUILD_MODNAME, wiphy_name(wl->hw->wiphy)); |
2328 | wl->rng.name = wl->rng_name; | 2429 | wl->rng.name = wl->rng_name; |
@@ -2336,6 +2437,7 @@ static int b43legacy_rng_init(struct b43legacy_wl *wl) | |||
2336 | "number generator (%d)\n", err); | 2437 | "number generator (%d)\n", err); |
2337 | } | 2438 | } |
2338 | 2439 | ||
2440 | #endif | ||
2339 | return err; | 2441 | return err; |
2340 | } | 2442 | } |
2341 | 2443 | ||
@@ -3392,6 +3494,9 @@ static int b43legacy_op_start(struct ieee80211_hw *hw) | |||
3392 | memset(wl->bssid, 0, ETH_ALEN); | 3494 | memset(wl->bssid, 0, ETH_ALEN); |
3393 | memset(wl->mac_addr, 0, ETH_ALEN); | 3495 | memset(wl->mac_addr, 0, ETH_ALEN); |
3394 | wl->filter_flags = 0; | 3496 | wl->filter_flags = 0; |
3497 | wl->beacon0_uploaded = 0; | ||
3498 | wl->beacon1_uploaded = 0; | ||
3499 | wl->beacon_templates_virgin = 1; | ||
3395 | 3500 | ||
3396 | mutex_lock(&wl->mutex); | 3501 | mutex_lock(&wl->mutex); |
3397 | 3502 | ||
@@ -3429,6 +3534,7 @@ static void b43legacy_op_stop(struct ieee80211_hw *hw) | |||
3429 | struct b43legacy_wldev *dev = wl->current_dev; | 3534 | struct b43legacy_wldev *dev = wl->current_dev; |
3430 | 3535 | ||
3431 | b43legacy_rfkill_exit(dev); | 3536 | b43legacy_rfkill_exit(dev); |
3537 | cancel_work_sync(&(wl->beacon_update_trigger)); | ||
3432 | 3538 | ||
3433 | mutex_lock(&wl->mutex); | 3539 | mutex_lock(&wl->mutex); |
3434 | if (b43legacy_status(dev) >= B43legacy_STAT_STARTED) | 3540 | if (b43legacy_status(dev) >= B43legacy_STAT_STARTED) |
@@ -3760,6 +3866,7 @@ static int b43legacy_wireless_init(struct ssb_device *dev) | |||
3760 | spin_lock_init(&wl->leds_lock); | 3866 | spin_lock_init(&wl->leds_lock); |
3761 | mutex_init(&wl->mutex); | 3867 | mutex_init(&wl->mutex); |
3762 | INIT_LIST_HEAD(&wl->devlist); | 3868 | INIT_LIST_HEAD(&wl->devlist); |
3869 | INIT_WORK(&wl->beacon_update_trigger, b43legacy_beacon_update_trigger_work); | ||
3763 | 3870 | ||
3764 | ssb_set_devtypedata(dev, wl); | 3871 | ssb_set_devtypedata(dev, wl); |
3765 | b43legacyinfo(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id); | 3872 | b43legacyinfo(wl, "Broadcom %04X WLAN found\n", dev->bus->chip_id); |