wil6210: Tx management frame
authorVladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Thu, 27 Feb 2014 14:20:40 +0000 (16:20 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 28 Feb 2014 19:33:27 +0000 (14:33 -0500)
Implement management frame passing. In order to receive frame on the other
side, remain_on_channel() should be implemented as well

Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/ath/wil6210/cfg80211.c
drivers/net/wireless/ath/wil6210/wil6210.h
drivers/net/wireless/ath/wil6210/wmi.c

index 5b34076..204c7c8 100644 (file)
@@ -352,6 +352,40 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy,
        return rc;
 }
 
+static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy,
+                               struct wireless_dev *wdev,
+                               struct cfg80211_mgmt_tx_params *params,
+                               u64 *cookie)
+{
+       const u8 *buf = params->buf;
+       size_t len = params->len;
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       int rc;
+       struct ieee80211_mgmt *mgmt_frame = (void *)buf;
+       struct wmi_sw_tx_req_cmd *cmd;
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_sw_tx_complete_event evt;
+       } __packed evt;
+
+       cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL);
+       if (!cmd)
+               return -ENOMEM;
+
+       memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN);
+       cmd->len = cpu_to_le16(len);
+       memcpy(cmd->payload, buf, len);
+
+       rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len,
+                     WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000);
+       if (rc == 0)
+               rc = evt.evt.status;
+
+       kfree(cmd);
+
+       return rc;
+}
+
 static int wil_cfg80211_set_channel(struct wiphy *wiphy,
                                    struct cfg80211_chan_def *chandef)
 {
@@ -402,6 +436,41 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy,
        return 0;
 }
 
+static int wil_remain_on_channel(struct wiphy *wiphy,
+                                struct wireless_dev *wdev,
+                                struct ieee80211_channel *chan,
+                                unsigned int duration,
+                                u64 *cookie)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       int rc;
+
+       /* TODO: handle duration */
+       wil_info(wil, "%s(%d, %d ms)\n", __func__, chan->center_freq, duration);
+
+       rc = wmi_set_channel(wil, chan->hw_value);
+       if (rc)
+               return rc;
+
+       rc = wmi_rxon(wil, true);
+
+       return rc;
+}
+
+static int wil_cancel_remain_on_channel(struct wiphy *wiphy,
+                                       struct wireless_dev *wdev,
+                                       u64 cookie)
+{
+       struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+       int rc;
+
+       wil_info(wil, "%s()\n", __func__);
+
+       rc = wmi_rxon(wil, false);
+
+       return rc;
+}
+
 static int wil_fix_bcon(struct wil6210_priv *wil,
                        struct cfg80211_beacon_data *bcon)
 {
@@ -510,6 +579,9 @@ static struct cfg80211_ops wil_cfg80211_ops = {
        .disconnect = wil_cfg80211_disconnect,
        .change_virtual_intf = wil_cfg80211_change_iface,
        .get_station = wil_cfg80211_get_station,
+       .remain_on_channel = wil_remain_on_channel,
+       .cancel_remain_on_channel = wil_cancel_remain_on_channel,
+       .mgmt_tx = wil_cfg80211_mgmt_tx,
        .set_monitor_channel = wil_cfg80211_set_channel,
        .add_key = wil_cfg80211_add_key,
        .del_key = wil_cfg80211_del_key,
index 1f91eaf..0d7fba4 100644 (file)
@@ -357,6 +357,7 @@ int wmi_echo(struct wil6210_priv *wil);
 int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie);
 int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring);
 int wmi_p2p_cfg(struct wil6210_priv *wil, int channel);
+int wmi_rxon(struct wil6210_priv *wil, bool on);
 int wmi_get_temperature(struct wil6210_priv *wil, u32 *t_m, u32 *t_r);
 
 int wil6210_init_irq(struct wil6210_priv *wil, int irq);
index 063963e..d65da55 100644 (file)
@@ -893,6 +893,38 @@ int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie)
        return rc;
 }
 
+/**
+ * wmi_rxon - turn radio on/off
+ * @on:                turn on if true, off otherwise
+ *
+ * Only switch radio. Channel should be set separately.
+ * No timeout for rxon - radio turned on forever unless some other call
+ * turns it off
+ */
+int wmi_rxon(struct wil6210_priv *wil, bool on)
+{
+       int rc;
+       struct {
+               struct wil6210_mbox_hdr_wmi wmi;
+               struct wmi_listen_started_event evt;
+       } __packed reply;
+
+       wil_info(wil, "%s(%s)\n", __func__, on ? "on" : "off");
+
+       if (on) {
+               rc = wmi_call(wil, WMI_START_LISTEN_CMDID, NULL, 0,
+                             WMI_LISTEN_STARTED_EVENTID,
+                             &reply, sizeof(reply), 100);
+               if ((rc == 0) && (reply.evt.status != WMI_FW_STATUS_SUCCESS))
+                       rc = -EINVAL;
+       } else {
+               rc = wmi_call(wil, WMI_DISCOVERY_STOP_CMDID, NULL, 0,
+                             WMI_DISCOVERY_STOPPED_EVENTID, NULL, 0, 20);
+       }
+
+       return rc;
+}
+
 int wmi_rx_chain_add(struct wil6210_priv *wil, struct vring *vring)
 {
        struct wireless_dev *wdev = wil->wdev;