static void wl1271_spi_read_busy(struct wl1271 *wl, void *buf, size_t len) { struct spi_transfer t[1]; struct spi_message m; u32 *busy_buf; int num_busy_bytes = 0; wl1271_info("spi read BUSY!"); /* * Look for the non-busy word in the read buffer, and if found, * read in the remaining data into the buffer. */ busy_buf = (u32 *)buf; for (; (u32)busy_buf < (u32)buf + len; busy_buf++) { num_busy_bytes += sizeof(u32); if (*busy_buf & 0x1) { spi_message_init(&m); memset(t, 0, sizeof(t)); memmove(buf, busy_buf, len - num_busy_bytes); t[0].rx_buf = buf + (len - num_busy_bytes); t[0].len = num_busy_bytes; spi_message_add_tail(&t[0], &m); spi_sync(wl->spi, &m); return; } } /* * Read further busy words from SPI until a non-busy word is * encountered, then read the data itself into the buffer. */ wl1271_info("spi read BUSY-polling needed!"); num_busy_bytes = WL1271_BUSY_WORD_TIMEOUT; busy_buf = wl->buffer_busyword; while (num_busy_bytes) { num_busy_bytes--; spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].rx_buf = busy_buf; t[0].len = sizeof(u32); spi_message_add_tail(&t[0], &m); spi_sync(wl->spi, &m); if (*busy_buf & 0x1) { spi_message_init(&m); memset(t, 0, sizeof(t)); t[0].rx_buf = buf; t[0].len = len; spi_message_add_tail(&t[0], &m); spi_sync(wl->spi, &m); return; } } /* The SPI bus is unresponsive, the read failed. */ memset(buf, 0, len); wl1271_error("SPI read busy-word timeout!\n"); }
static int wl1271_suspend(struct device *dev) { /* Tell MMC/SDIO core it's OK to power down the card * (if it isn't already), but not to remove it completely */ struct sdio_func *func = dev_to_sdio_func(dev); struct wl1271 *wl = sdio_get_drvdata(func); mmc_pm_flag_t sdio_flags; unsigned long flags; bool abort; int ret = 0; /* * if there is a pending irq work, we should abort the suspension. * (irq might come between mac80211 suspension and our suspension) * TODO: maybe remove it, since system will wake up anyway? */ spin_lock_irqsave(&wl->wl_lock, flags); abort = !!test_bit(WL1271_FLAG_PENDING_WORK, &wl->flags); spin_unlock_irqrestore(&wl->wl_lock, flags); if (abort) { wl1271_info("pending irq work - aborting suspend"); return -EBUSY; } /* * we need to look into wl to tell which suspend method to use. * we will have full power, ps mode, elp, and power off */ wl1271_info("%s: wow_enabled: %d", __func__, wl->wow_enabled); if (wl->wow_enabled) { sdio_flags = sdio_get_host_pm_caps(func); wl1271_info("suspend PM flags = 0x%x", sdio_flags); if (!(sdio_flags & MMC_PM_KEEP_POWER)) { wl1271_error("can't keep power while host " "is suspended"); goto power_off; } /* keep power while host suspended */ ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); if (ret) { wl1271_error("error while trying to keep power"); goto power_off; } /* release host */ sdio_release_host(func); } power_off: return 0; }
static void wlcore_event_time_sync(struct wl1271 *wl, u16 tsf_msb, u16 tsf_lsb) { u32 clock; /* convert the MSB+LSB to a u32 TSF value */ clock = (tsf_msb << 16) | tsf_lsb; wl1271_info("TIME_SYNC_EVENT_ID: clock %u", clock); }
static void wl1271_event_pspoll_delivery_fail(struct wl1271 *wl) { int delay = wl->conf.conn.ps_poll_recovery_period; int ret; wl->ps_poll_failures++; if (wl->ps_poll_failures == 1) wl1271_info("AP with dysfunctional ps-poll, " "trying to work around it."); /* force active mode receive data from the AP */ if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, wl->basic_rate, true); if (ret < 0) return; set_bit(WL1271_FLAG_PSPOLL_FAILURE, &wl->flags); ieee80211_queue_delayed_work(wl->hw, &wl->pspoll_work, msecs_to_jiffies(delay)); } /* * If already in active mode, lets we should be getting data from * the AP right away. If we enter PSM too fast after this, and data * remains on the AP, we will get another event like this, and we'll * go into active once more. */ }
int wlcore_boot_upload_firmware(struct wl1271 *wl) { u32 chunks, addr, len; int ret = 0; u8 *fw; fw = wl->fw; chunks = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u", chunks); while (chunks--) { addr = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); len = be32_to_cpup((__be32 *) fw); fw += sizeof(u32); if (len > 300000) { wl1271_info("firmware chunk too long: %u", len); return -EINVAL; } wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u", chunks, addr, len); ret = wl1271_boot_upload_firmware_chunk(wl, fw, len, addr); if (ret != 0) break; fw += len; } return ret; }
void wl1271_scan_complete_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; int ret; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, scan_complete_work); wl1271_debug(DEBUG_SCAN, "Scanning complete"); mutex_lock(&wl->mutex); if (unlikely(wl->state != WLCORE_STATE_ON)) goto out; if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; vif = wl->scan_vif; wlvif = wl12xx_vif_to_data(vif); /* * Rearm the tx watchdog just before idling scan. This * prevents just-finished scans from triggering the watchdog */ wl12xx_rearm_tx_watchdog_locked(wl); wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; wl->scan_vif = NULL; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { /* restore hardware connection monitoring template */ wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq); } wl1271_ps_elp_sleep(wl); if (wl->scan.failed) { wl1271_info("Scan completed due to error."); wl12xx_queue_recovery_work(wl); } wlcore_cmd_regdomain_config_locked(wl); ieee80211_scan_completed(wl->hw, false); out: mutex_unlock(&wl->mutex); }
static void wl1271_op_stop(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; int i; wl1271_info("down"); wl1271_debug(DEBUG_MAC80211, "mac80211 stop"); mutex_lock(&wl->mutex); WARN_ON(wl->state != WL1271_STATE_ON); if (wl->scanning) { mutex_unlock(&wl->mutex); ieee80211_scan_completed(wl->hw, true); mutex_lock(&wl->mutex); wl->scanning = false; } wl->state = WL1271_STATE_OFF; wl1271_disable_interrupts(wl); mutex_unlock(&wl->mutex); cancel_work_sync(&wl->irq_work); cancel_work_sync(&wl->tx_work); cancel_work_sync(&wl->filter_work); mutex_lock(&wl->mutex); wl1271_tx_flush(wl); wl1271_power_off(wl); memset(wl->bssid, 0, ETH_ALEN); memset(wl->ssid, 0, IW_ESSID_MAX_SIZE + 1); wl->ssid_len = 0; wl->listen_int = 1; wl->bss_type = MAX_BSS_TYPE; wl->rx_counter = 0; wl->elp = false; wl->psm = 0; wl->tx_queue_stopped = false; wl->power_level = WL1271_DEFAULT_POWER_LEVEL; wl->tx_blocks_available = 0; wl->tx_results_count = 0; wl->tx_packets_count = 0; wl->time_offset = 0; wl->session_counter = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; wl1271_debugfs_reset(wl); mutex_unlock(&wl->mutex); }
static int wl1271_event_ps_report(struct wl1271 *wl, struct event_mailbox *mbox, bool *beacon_loss) { int ret = 0; u32 total_retries = wl->conf.conn.psm_entry_retries; wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: wl1271_debug(DEBUG_PSM, "PSM entry failed"); if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { /* remain in active mode */ wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < total_retries) { wl->psm_entry_retry++; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true); } else { wl1271_info("No ack to nullfunc from AP."); wl->psm_entry_retry = 0; *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; /* enable beacon filtering */ ret = wl1271_acx_beacon_filter_opt(wl, true); if (ret < 0) break; /* * BET has only a minor effect in 5GHz and masks * channel switch IEs, so we only enable BET on 2.4GHz */ if (wl->band == IEEE80211_BAND_2GHZ) /* enable beacon early termination */ ret = wl1271_acx_bet_enable(wl, true); if (wl->ps_compl) { complete(wl->ps_compl); wl->ps_compl = NULL; } break; default: break; } return ret; }
void wl1271_scan_complete_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; int ret; bool is_sta, is_ibss; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, scan_complete_work); wl1271_debug(DEBUG_SCAN, "Scanning complete"); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) goto out; if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { /* restore hardware connection monitoring template */ wl1271_cmd_build_ap_probe_req(wl, wl->probereq); } /* return to ROC if needed */ is_sta = (wl->bss_type == BSS_TYPE_STA_BSS); is_ibss = (wl->bss_type == BSS_TYPE_IBSS); if (((is_sta && !test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) || (is_ibss && !test_bit(WL1271_FLAG_IBSS_JOINED, &wl->flags))) && !test_bit(wl->dev_role_id, wl->roc_map)) { /* restore remain on channel */ wl12xx_cmd_role_start_dev(wl); wl12xx_roc(wl, wl->dev_role_id); } wl1271_ps_elp_sleep(wl); if (wl->scan.failed) { wl1271_info("Scan completed due to error."); wl12xx_queue_recovery_work(wl); } ieee80211_scan_completed(wl->hw, false); out: mutex_unlock(&wl->mutex); }
void wl1271_scan_complete_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; struct ieee80211_vif *vif; struct wl12xx_vif *wlvif; int ret; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, scan_complete_work); wl1271_debug(DEBUG_SCAN, "Scanning complete"); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) goto out; if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; vif = wl->scan_vif; wlvif = wl12xx_vif_to_data(vif); wl12xx_rearm_tx_watchdog_locked(wl); wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; wl->scan_vif = NULL; ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; if (test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { wl1271_cmd_build_ap_probe_req(wl, wlvif, wlvif->probereq); } wl1271_ps_elp_sleep(wl); if (wl->scan.failed) { wl1271_info("Scan completed due to error."); wl12xx_queue_recovery_work(wl); } ieee80211_scan_completed(wl->hw, false); out: mutex_unlock(&wl->mutex); }
static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { int ret; u32 vector; bool beacon_loss = false; wl1271_event_mbox_dump(mbox); vector = le32_to_cpu(mbox->events_vector); vector &= ~(le32_to_cpu(mbox->events_mask)); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { ret = wl1271_event_scan_complete(wl, mbox); if (ret < 0) return ret; } /* * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon * filtering) is enabled. Without PSM, the stack will receive all * beacons and can detect beacon loss by itself. * * As there's possibility that the driver disables PSM before receiving * BSS_LOSE_EVENT, beacon loss has to be reported to the stack. * */ if (vector & BSS_LOSE_EVENT_ID) { wl1271_info("Beacon loss detected."); /* indicate to the stack, that beacons have been lost */ beacon_loss = true; } if (vector & PS_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); if (ret < 0) return ret; } if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); if (wl->vif) wl1271_event_rssi_trigger(wl, mbox); } if (wl->vif && beacon_loss) ieee80211_connection_loss(wl->vif); return 0; }
static int wl1271_resume(struct device *dev) { struct sdio_func *func = dev_to_sdio_func(dev); struct wl1271 *wl = sdio_get_drvdata(func); wl1271_info("wl1271 resume"); if (wl->wow_enabled) { /* claim back host */ sdio_claim_host(func); } return 0; }
static ssize_t tx_num_comp_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; mutex_lock(&wl->mutex); if (unlikely(wl->state != WLCORE_STATE_ON)) goto out; wl1271_info("zeroing out Tx num completion reasons"); memset(wl->tx_completions, 0, sizeof(wl->tx_completions)); out: mutex_unlock(&wl->mutex); return count; }
static ssize_t stats_tx_aggr_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; mutex_lock(&wl->mutex); if (unlikely(wl->state != WLCORE_STATE_ON)) goto out; wl1271_info("zeroing out aggr pkts reasons"); memset(wl->aggr_pkts_reason, 0, sizeof(struct wlcore_aggr_reason) * wl->aggr_pkts_reason_num); out: mutex_unlock(&wl->mutex); return count; }
static int wl1271_tx_send_packet(struct wl1271 *wl, struct sk_buff *skb, struct ieee80211_tx_info *control) { struct wl1271_tx_hw_descr *desc; int len; /* FIXME: This is a workaround for getting non-aligned packets. This happens at least with EAPOL packets from the user space. Our DMA requires packets to be aligned on a 4-byte boundary. */ if (unlikely((long)skb->data & 0x03)) { int offset = (4 - (long)skb->data) & 0x03; wl1271_debug(DEBUG_TX, "skb offset %d", offset); /* check whether the current skb can be used */ if (!skb_cloned(skb) && (skb_tailroom(skb) >= offset)) { unsigned char *src = skb->data; /* align the buffer on a 4-byte boundary */ skb_reserve(skb, offset); memmove(skb->data, src, skb->len); } else { wl1271_info("No handler, fixme!"); return -EINVAL; } } len = WL1271_TX_ALIGN(skb->len); /* perform a fixed address block write with the packet */ wl1271_spi_reg_write(wl, WL1271_SLV_MEM_DATA, skb->data, len, true); /* write packet new counter into the write access register */ wl->tx_packets_count++; wl1271_reg_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); desc = (struct wl1271_tx_hw_descr *) skb->data; wl1271_debug(DEBUG_TX, "tx id %u skb 0x%p payload %u (%u words)", desc->id, skb, len, desc->length); return 0; }
static int wl1271_op_start(struct ieee80211_hw *hw) { struct wl1271 *wl = hw->priv; int ret = 0; wl1271_debug(DEBUG_MAC80211, "mac80211 start"); mutex_lock(&wl->mutex); if (wl->state != WL1271_STATE_OFF) { wl1271_error("cannot start because not in off state: %d", wl->state); ret = -EBUSY; goto out; } ret = wl1271_chip_wakeup(wl); if (ret < 0) goto out; ret = wl1271_boot(wl); if (ret < 0) goto out; ret = wl1271_hw_init(wl); if (ret < 0) goto out; wl->state = WL1271_STATE_ON; wl1271_info("firmware booted (%s)", wl->chip.fw_ver); out: if (ret < 0) wl1271_power_off(wl); mutex_unlock(&wl->mutex); return ret; }
void wl1271_scan_complete_work(struct work_struct *work) { struct delayed_work *dwork; struct wl1271 *wl; dwork = container_of(work, struct delayed_work, work); wl = container_of(dwork, struct wl1271, scan_complete_work); wl1271_debug(DEBUG_SCAN, "Scanning complete"); mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) goto out; if (wl->scan.state == WL1271_SCAN_STATE_IDLE) goto out; wl->scan.state = WL1271_SCAN_STATE_IDLE; memset(wl->scan.scanned_ch, 0, sizeof(wl->scan.scanned_ch)); wl->scan.req = NULL; ieee80211_scan_completed(wl->hw, false); /* restore hardware connection monitoring template */ if (test_bit(WL1271_FLAG_STA_ASSOCIATED, &wl->flags)) { if (wl1271_ps_elp_wakeup(wl) == 0) { wl1271_cmd_build_ap_probe_req(wl, wl->probereq); wl1271_ps_elp_sleep(wl); } } if (wl->scan.failed) { wl1271_info("Scan completed due to error."); wl12xx_queue_recovery_work(wl); } out: mutex_unlock(&wl->mutex); }
static int wl1271_event_ps_report(struct wl1271 *wl, struct event_mailbox *mbox, bool *beacon_loss) { int ret = 0; wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: if (!wl->psm) { wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < wl->conf.conn.psm_entry_retries) { wl->psm_entry_retry++; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); } else { wl1271_error("PSM entry failed, giving up.\n"); wl->psm_entry_retry = 0; *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; break; case EVENT_EXIT_POWER_SAVE_FAIL: wl1271_info("PSM exit failed"); break; case EVENT_EXIT_POWER_SAVE_SUCCESS: default: break; } return ret; }
static ssize_t split_scan_timeout_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { struct wl1271 *wl = file->private_data; unsigned long value; int ret; ret = kstrtoul_from_user(user_buf, count, 10, &value); if (ret < 0) { wl1271_warning("illegal value in split_scan_timeout"); return -EINVAL; } if (value == 0) wl1271_info("split scan will be disabled"); mutex_lock(&wl->mutex); wl->conf.scan.split_scan_timeout = value * 1000; mutex_unlock(&wl->mutex); return count; }
static int wl1271_event_ps_report(struct wl1271 *wl, struct event_mailbox *mbox, bool *beacon_loss) { int ret = 0; u32 total_retries = wl->conf.conn.psm_entry_retries; wl1271_debug(DEBUG_EVENT, "ps_status: 0x%x", mbox->ps_status); switch (mbox->ps_status) { case EVENT_ENTER_POWER_SAVE_FAIL: wl1271_debug(DEBUG_PSM, "PSM entry failed"); if (!test_bit(WL1271_FLAG_PSM, &wl->flags)) { /* remain in active mode */ wl->psm_entry_retry = 0; break; } if (wl->psm_entry_retry < total_retries) { wl->psm_entry_retry++; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, wl->basic_rate, true); } else { wl1271_info("No ack to nullfunc from AP."); wl->psm_entry_retry = 0; *beacon_loss = true; } break; case EVENT_ENTER_POWER_SAVE_SUCCESS: wl->psm_entry_retry = 0; /* enable beacon filtering */ ret = wl1271_acx_beacon_filter_opt(wl, true); if (ret < 0) break; /* enable beacon early termination */ ret = wl1271_acx_bet_enable(wl, true); if (ret < 0) break; /* go to extremely low power mode */ wl1271_ps_elp_sleep(wl); break; case EVENT_EXIT_POWER_SAVE_FAIL: wl1271_debug(DEBUG_PSM, "PSM exit failed"); if (test_bit(WL1271_FLAG_PSM, &wl->flags)) { wl->psm_entry_retry = 0; break; } /* make sure the firmware goes to active mode - the frame to be sent next will indicate to the AP, that we are active. */ ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE, wl->basic_rate, false); break; case EVENT_EXIT_POWER_SAVE_SUCCESS: default: break; } return ret; }
static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { int ret; u32 vector; bool beacon_loss = false; bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); bool disconnect_sta = false; unsigned long sta_bitmap = 0; wl1271_event_mbox_dump(mbox); vector = le32_to_cpu(mbox->events_vector); vector &= ~(le32_to_cpu(mbox->events_mask)); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); wl1271_scan_stm(wl); } /* disable dynamic PS when requested by the firmware */ if (vector & SOFT_GEMINI_SENSE_EVENT_ID && wl->bss_type == BSS_TYPE_STA_BSS) { if (mbox->soft_gemini_sense_info) ieee80211_disable_dyn_ps(wl->vif); else ieee80211_enable_dyn_ps(wl->vif); } /* * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon * filtering) is enabled. Without PSM, the stack will receive all * beacons and can detect beacon loss by itself. * * As there's possibility that the driver disables PSM before receiving * BSS_LOSE_EVENT, beacon loss has to be reported to the stack. * */ if ((vector & BSS_LOSE_EVENT_ID) && !is_ap) { wl1271_info("Beacon loss detected."); /* indicate to the stack, that beacons have been lost */ beacon_loss = true; } if ((vector & PS_REPORT_EVENT_ID) && !is_ap) { wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); if (ret < 0) return ret; } if ((vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) && !is_ap) wl1271_event_pspoll_delivery_fail(wl); if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); if (wl->vif) wl1271_event_rssi_trigger(wl, mbox); } if ((vector & DUMMY_PACKET_EVENT_ID) && !is_ap) { wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); if (wl->vif) wl1271_tx_dummy_packet(wl); } /* * "TX retries exceeded" has a different meaning according to mode. * In AP mode the offending station is disconnected. In STA mode we * report connection loss. */ if (vector & MAX_TX_RETRY_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); if (is_ap) { sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); disconnect_sta = true; } else { beacon_loss = true; } } if ((vector & INACTIVE_STA_EVENT_ID) && is_ap) { wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); disconnect_sta = true; } if (wl->vif && beacon_loss) ieee80211_connection_loss(wl->vif); if (is_ap && disconnect_sta) { u32 num_packets = wl->conf.tx.max_tx_retries; struct ieee80211_sta *sta; const u8 *addr; int h; for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS); h < AP_MAX_LINKS; h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) { if (!wl1271_is_active_sta(wl, h)) continue; addr = wl->links[h].addr; rcu_read_lock(); sta = ieee80211_find_sta(wl->vif, addr); if (sta) { wl1271_debug(DEBUG_EVENT, "remove sta %d", h); #if 0 ieee80211_report_low_ack(sta, num_packets); #endif } rcu_read_unlock(); } } return 0; }
int wl18xx_process_mailbox_events(struct wl1271 *wl) { struct wl18xx_event_mailbox *mbox = wl->mbox; u32 vector; vector = le32_to_cpu(mbox->events_vector); wl1271_debug(DEBUG_EVENT, "MBOX vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "scan results: %d", mbox->number_of_scan_results); if (wl->scan_wlvif) wl18xx_scan_completed(wl, wl->scan_wlvif); } if (vector & TIME_SYNC_EVENT_ID) wlcore_event_time_sync(wl, mbox->time_sync_tsf_msb, mbox->time_sync_tsf_lsb); if (vector & RADAR_DETECTED_EVENT_ID) { wl1271_info("radar event: channel %d type %s", mbox->radar_channel, wl18xx_radar_type_decode(mbox->radar_type)); ieee80211_radar_detected(wl->hw); } if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT (results %d)", mbox->number_of_sched_scan_results); wlcore_scan_sched_scan_results(wl); } if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) wlcore_event_sched_scan_completed(wl, 1); if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) wlcore_event_rssi_trigger(wl, mbox->rssi_snr_trigger_metric); if (vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID) wlcore_event_ba_rx_constraint(wl, le16_to_cpu(mbox->rx_ba_role_id_bitmap), le16_to_cpu(mbox->rx_ba_allowed_bitmap)); if (vector & BSS_LOSS_EVENT_ID) wlcore_event_beacon_loss(wl, le16_to_cpu(mbox->bss_loss_bitmap)); if (vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) wlcore_event_channel_switch(wl, le16_to_cpu(mbox->channel_switch_role_id_bitmap), true); if (vector & DUMMY_PACKET_EVENT_ID) wlcore_event_dummy_packet(wl); /* * "TX retries exceeded" has a different meaning according to mode. * In AP mode the offending station is disconnected. */ if (vector & MAX_TX_FAILURE_EVENT_ID) wlcore_event_max_tx_failure(wl, le32_to_cpu(mbox->tx_retry_exceeded_bitmap)); if (vector & INACTIVE_STA_EVENT_ID) wlcore_event_inactive_sta(wl, le32_to_cpu(mbox->inactive_sta_bitmap)); if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) wlcore_event_roc_complete(wl); if (vector & SMART_CONFIG_SYNC_EVENT_ID) wlcore_smart_config_sync_event(wl, mbox->sc_sync_channel, mbox->sc_sync_band); if (vector & SMART_CONFIG_DECODE_EVENT_ID) wlcore_smart_config_decode_event(wl, mbox->sc_ssid_len, mbox->sc_ssid, mbox->sc_pwd_len, mbox->sc_pwd); if (vector & FW_LOGGER_INDICATION) wlcore_event_fw_logger(wl); return 0; }
static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1271 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (channel != wl->channel) { u8 old_channel = wl->channel; wl->channel = channel; /* FIXME: use beacon interval provided by mac80211 */ ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); if (ret < 0) { wl->channel = old_channel; goto out_sleep; } } ret = wl1271_cmd_build_null_data(wl); if (ret < 0) goto out_sleep; if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1271_info("psm enabled"); wl->psm_requested = true; /* * We enter PSM only if we're already associated. * If we're not, we'll enter it when joining an SSID, * through the bss_info_changed() hook. */ ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { wl1271_info("psm disabled"); wl->psm_requested = false; if (wl->psm) ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); } if (conf->power_level != wl->power_level) { ret = wl1271_acx_tx_power(wl, conf->power_level); if (ret < 0) goto out; wl->power_level = conf->power_level; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; }
static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { int ret; u32 vector; bool beacon_loss = false; bool is_ap = (wl->bss_type == BSS_TYPE_AP_BSS); bool disconnect_sta = false; unsigned long sta_bitmap = 0; wl1271_event_mbox_dump(mbox); vector = le32_to_cpu(mbox->events_vector); vector &= ~(le32_to_cpu(mbox->events_mask)); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); wl1271_scan_stm(wl); } if (vector & PERIODIC_SCAN_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_REPORT_EVENT " "(status 0x%0x)", mbox->scheduled_scan_status); wl1271_scan_sched_scan_results(wl); } if (vector & PERIODIC_SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PERIODIC_SCAN_COMPLETE_EVENT " "(status 0x%0x)", mbox->scheduled_scan_status); if (wl->sched_scanning) { ieee80211_sched_scan_stopped(wl->hw); wl->sched_scanning = false; } } if (vector & SOFT_GEMINI_SENSE_EVENT_ID && wl->bss_type == BSS_TYPE_STA_BSS) wl12xx_event_soft_gemini_sense(wl, mbox->soft_gemini_sense_info); if (vector & CHANGE_AUTO_MODE_TIMEOUT_EVENT_ID && wl->bss_type == BSS_TYPE_STA_BSS) { int timeout = 0; if (mbox->change_auto_mode_timeout) timeout = 500; ieee80211_set_dyn_ps_timeout(wl->vif, timeout); } /* * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon * filtering) is enabled. Without PSM, the stack will receive all * beacons and can detect beacon loss by itself. * * As there's possibility that the driver disables PSM before receiving * BSS_LOSE_EVENT, beacon loss has to be reported to the stack. * */ if ((vector & BSS_LOSE_EVENT_ID) && !is_ap) { wl1271_info("Beacon loss detected."); /* indicate to the stack, that beacons have been lost */ beacon_loss = true; } if ((vector & PS_REPORT_EVENT_ID) && !is_ap) { wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); if (ret < 0) return ret; } if ((vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) && !is_ap) wl1271_event_pspoll_delivery_fail(wl); if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); if (wl->vif) wl1271_event_rssi_trigger(wl, mbox); } if ((vector & BA_SESSION_RX_CONSTRAINT_EVENT_ID)) { wl1271_debug(DEBUG_EVENT, "BA_SESSION_RX_CONSTRAINT_EVENT_ID. " "ba_allowed = 0x%x", mbox->rx_ba_allowed); wl->ba_allowed = !!mbox->rx_ba_allowed; if (wl->vif && !wl->ba_allowed) wl1271_stop_ba_event(wl); } if ((vector & CHANNEL_SWITCH_COMPLETE_EVENT_ID) && !is_ap) { wl1271_debug(DEBUG_EVENT, "CHANNEL_SWITCH_COMPLETE_EVENT_ID. " "channel_switch_status = 0x%x", mbox->channel_switch_status); /* * That event uses for two cases: * 1) channel switch complete with channel_switch_status=0 * 2) fixing beacon actual TSF with channel_switch_status=1 * calling chswitch_done only for the first option */ if (!mbox->channel_switch_status && test_and_clear_bit(WL1271_FLAG_CS_PROGRESS, &wl->flags) && (wl->vif)) ieee80211_chswitch_done(wl->vif, true); } if ((vector & DUMMY_PACKET_EVENT_ID)) { wl1271_debug(DEBUG_EVENT, "DUMMY_PACKET_ID_EVENT_ID"); if (wl->vif) wl1271_tx_dummy_packet(wl); } if (vector & DISCONNECT_EVENT_COMPLETE_ID) wl1271_debug(DEBUG_EVENT, "disconnect event"); /* * "TX retries exceeded" has a different meaning according to mode. * In AP mode the offending station is disconnected. */ if ((vector & MAX_TX_RETRY_EVENT_ID) && is_ap) { wl1271_debug(DEBUG_EVENT, "MAX_TX_RETRY_EVENT_ID"); sta_bitmap |= le16_to_cpu(mbox->sta_tx_retry_exceeded); disconnect_sta = true; } if ((vector & INACTIVE_STA_EVENT_ID) && is_ap) { wl1271_debug(DEBUG_EVENT, "INACTIVE_STA_EVENT_ID"); sta_bitmap |= le16_to_cpu(mbox->sta_aging_status); disconnect_sta = true; } if (is_ap && disconnect_sta) { u32 num_packets = wl->conf.tx.max_tx_retries; struct ieee80211_sta *sta; const u8 *addr; int h; for (h = find_first_bit(&sta_bitmap, AP_MAX_LINKS); h < AP_MAX_LINKS; h = find_next_bit(&sta_bitmap, AP_MAX_LINKS, h+1)) { if (!wl1271_is_active_sta(wl, h)) continue; addr = wl->links[h].addr; rcu_read_lock(); sta = ieee80211_find_sta(wl->vif, addr); if (sta) { wl1271_debug(DEBUG_EVENT, "remove sta %d", h); ieee80211_report_low_ack(sta, num_packets); } rcu_read_unlock(); } } if (wl->vif && beacon_loss) ieee80211_connection_loss(wl->vif); return 0; }
int wl1271_scan_sched_scan_config(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct cfg80211_sched_scan_request *req, struct ieee80211_sched_scan_ies *ies) { struct wl1271_cmd_scan_params *cmd; struct conf_sched_scan_settings *c = &wl->conf.sched_scan; int ret; int filter_type; wl1271_debug(DEBUG_CMD, "cmd sched_scan scan config"); if (req->n_short_intervals > SCAN_MAX_SHORT_INTERVALS) { wl1271_warning("Number of short intervals requested (%d)" "exceeds limit (%d)", req->n_short_intervals, SCAN_MAX_SHORT_INTERVALS); return -EINVAL; } filter_type = wl12xx_scan_set_ssid_list(wl,req); if (filter_type < 0) return filter_type; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out; } cmd->role_id = wlvif->role_id; /* report APs when at least 1 is found */ //cfg->report_after = 1; if (WARN_ON(cmd->role_id == WL12XX_INVALID_ROLE_ID)) { ret = -EINVAL; goto out; } cmd->scan_type = SCAN_TYPE_PERIODIC; cmd->rssi_threshold = c->rssi_threshold; cmd->snr_threshold = c->snr_threshold; /* don't filter on BSS type */ cmd->bss_type = SCAN_BSS_TYPE_ANY; cmd->ssid_from_list = 1; if (filter_type == SCAN_SSID_FILTER_LIST) cmd->filter = 1; cmd->add_broadcast = 0; /* TODO: figure this ones out */ cmd->urgency = 0; cmd->protect = 0; cmd->n_probe_reqs = c->num_probe_reqs; /* don't stop scanning automatically when something is found */ cmd->terminate_after = 0; /* configure channels */ wlcore_set_scan_chan_params(wl, cmd, req->channels, req->n_channels, req->n_ssids, SCAN_TYPE_PERIODIC); /* TODO: check params */ cmd->short_cycles_sec = req->short_interval; cmd->long_cycles_sec = req->long_interval; cmd->short_cycles_count = req->n_short_intervals; cmd->total_cycles = 0; /* TODO: how to set tx rate? */ cmd->tag = WL1271_SCAN_DEFAULT_TAG; /* create a PERIODIC_SCAN_REPORT_EVENT whenever we've got a match */ cmd->report_threshold = 1; cmd->terminate_on_report = 0; if (cmd->active[0]) { u8 band = IEEE80211_BAND_2GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, cmd->role_id, band, req->ssids ? req->ssids[0].ssid : NULL, req->ssids ? req->ssids[0].ssid_len : 0, ies->ie[band], ies->len[band], true); if (ret < 0) { wl1271_error("2.4GHz PROBE request template failed"); goto out; } } if (cmd->active[1] || cmd->dfs) { u8 band = IEEE80211_BAND_5GHZ; ret = wl12xx_cmd_build_probe_req(wl, wlvif, cmd->role_id, band, req->ssids ? req->ssids[0].ssid : NULL, req->ssids ? req->ssids[0].ssid_len : 0, ies->ie[band], ies->len[band], true); if (ret < 0) { wl1271_error("5GHz PROBE request template failed"); goto out; } } wl1271_dump(DEBUG_SCAN, "SCAN: ", cmd, sizeof(*cmd)); wl1271_info("scan size: %d", sizeof(*cmd)); ret = wl1271_cmd_send(wl, CMD_SCAN, cmd, sizeof(*cmd), 0); if (ret < 0) { wl1271_error("SCAN failed"); goto out; } out: kfree(cmd); return ret; }
static int wl1271_event_process(struct wl1271 *wl, struct event_mailbox *mbox) { int ret; u32 vector; bool beacon_loss = false; wl1271_event_mbox_dump(mbox); vector = le32_to_cpu(mbox->events_vector); vector &= ~(le32_to_cpu(mbox->events_mask)); wl1271_debug(DEBUG_EVENT, "vector: 0x%x", vector); if (vector & SCAN_COMPLETE_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "status: 0x%x", mbox->scheduled_scan_status); wl1271_scan_stm(wl); } /* disable dynamic PS when requested by the firmware */ if (vector & SOFT_GEMINI_SENSE_EVENT_ID && wl->bss_type == BSS_TYPE_STA_BSS) { if (mbox->soft_gemini_sense_info) ieee80211_disable_dyn_ps(wl->vif); else ieee80211_enable_dyn_ps(wl->vif); } /* * The BSS_LOSE_EVENT_ID is only needed while psm (and hence beacon * filtering) is enabled. Without PSM, the stack will receive all * beacons and can detect beacon loss by itself. * * As there's possibility that the driver disables PSM before receiving * BSS_LOSE_EVENT, beacon loss has to be reported to the stack. * */ if (vector & BSS_LOSE_EVENT_ID) { wl1271_info("Beacon loss detected."); /* indicate to the stack, that beacons have been lost */ beacon_loss = true; } if (vector & PS_REPORT_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "PS_REPORT_EVENT"); ret = wl1271_event_ps_report(wl, mbox, &beacon_loss); if (ret < 0) return ret; } if (vector & PSPOLL_DELIVERY_FAILURE_EVENT_ID) wl1271_event_pspoll_delivery_fail(wl); if (vector & RSSI_SNR_TRIGGER_0_EVENT_ID) { wl1271_debug(DEBUG_EVENT, "RSSI_SNR_TRIGGER_0_EVENT"); if (wl->vif) wl1271_event_rssi_trigger(wl, mbox); } if (wl->vif && beacon_loss) ieee80211_connection_loss(wl->vif); return 0; }
static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[]) { int buf_len, ret, len; struct sk_buff *skb; void *buf; u8 answer = 0; wl1271_debug(DEBUG_TESTMODE, "testmode cmd test"); if (!tb[WL1271_TM_ATTR_DATA]) return -EINVAL; buf = nla_data(tb[WL1271_TM_ATTR_DATA]); buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); if (tb[WL1271_TM_ATTR_ANSWER]) answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]); if (buf_len > sizeof(struct wl1271_command)) return -EMSGSIZE; mutex_lock(&wl->mutex); if (wl->state == WL1271_STATE_OFF) { ret = -EINVAL; goto out; } ret = wl1271_ps_elp_wakeup(wl); if (ret < 0) goto out; ret = wl1271_cmd_test(wl, buf, buf_len, answer); if (ret < 0) { wl1271_warning("testmode cmd test failed: %d", ret); goto out_sleep; } if (answer) { /* If we got bip calibration answer print radio status */ struct wl1271_cmd_cal_p2g *params = (struct wl1271_cmd_cal_p2g *) buf; s16 radio_status = (s16) le16_to_cpu(params->radio_status); if (params->test.id == TEST_CMD_P2G_CAL && radio_status < 0) wl1271_warning("testmode cmd: radio status=%d", radio_status); else wl1271_info("testmode cmd: radio status=%d", radio_status); len = nla_total_size(buf_len); skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); if (!skb) { ret = -ENOMEM; goto out_sleep; } if (nla_put(skb, WL1271_TM_ATTR_DATA, buf_len, buf)) { kfree_skb(skb); ret = -EMSGSIZE; goto out_sleep; } ret = cfg80211_testmode_reply(skb); if (ret < 0) goto out_sleep; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; }
static int wl1271_op_config(struct ieee80211_hw *hw, u32 changed) { struct wl1271 *wl = hw->priv; struct ieee80211_conf *conf = &hw->conf; int channel, ret = 0; channel = ieee80211_frequency_to_channel(conf->channel->center_freq); wl1271_debug(DEBUG_MAC80211, "mac80211 config ch %d psm %s power %d", channel, conf->flags & IEEE80211_CONF_PS ? "on" : "off", conf->power_level); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (channel != wl->channel) { u8 old_channel = wl->channel; wl->channel = channel; ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); if (ret < 0) { wl->channel = old_channel; goto out_sleep; } } ret = wl1271_cmd_build_null_data(wl); if (ret < 0) goto out_sleep; if (conf->flags & IEEE80211_CONF_PS && !wl->psm_requested) { wl1271_info("psm enabled"); wl->psm_requested = true; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE); } else if (!(conf->flags & IEEE80211_CONF_PS) && wl->psm_requested) { wl1271_info("psm disabled"); wl->psm_requested = false; if (wl->psm) ret = wl1271_ps_set_mode(wl, STATION_ACTIVE_MODE); } if (conf->power_level != wl->power_level) { ret = wl1271_acx_tx_power(wl, conf->power_level); if (ret < 0) goto out; wl->power_level = conf->power_level; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; }