// SPDX-License-Identifier: GPL-2.0-only /* * This file is part of wl1271 * * Copyright (C) 2009-2010 Nokia Corporation * * Contact: Luciano Coelho <luciano.coelho@nokia.com>
*/
ret = wlcore_write(wl, wl->cmd_box_addr, buf, len, false); if (ret < 0) return ret;
/* * TODO: we just need this because one bit is in a different * place. Is there any better way?
*/
ret = wl->ops->trigger_cmd(wl, wl->cmd_box_addr, buf, len); if (ret < 0) return ret;
/* * wrapper for wlcore_cmd_send that accept only CMD_STATUS_SUCCESS * return 0 on success.
*/ int wl1271_cmd_send(struct wl1271 *wl, u16 id, void *buf, size_t len,
size_t res_len)
{ int ret = wlcore_cmd_send_failsafe(wl, id, buf, len, res_len, 0);
if (ret < 0) return ret; return 0;
}
EXPORT_SYMBOL_GPL(wl1271_cmd_send);
/* * Poll the mailbox event field until any of the bits in the mask is set or a * timeout occurs (WL1271_EVENT_TIMEOUT in msecs)
*/ int wlcore_cmd_wait_for_event_or_timeout(struct wl1271 *wl,
u32 mask, bool *timeout)
{
u32 *events_vector;
u32 event; unsignedlong timeout_time;
u16 poll_count = 0; int ret = 0;
*timeout = false;
events_vector = kmalloc(sizeof(*events_vector), GFP_KERNEL | GFP_DMA); if (!events_vector) return -ENOMEM;
int wl12xx_cmd_role_enable(struct wl1271 *wl, u8 *addr, u8 role_type,
u8 *role_id)
{ struct wl12xx_cmd_role_enable *cmd; int ret;
wl1271_debug(DEBUG_CMD, "cmd role enable");
if (WARN_ON(*role_id != WL12XX_INVALID_ROLE_ID)) return -EBUSY;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) {
ret = -ENOMEM; goto out;
}
/* get role id */
cmd->role_id = find_first_zero_bit(wl->roles_map, WL12XX_MAX_ROLES); if (cmd->role_id >= WL12XX_MAX_ROLES) {
ret = -EBUSY; goto out_free;
}
/* these bits are used by op_tx */
spin_lock_irqsave(&wl->wl_lock, flags);
__set_bit(link, wl->links_map);
__set_bit(link, wlvif->links_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);
/* * take the last "freed packets" value from the current FW status. * on recovery, we might not have fw_status yet, and * tx_lnk_free_pkts will be NULL. check for it.
*/ if (wl->fw_status->counters.tx_lnk_free_pkts)
wl->links[link].prev_freed_pkts =
wl->fw_status->counters.tx_lnk_free_pkts[link];
wl->links[link].wlvif = wlvif;
/* * Take the last sec_pn16 value from the current FW status. On recovery, * we might not have fw_status yet, and tx_lnk_sec_pn16[] will be NULL.
*/ if (wl->fw_status->counters.tx_lnk_sec_pn16)
wl->links[link].prev_sec_pn16 =
le16_to_cpu(wl->fw_status->counters.tx_lnk_sec_pn16[link]);
/* * Take saved value for total freed packets from wlvif, in case this is * recovery/resume
*/ if (wlvif->bss_type != BSS_TYPE_AP_BSS)
wl->links[link].total_freed_pkts = wlvif->total_freed_pkts;
/* these bits are used by op_tx */
spin_lock_irqsave(&wl->wl_lock, flags);
__clear_bit(*hlid, wl->links_map);
__clear_bit(*hlid, wlvif->links_map);
spin_unlock_irqrestore(&wl->wl_lock, flags);
/* * At this point op_tx() will not add more packets to the queues. We * can purge them.
*/
wl1271_tx_reset_link_queues(wl, *hlid);
wl->links[*hlid].wlvif = NULL;
if (wlvif->bss_type == BSS_TYPE_AP_BSS &&
*hlid == wlvif->ap.bcast_hlid) {
u32 sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING; /* * save the total freed packets in the wlvif, in case this is * recovery or suspend
*/
wlvif->total_freed_pkts = wl->links[*hlid].total_freed_pkts;
/* * increment the initial seq number on recovery to account for * transmitted packets that we haven't yet got in the FW status
*/ if (wlvif->encryption_type == KEY_GEM)
sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM;
if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags))
wlvif->total_freed_pkts += sqn_padding;
}
if (wlvif->sta.hlid == WL12XX_INVALID_LINK_ID) {
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->sta.hlid); if (ret) goto out_free;
}
cmd->sta.hlid = wlvif->sta.hlid;
cmd->sta.session = wl->session_ids[wlvif->sta.hlid]; /* * We don't have the correct remote rates in this stage. The * rates will be reconfigured later, after association, if the * firmware supports ACX_PEER_CAP. Otherwise, there's nothing * we can do, so use all supported_rates here.
*/
cmd->sta.remote_rates = cpu_to_le32(supported_rates);
err_hlid: /* clear links on error. */
wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
out_free:
kfree(cmd);
out: return ret;
}
/* use this function to stop ibss as well */ int wl12xx_cmd_role_stop_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{ struct wl12xx_cmd_role_stop *cmd; int ret;
if (WARN_ON(wlvif->sta.hlid == WL12XX_INVALID_LINK_ID)) return -EINVAL;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) {
ret = -ENOMEM; goto out;
}
wl1271_debug(DEBUG_CMD, "cmd role stop sta %d", wlvif->role_id);
wl1271_debug(DEBUG_CMD, "cmd role start ap %d", wlvif->role_id);
/* If MESH --> ssid_len is always 0 */ if (!ieee80211_vif_is_mesh(vif)) { /* trying to use hidden SSID with an old hostapd version */ if (wlvif->ssid_len == 0 && !bss_conf->hidden_ssid) {
wl1271_error("got a null SSID from beacon/bss");
ret = -EINVAL; goto out;
}
}
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) {
ret = -ENOMEM; goto out;
}
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.global_hlid); if (ret < 0) goto out_free;
ret = wl12xx_allocate_link(wl, wlvif, &wlvif->ap.bcast_hlid); if (ret < 0) goto out_free_global;
/* use the previous security seq, if this is a recovery/resume */
wl->links[wlvif->ap.bcast_hlid].total_freed_pkts =
wlvif->total_freed_pkts;
ret = wl1271_cmd_send(wl, CMD_ROLE_START, cmd, sizeof(*cmd), 0); if (ret < 0) {
wl1271_error("failed to initiate cmd role enable"); goto err_hlid;
}
goto out_free;
err_hlid: /* clear links on error. */
wl12xx_free_link(wl, wlvif, &wlvif->sta.hlid);
out_free:
kfree(cmd);
out: return ret;
}
/** * wl1271_cmd_test - send test command to firmware * * @wl: wl struct * @buf: buffer containing the command, with all headers, must work with dma * @buf_len: length of the buffer * @answer: is answer needed
*/ int wl1271_cmd_test(struct wl1271 *wl, void *buf, size_t buf_len, u8 answer)
{ int ret;
size_t res_len = 0;
wl1271_debug(DEBUG_CMD, "cmd test");
if (answer)
res_len = buf_len;
ret = wl1271_cmd_send(wl, CMD_TEST, buf, buf_len, res_len);
/** * wl1271_cmd_interrogate - read acx from firmware * * @wl: wl struct * @id: acx id * @buf: buffer for the response, including all headers, must work with dma * @cmd_len: length of command * @res_len: length of payload
*/ int wl1271_cmd_interrogate(struct wl1271 *wl, u16 id, void *buf,
size_t cmd_len, size_t res_len)
{ struct acx_header *acx = buf; int ret;
wl1271_debug(DEBUG_CMD, "cmd interrogate");
acx->id = cpu_to_le16(id);
/* response payload length, does not include any headers */
acx->len = cpu_to_le16(res_len - sizeof(*acx));
ret = wl1271_cmd_send(wl, CMD_INTERROGATE, acx, cmd_len, res_len); if (ret < 0)
wl1271_error("INTERROGATE command failed");
return ret;
}
/** * wlcore_cmd_configure_failsafe - write acx value to firmware * * @wl: wl struct * @id: acx id * @buf: buffer containing acx, including all headers, must work with dma * @len: length of buf * @valid_rets: bitmap of valid cmd status codes (i.e. return values). * return the cmd status on success.
*/ int wlcore_cmd_configure_failsafe(struct wl1271 *wl, u16 id, void *buf,
size_t len, unsignedlong valid_rets)
{ struct acx_header *acx = buf; int ret;
if (WARN_ON_ONCE(len < sizeof(*acx))) return -EIO;
acx->id = cpu_to_le16(id);
/* payload length, does not include any headers */
acx->len = cpu_to_le16(len - sizeof(*acx));
ret = wlcore_cmd_send_failsafe(wl, CMD_CONFIGURE, acx, len, 0,
valid_rets); if (ret < 0) {
wl1271_warning("CONFIGURE command NOK"); return ret;
}
return ret;
}
/* * wrapper for wlcore_cmd_configure that accepts only success status. * return 0 on success
*/ int wl1271_cmd_configure(struct wl1271 *wl, u16 id, void *buf, size_t len)
{ int ret = wlcore_cmd_configure_failsafe(wl, id, buf, len, 0);
if (ret < 0) return ret; return 0;
}
EXPORT_SYMBOL_GPL(wl1271_cmd_configure);
int wl1271_cmd_data_path(struct wl1271 *wl, bool enable)
{ struct cmd_enabledisable_path *cmd; int ret;
u16 cmd_rx, cmd_tx;
wl1271_debug(DEBUG_CMD, "cmd data path");
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) {
ret = -ENOMEM; goto out;
}
/* the channel here is only used for calibration, so hardcoded to 1 */
cmd->channel = 1;
/* encryption space */ switch (wlvif->encryption_type) { case KEY_TKIP: if (wl->quirks & WLCORE_QUIRK_TKIP_HEADER_SPACE)
extra = WL1271_EXTRA_SPACE_TKIP; break; case KEY_AES:
extra = WL1271_EXTRA_SPACE_AES; break; case KEY_NONE: case KEY_WEP: case KEY_GEM:
extra = 0; break; default:
wl1271_warning("Unknown encryption type: %d",
wlvif->encryption_type);
ret = -EINVAL; goto out;
}
if (key_type == KEY_TKIP) { /* * We get the key in the following form: * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) * but the target is expecting: * TKIP - RX MIC - TX MIC
*/
memcpy(cmd->key, key, 16);
memcpy(cmd->key + 16, key + 24, 8);
memcpy(cmd->key + 24, key + 16, 8);
if (key_type == KEY_TKIP) { /* * We get the key in the following form: * TKIP (16 bytes) - TX MIC (8 bytes) - RX MIC (8 bytes) * but the target is expecting: * TKIP - RX MIC - TX MIC
*/
memcpy(cmd->key, key, 16);
memcpy(cmd->key + 16, key + 24, 8);
memcpy(cmd->key + 24, key + 16, 8);
} else {
memcpy(cmd->key, key, key_size);
}
wl1271_dump(DEBUG_CRYPT, "TARGET AP KEY: ", cmd, sizeof(*cmd));
ret = wl1271_cmd_send(wl, CMD_SET_KEYS, cmd, sizeof(*cmd), 0); if (ret < 0) {
wl1271_warning("could not set ap keys"); goto out;
}
out:
kfree(cmd); return ret;
}
int wl12xx_cmd_set_peer_state(struct wl1271 *wl, struct wl12xx_vif *wlvif,
u8 hlid)
{ struct wl12xx_cmd_set_peer_state *cmd; int ret = 0;
wl1271_debug(DEBUG_CMD, "cmd set peer state (hlid=%d)", hlid);
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) {
ret = -ENOMEM; goto out;
}
/* wmm param is valid only for station role */ if (wlvif->bss_type == BSS_TYPE_STA_BSS)
cmd->wmm = wlvif->wmm_enabled;
ret = wl1271_cmd_send(wl, CMD_SET_PEER_STATE, cmd, sizeof(*cmd), 0); if (ret < 0) {
wl1271_error("failed to send set peer state command"); goto out_free;
}
out_free:
kfree(cmd);
out: return ret;
}
int wl12xx_cmd_add_peer(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_sta *sta, u8 hlid)
{ struct wl12xx_cmd_add_peer *cmd; int i, ret;
u32 sta_rates;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) {
ret = -ENOMEM; goto out;
}
cmd->hlid = hlid; /* We never send a deauth, mac80211 is in charge of this */
cmd->reason_opcode = 0;
cmd->send_deauth_flag = 0;
cmd->role_id = wlvif->role_id;
ret = wl1271_cmd_send(wl, CMD_REMOVE_PEER, cmd, sizeof(*cmd), 0); if (ret < 0) {
wl1271_error("failed to initiate cmd remove peer"); goto out_free;
}
ret = wl->ops->wait_for_event(wl,
WLCORE_EVENT_PEER_REMOVE_COMPLETE,
&timeout);
/* * We are ok with a timeout here. The event is sometimes not sent * due to a firmware bug. In case of another error (like SDIO timeout) * queue a recovery.
*/ if (ret)
wl12xx_queue_recovery_work(wl);
out_free:
kfree(cmd);
out: return ret;
}
staticint wlcore_get_reg_conf_ch_idx(enum nl80211_band band, u16 ch)
{ /* * map the given band/channel to the respective predefined * bit expected by the fw
*/ switch (band) { case NL80211_BAND_2GHZ: /* channels 1..14 are mapped to 0..13 */ if (ch >= 1 && ch <= 14) return ch - 1; break; case NL80211_BAND_5GHZ: switch (ch) { case 8 ... 16: /* channels 8,12,16 are mapped to 18,19,20 */ return 18 + (ch-8)/4; case 34 ... 48: /* channels 34,36..48 are mapped to 21..28 */ return 21 + (ch-34)/2; case 52 ... 64: /* channels 52,56..64 are mapped to 29..32 */ return 29 + (ch-52)/4; case 100 ... 140: /* channels 100,104..140 are mapped to 33..43 */ return 33 + (ch-100)/4; case 149 ... 165: /* channels 149,153..165 are mapped to 44..48 */ return 44 + (ch-149)/4; default: break;
} break; default: break;
}
int wl12xx_croc(struct wl1271 *wl, u8 role_id)
{ int ret = 0;
if (WARN_ON(!test_bit(role_id, wl->roc_map))) return 0;
ret = wl12xx_cmd_croc(wl, role_id); if (ret < 0) goto out;
__clear_bit(role_id, wl->roc_map);
/* * Rearm the tx watchdog when removing the last ROC. This prevents * recoveries due to just finished ROCs - when Tx hasn't yet had * a chance to get out.
*/ if (find_first_bit(wl->roc_map, WL12XX_MAX_ROLES) >= WL12XX_MAX_ROLES)
wl12xx_rearm_tx_watchdog_locked(wl);
out: return ret;
}
int wl12xx_cmd_stop_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif)
{ struct wl12xx_cmd_stop_channel_switch *cmd; int ret;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) {
ret = -ENOMEM; goto out;
}
cmd->role_id = wlvif->role_id;
ret = wl1271_cmd_send(wl, CMD_STOP_CHANNEL_SWICTH, cmd, sizeof(*cmd), 0); if (ret < 0) {
wl1271_error("failed to stop channel switch command"); goto out_free;
}
out_free:
kfree(cmd);
out: return ret;
}
/* start dev role and roc on its channel */ int wl12xx_start_dev(struct wl1271 *wl, struct wl12xx_vif *wlvif, enum nl80211_band band, int channel)
{ int ret;
if (WARN_ON(!(wlvif->bss_type == BSS_TYPE_STA_BSS ||
wlvif->bss_type == BSS_TYPE_IBSS))) return -EINVAL;
/* the dev role is already started for p2p mgmt interfaces */ if (!wlcore_is_p2p_mgmt(wlvif)) {
ret = wl12xx_cmd_role_enable(wl,
wl12xx_wlvif_to_vif(wlvif)->addr,
WL1271_ROLE_DEVICE,
&wlvif->dev_role_id); if (ret < 0) goto out;
}
ret = wl12xx_cmd_role_start_dev(wl, wlvif, band, channel); if (ret < 0) goto out_disable;
ret = wl12xx_roc(wl, wlvif, wlvif->dev_role_id, band, channel); if (ret < 0) goto out_stop;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.