diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/wmi.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.c | 233 |
1 files changed, 204 insertions, 29 deletions
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 493e721c4fa7..b80c5d850e1e 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -32,6 +32,11 @@ module_param(agg_wsize, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(agg_wsize, " Window size for Tx Block Ack after connect;" " 0 - use default; < 0 - don't auto-establish"); +u8 led_id = WIL_LED_INVALID_ID; +module_param(led_id, byte, S_IRUGO); +MODULE_PARM_DESC(led_id, + " 60G device led enablement. Set the led ID (0-2) to enable"); + /** * WMI event receiving - theory of operations * @@ -94,6 +99,14 @@ const struct fw_map fw_mapping[] = { */ }; +struct blink_on_off_time led_blink_time[] = { + {WIL_LED_BLINK_ON_SLOW_MS, WIL_LED_BLINK_OFF_SLOW_MS}, + {WIL_LED_BLINK_ON_MED_MS, WIL_LED_BLINK_OFF_MED_MS}, + {WIL_LED_BLINK_ON_FAST_MS, WIL_LED_BLINK_OFF_FAST_MS}, +}; + +u8 led_polarity = LED_POLARITY_LOW_ACTIVE; + /** * return AHB address for given firmware/ucode internal (linker) address * @x - internal address @@ -176,7 +189,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) { struct { struct wil6210_mbox_hdr hdr; - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; } __packed cmd = { .hdr = { .type = WIL_MBOX_HDR_TYPE_WMI, @@ -185,7 +198,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) }, .wmi = { .mid = 0, - .id = cpu_to_le16(cmdid), + .command_id = cpu_to_le16(cmdid), }, }; struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; @@ -194,6 +207,7 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) void __iomem *dst; void __iomem *head = wmi_addr(wil, r->head); uint retry; + int rc = 0; if (sizeof(cmd) + len > r->entry_size) { wil_err(wil, "WMI size too large: %d bytes, max is %d\n", @@ -212,6 +226,9 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); return -EINVAL; } + + wil_halp_vote(wil); + /* read Tx head till it is not busy */ for (retry = 5; retry > 0; retry--) { wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); @@ -221,7 +238,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) } if (d_head.sync != 0) { wil_err(wil, "WMI head busy\n"); - return -EBUSY; + rc = -EBUSY; + goto out; } /* next head */ next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); @@ -230,7 +248,8 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) for (retry = 5; retry > 0; retry--) { if (!test_bit(wil_status_fwready, wil->status)) { wil_err(wil, "WMI: cannot send command while FW not ready\n"); - return -EAGAIN; + rc = -EAGAIN; + goto out; } r->tail = wil_r(wil, RGF_MBOX + offsetof(struct wil6210_mbox_ctl, tx.tail)); @@ -240,13 +259,15 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) } if (next_head == r->tail) { wil_err(wil, "WMI ring full\n"); - return -EBUSY; + rc = -EBUSY; + goto out; } dst = wmi_buffer(wil, d_head.addr); if (!dst) { wil_err(wil, "invalid WMI buffer: 0x%08x\n", le32_to_cpu(d_head.addr)); - return -EINVAL; + rc = -EAGAIN; + goto out; } cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); /* set command */ @@ -269,7 +290,9 @@ static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) wil_w(wil, RGF_USER_USER_ICR + offsetof(struct RGF_ICR, ICS), SW_INT_MBOX); - return 0; +out: + wil_halp_unvote(wil); + return rc; } int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) @@ -333,7 +356,7 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) } ch_no = data->info.channel + 1; - freq = ieee80211_channel_to_frequency(ch_no, IEEE80211_BAND_60GHZ); + freq = ieee80211_channel_to_frequency(ch_no, NL80211_BAND_60GHZ); channel = ieee80211_get_channel(wiphy, freq); signal = data->info.sqi; d_status = le16_to_cpu(data->info.status); @@ -368,6 +391,8 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) wil_hex_dump_wmi("IE ", DUMP_PREFIX_OFFSET, 16, 1, ie_buf, ie_len, true); + wil_dbg_wmi(wil, "Capability info : 0x%04x\n", cap); + bss = cfg80211_inform_bss_frame(wiphy, channel, rx_mgmt_frame, d_len, signal, GFP_KERNEL); if (bss) { @@ -378,8 +403,10 @@ static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) wil_err(wil, "cfg80211_inform_bss_frame() failed\n"); } } else { - cfg80211_rx_mgmt(wil->wdev, freq, signal, + mutex_lock(&wil->p2p_wdev_mutex); + cfg80211_rx_mgmt(wil->radio_wdev, freq, signal, (void *)rx_mgmt_frame, d_len, 0); + mutex_unlock(&wil->p2p_wdev_mutex); } } @@ -406,7 +433,10 @@ static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, wil->scan_request, aborted); del_timer_sync(&wil->scan_timer); + mutex_lock(&wil->p2p_wdev_mutex); cfg80211_scan_done(wil->scan_request, aborted); + wil->radio_wdev = wil->wdev; + mutex_unlock(&wil->p2p_wdev_mutex); wil->scan_request = NULL; } else { wil_err(wil, "SCAN_COMPLETE while not scanning\n"); @@ -487,6 +517,14 @@ static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) return; } del_timer_sync(&wil->connect_timer); + } else if ((wdev->iftype == NL80211_IFTYPE_AP) || + (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { + if (wil->sta[evt->cid].status != wil_sta_unused) { + wil_err(wil, "%s: AP: Invalid status %d for CID %d\n", + __func__, wil->sta[evt->cid].status, evt->cid); + mutex_unlock(&wil->mutex); + return; + } } /* FIXME FW can transmit only ucast frames to peer */ @@ -648,7 +686,7 @@ static void wmi_evt_vring_en(struct wil6210_priv *wil, int id, void *d, int len) static void wmi_evt_ba_status(struct wil6210_priv *wil, int id, void *d, int len) { - struct wmi_vring_ba_status_event *evt = d; + struct wmi_ba_status_event *evt = d; struct vring_tx_data *txdata; wil_dbg_wmi(wil, "BACK[%d] %s {%d} timeout %d AMSDU%s\n", @@ -834,10 +872,10 @@ void wmi_recv_cmd(struct wil6210_priv *wil) offsetof(struct wil6210_mbox_ring_desc, sync), 0); /* indicate */ if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && - (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { - struct wil6210_mbox_hdr_wmi *wmi = &evt->event.wmi; - u16 id = le16_to_cpu(wmi->id); - u32 tstamp = le32_to_cpu(wmi->timestamp); + (len >= sizeof(struct wmi_cmd_hdr))) { + struct wmi_cmd_hdr *wmi = &evt->event.wmi; + u16 id = le16_to_cpu(wmi->command_id); + u32 tstamp = le32_to_cpu(wmi->fw_timestamp); spin_lock_irqsave(&wil->wmi_ev_lock, flags); if (wil->reply_id && wil->reply_id == id) { if (wil->reply_buf) { @@ -946,8 +984,62 @@ int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); } +int wmi_led_cfg(struct wil6210_priv *wil, bool enable) +{ + int rc = 0; + struct wmi_led_cfg_cmd cmd = { + .led_mode = enable, + .id = led_id, + .slow_blink_cfg.blink_on = + cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].on_ms), + .slow_blink_cfg.blink_off = + cpu_to_le32(led_blink_time[WIL_LED_TIME_SLOW].off_ms), + .medium_blink_cfg.blink_on = + cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].on_ms), + .medium_blink_cfg.blink_off = + cpu_to_le32(led_blink_time[WIL_LED_TIME_MED].off_ms), + .fast_blink_cfg.blink_on = + cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].on_ms), + .fast_blink_cfg.blink_off = + cpu_to_le32(led_blink_time[WIL_LED_TIME_FAST].off_ms), + .led_polarity = led_polarity, + }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_led_cfg_done_event evt; + } __packed reply; + + if (led_id == WIL_LED_INVALID_ID) + goto out; + + if (led_id > WIL_LED_MAX_ID) { + wil_err(wil, "Invalid led id %d\n", led_id); + rc = -EINVAL; + goto out; + } + + wil_dbg_wmi(wil, + "%s led %d\n", + enable ? "enabling" : "disabling", led_id); + + rc = wmi_call(wil, WMI_LED_CFG_CMDID, &cmd, sizeof(cmd), + WMI_LED_CFG_DONE_EVENTID, &reply, sizeof(reply), + 100); + if (rc) + goto out; + + if (reply.evt.status) { + wil_err(wil, "led %d cfg failed with status %d\n", + led_id, le32_to_cpu(reply.evt.status)); + rc = -EINVAL; + } + +out: + return rc; +} + int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, - u8 chan, u8 hidden_ssid) + u8 chan, u8 hidden_ssid, u8 is_go) { int rc; @@ -958,9 +1050,10 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, .channel = chan - 1, .pcp_max_assoc_sta = max_assoc_sta, .hidden_ssid = hidden_ssid, + .is_go = is_go, }; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_pcp_started_event evt; } __packed reply; @@ -987,11 +1080,21 @@ int wmi_pcp_start(struct wil6210_priv *wil, int bi, u8 wmi_nettype, if (reply.evt.status != WMI_FW_STATUS_SUCCESS) rc = -EINVAL; + if (wmi_nettype != WMI_NETTYPE_P2P) + /* Don't fail due to error in the led configuration */ + wmi_led_cfg(wil, true); + return rc; } int wmi_pcp_stop(struct wil6210_priv *wil) { + int rc; + + rc = wmi_led_cfg(wil, false); + if (rc) + return rc; + return wmi_call(wil, WMI_PCP_STOP_CMDID, NULL, 0, WMI_PCP_STOPPED_EVENTID, NULL, 0, 20); } @@ -1014,7 +1117,7 @@ int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) { int rc; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_set_ssid_cmd cmd; } __packed reply; int len; /* reply.cmd.ssid_len in CPU order */ @@ -1047,7 +1150,7 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel) { int rc; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_set_pcp_channel_cmd cmd; } __packed reply; @@ -1064,14 +1167,86 @@ int wmi_get_channel(struct wil6210_priv *wil, int *channel) return 0; } -int wmi_p2p_cfg(struct wil6210_priv *wil, int channel) +int wmi_p2p_cfg(struct wil6210_priv *wil, int channel, int bi) { + int rc; struct wmi_p2p_cfg_cmd cmd = { - .discovery_mode = WMI_DISCOVERY_MODE_NON_OFFLOAD, + .discovery_mode = WMI_DISCOVERY_MODE_PEER2PEER, + .bcon_interval = cpu_to_le16(bi), .channel = channel - 1, }; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_p2p_cfg_done_event evt; + } __packed reply; + + wil_dbg_wmi(wil, "sending WMI_P2P_CFG_CMDID\n"); + + rc = wmi_call(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd), + WMI_P2P_CFG_DONE_EVENTID, &reply, sizeof(reply), 300); + if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "P2P_CFG failed. status %d\n", reply.evt.status); + rc = -EINVAL; + } + + return rc; +} + +int wmi_start_listen(struct wil6210_priv *wil) +{ + int rc; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_listen_started_event evt; + } __packed reply; + + wil_dbg_wmi(wil, "sending WMI_START_LISTEN_CMDID\n"); + + rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0, + WMI_LISTEN_STARTED_EVENTID, &reply, sizeof(reply), 300); + if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "device failed to start listen. status %d\n", + reply.evt.status); + rc = -EINVAL; + } + + return rc; +} + +int wmi_start_search(struct wil6210_priv *wil) +{ + int rc; + struct { + struct wmi_cmd_hdr wmi; + struct wmi_search_started_event evt; + } __packed reply; + + wil_dbg_wmi(wil, "sending WMI_START_SEARCH_CMDID\n"); + + rc = wmi_call(wil, WMI_START_SEARCH_CMDID, NULL, 0, + WMI_SEARCH_STARTED_EVENTID, &reply, sizeof(reply), 300); + if (!rc && reply.evt.status != WMI_FW_STATUS_SUCCESS) { + wil_err(wil, "device failed to start search. status %d\n", + reply.evt.status); + rc = -EINVAL; + } + + return rc; +} + +int wmi_stop_discovery(struct wil6210_priv *wil) +{ + int rc; + + wil_dbg_wmi(wil, "sending WMI_DISCOVERY_STOP_CMDID\n"); + + rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0, + WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 100); - return wmi_send(wil, WMI_P2P_CFG_CMDID, &cmd, sizeof(cmd)); + if (rc) + wil_err(wil, "Failed to stop discovery\n"); + + return rc; } int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, @@ -1155,7 +1330,7 @@ int wmi_rxon(struct wil6210_priv *wil, bool on) { int rc; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_listen_started_event evt; } __packed reply; @@ -1192,7 +1367,7 @@ int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring) .host_thrsh = cpu_to_le16(rx_ring_overflow_thrsh), }; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_cfg_rx_chain_done_event evt; } __packed evt; int rc; @@ -1246,7 +1421,7 @@ int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_bb, u32 *t_rf) .measure_mode = cpu_to_le32(TEMPERATURE_MEASURE_NOW), }; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_temp_sense_done_event evt; } __packed reply; @@ -1272,7 +1447,7 @@ int wmi_disconnect_sta(struct wil6210_priv *wil, const u8 *mac, u16 reason, .disconnect_reason = cpu_to_le16(reason), }; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_disconnect_event evt; } __packed reply; @@ -1364,7 +1539,7 @@ int wmi_addba_rx_resp(struct wil6210_priv *wil, u8 cid, u8 tid, u8 token, .ba_timeout = cpu_to_le16(timeout), }; struct { - struct wil6210_mbox_hdr_wmi wmi; + struct wmi_cmd_hdr wmi; struct wmi_rcp_addba_resp_sent_event evt; } __packed reply; @@ -1420,10 +1595,10 @@ static void wmi_event_handle(struct wil6210_priv *wil, u16 len = le16_to_cpu(hdr->len); if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && - (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { - struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); + (len >= sizeof(struct wmi_cmd_hdr))) { + struct wmi_cmd_hdr *wmi = (void *)(&hdr[1]); void *evt_data = (void *)(&wmi[1]); - u16 id = le16_to_cpu(wmi->id); + u16 id = le16_to_cpu(wmi->command_id); wil_dbg_wmi(wil, "Handle WMI 0x%04x (reply_id 0x%04x)\n", id, wil->reply_id); |