static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) { int ret; struct wl1271_command *cmd; struct sk_buff *skb; u8 ie_id; wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate"); if (!tb[WL1271_TM_ATTR_IE_ID]) return -EINVAL; ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); 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; cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) { ret = -ENOMEM; goto out_sleep; } ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); if (ret < 0) { wl1271_warning("testmode cmd interrogate failed: %d", ret); goto out_free; } skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd)); if (!skb) { ret = -ENOMEM; goto out_free; } NLA_PUT(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd); ret = cfg80211_testmode_reply(skb); if (ret < 0) goto out_free; out_free: kfree(cmd); out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; nla_put_failure: kfree_skb(skb); ret = -EMSGSIZE; goto out_free; }
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) { len = nla_total_size(buf_len); skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); if (!skb) { ret = -ENOMEM; goto out_sleep; } NLA_PUT(skb, WL1271_TM_ATTR_DATA, buf_len, buf); 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; nla_put_failure: kfree_skb(skb); ret = -EMSGSIZE; goto out_sleep; }
static int wl1271_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key_conf) { struct wl1271 *wl = hw->priv; const u8 *addr; int ret; u8 key_type; static const u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; wl1271_debug(DEBUG_MAC80211, "mac80211 set key"); addr = sta ? sta->addr : bcast_addr; wl1271_debug(DEBUG_CRYPT, "CMD: 0x%x", cmd); wl1271_dump(DEBUG_CRYPT, "ADDR: ", addr, ETH_ALEN); wl1271_debug(DEBUG_CRYPT, "Key: algo:0x%x, id:%d, len:%d flags 0x%x", key_conf->alg, key_conf->keyidx, key_conf->keylen, key_conf->flags); wl1271_dump(DEBUG_CRYPT, "KEY: ", key_conf->key, key_conf->keylen); if (is_zero_ether_addr(addr)) { /* We dont support TX only encryption */ ret = -EOPNOTSUPP; goto out; } mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out_unlock; switch (key_conf->alg) { case ALG_WEP: key_type = KEY_WEP; key_conf->hw_key_idx = key_conf->keyidx; break; case ALG_TKIP: key_type = KEY_TKIP; key_conf->hw_key_idx = key_conf->keyidx; break; case ALG_CCMP: key_type = KEY_AES; key_conf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; break; default: wl1271_error("Unknown key algo 0x%x", key_conf->alg); ret = -EOPNOTSUPP; goto out_sleep; } switch (cmd) { case SET_KEY: ret = wl1271_cmd_set_key(wl, KEY_ADD_OR_REPLACE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, addr); if (ret < 0) { wl1271_error("Could not add or replace key"); goto out_sleep; } break; case DISABLE_KEY: ret = wl1271_cmd_set_key(wl, KEY_REMOVE, key_conf->keyidx, key_type, key_conf->keylen, key_conf->key, addr); if (ret < 0) { wl1271_error("Could not remove key"); goto out_sleep; } break; default: wl1271_error("Unsupported key cmd 0x%x", cmd); ret = -EOPNOTSUPP; goto out_sleep; break; } out_sleep: wl1271_ps_elp_sleep(wl); out_unlock: mutex_unlock(&wl->mutex); out: return ret; }
static void wl1271_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, u32 changed) { enum wl1271_cmd_ps_mode mode; struct wl1271 *wl = hw->priv; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 bss info changed"); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; if (changed & BSS_CHANGED_ASSOC) { if (bss_conf->assoc) { wl->aid = bss_conf->aid; ret = wl1271_cmd_build_ps_poll(wl, wl->aid); if (ret < 0) goto out_sleep; ret = wl1271_acx_aid(wl, wl->aid); if (ret < 0) goto out_sleep; /* If we want to go in PSM but we're not there yet */ if (wl->psm_requested && !wl->psm) { mode = STATION_POWER_SAVE_MODE; ret = wl1271_ps_set_mode(wl, mode); if (ret < 0) goto out_sleep; } } } if (changed & BSS_CHANGED_ERP_SLOT) { if (bss_conf->use_short_slot) ret = wl1271_acx_slot(wl, SLOT_TIME_SHORT); else ret = wl1271_acx_slot(wl, SLOT_TIME_LONG); if (ret < 0) { wl1271_warning("Set slot time failed %d", ret); goto out_sleep; } } if (changed & BSS_CHANGED_ERP_PREAMBLE) { if (bss_conf->use_short_preamble) wl1271_acx_set_preamble(wl, ACX_PREAMBLE_SHORT); else wl1271_acx_set_preamble(wl, ACX_PREAMBLE_LONG); } if (changed & BSS_CHANGED_ERP_CTS_PROT) { if (bss_conf->use_cts_prot) ret = wl1271_acx_cts_protect(wl, CTSPROTECT_ENABLE); else ret = wl1271_acx_cts_protect(wl, CTSPROTECT_DISABLE); if (ret < 0) { wl1271_warning("Set ctsprotect failed %d", ret); goto out_sleep; } } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); }
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_op_config_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_if_conf *conf) { struct wl1271 *wl = hw->priv; struct sk_buff *beacon; int ret; wl1271_debug(DEBUG_MAC80211, "mac80211 config_interface bssid %pM", conf->bssid); wl1271_dump_ascii(DEBUG_MAC80211, "ssid: ", conf->ssid, conf->ssid_len); mutex_lock(&wl->mutex); ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) goto out; memcpy(wl->bssid, conf->bssid, ETH_ALEN); ret = wl1271_cmd_build_null_data(wl); if (ret < 0) goto out_sleep; wl->ssid_len = conf->ssid_len; if (wl->ssid_len) memcpy(wl->ssid, conf->ssid, wl->ssid_len); if (wl->bss_type != BSS_TYPE_IBSS) { /* FIXME: replace the magic numbers with proper definitions */ ret = wl1271_cmd_join(wl, wl->bss_type, 5, 100, 1); if (ret < 0) goto out_sleep; } if (conf->changed & IEEE80211_IFCC_BEACON) { beacon = ieee80211_beacon_get(hw, vif); ret = wl1271_cmd_template_set(wl, CMD_TEMPL_BEACON, beacon->data, beacon->len); if (ret < 0) { dev_kfree_skb(beacon); goto out_sleep; } ret = wl1271_cmd_template_set(wl, CMD_TEMPL_PROBE_RESPONSE, beacon->data, beacon->len); dev_kfree_skb(beacon); if (ret < 0) goto out_sleep; /* FIXME: replace the magic numbers with proper definitions */ ret = wl1271_cmd_join(wl, wl->bss_type, 1, 100, 0); if (ret < 0) goto out_sleep; } out_sleep: wl1271_ps_elp_sleep(wl); out: mutex_unlock(&wl->mutex); return ret; }
static void wl1271_irq_work(struct work_struct *work) { u32 intr, ctr = WL1271_IRQ_MAX_LOOPS; int ret; struct wl1271 *wl = container_of(work, struct wl1271, irq_work); mutex_lock(&wl->mutex); wl1271_debug(DEBUG_IRQ, "IRQ work"); if (wl->state == WL1271_STATE_OFF) goto out; ret = wl1271_ps_elp_wakeup(wl, true); if (ret < 0) goto out; wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL); intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); if (!intr) { wl1271_debug(DEBUG_IRQ, "Zero interrupt received."); goto out_sleep; } intr &= WL1271_INTR_MASK; do { wl1271_fw_status(wl, wl->fw_status); if (intr & (WL1271_ACX_INTR_EVENT_A | WL1271_ACX_INTR_EVENT_B)) { wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_EVENT (0x%x)", intr); if (intr & WL1271_ACX_INTR_EVENT_A) wl1271_event_handle(wl, 0); else wl1271_event_handle(wl, 1); } if (intr & WL1271_ACX_INTR_INIT_COMPLETE) wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_INIT_COMPLETE"); if (intr & WL1271_ACX_INTR_HW_AVAILABLE) wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_HW_AVAILABLE"); if (intr & WL1271_ACX_INTR_DATA) { u8 tx_res_cnt = wl->fw_status->tx_results_counter - wl->tx_results_count; wl1271_debug(DEBUG_IRQ, "WL1271_ACX_INTR_DATA"); /* check for tx results */ if (tx_res_cnt) wl1271_tx_complete(wl, tx_res_cnt); wl1271_rx(wl, wl->fw_status); } intr = wl1271_reg_read32(wl, ACX_REG_INTERRUPT_CLEAR); intr &= WL1271_INTR_MASK; } while (intr && --ctr); out_sleep: wl1271_reg_write32(wl, ACX_REG_INTERRUPT_MASK, WL1271_ACX_INTR_ALL & ~(WL1271_INTR_MASK)); wl1271_ps_elp_sleep(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: 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 < wl->conf.conn.psm_entry_retries) { wl->psm_entry_retry++; ret = wl1271_ps_set_mode(wl, STATION_POWER_SAVE_MODE, 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); if (ret < 0) break; 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, false); break; case EVENT_EXIT_POWER_SAVE_SUCCESS: default: break; } return ret; }
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; }