|
@@ -40,6 +40,7 @@
|
|
|
#define TWT_DISABLE_COMPLETE_TIMEOUT 4000
|
|
|
#define TWT_TERMINATE_COMPLETE_TIMEOUT 4000
|
|
|
#define TWT_PAUSE_COMPLETE_TIMEOUT 4000
|
|
|
+#define TWT_NUDGE_COMPLETE_TIMEOUT 4000
|
|
|
#define TWT_RESUME_COMPLETE_TIMEOUT 4000
|
|
|
|
|
|
#define TWT_FLOW_TYPE_ANNOUNCED 0
|
|
@@ -62,6 +63,18 @@ struct twt_pause_dialog_comp_ev_priv {
|
|
|
struct wmi_twt_pause_dialog_complete_event_param pause_dialog_comp_ev_buf;
|
|
|
};
|
|
|
|
|
|
+/**
|
|
|
+ * struct twt_nudge_dialog_comp_ev_priv - private struct for twt nudge dialog
|
|
|
+ * @nudge_dialog_comp_ev_buf: buffer from TWT nudge dialog complete_event
|
|
|
+ *
|
|
|
+ * This TWT nudge dialog private structure is registered with os_if to
|
|
|
+ * retrieve the TWT nudge dialog response event buffer.
|
|
|
+ */
|
|
|
+struct twt_nudge_dialog_comp_ev_priv {
|
|
|
+ struct wmi_twt_nudge_dialog_complete_event_param
|
|
|
+ nudge_dialog_comp_ev_buf;
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* struct twt_resume_dialog_comp_ev_priv - private struct for twt resume dialog
|
|
|
* @resume_dialog_comp_ev_buf: buffer from TWT resume dialog complete_event
|
|
@@ -126,6 +139,14 @@ wlan_hdd_wifi_twt_config_policy[
|
|
|
.type = NLA_NESTED},
|
|
|
};
|
|
|
|
|
|
+static const struct nla_policy
|
|
|
+qca_wlan_vendor_twt_nudge_dialog_policy[QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAX + 1] = {
|
|
|
+ [QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID] = {.type = NLA_U8 },
|
|
|
+ [QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME] = {.type = NLA_U32 },
|
|
|
+ [QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE] = {.type = NLA_U32 },
|
|
|
+ [QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR] = VENDOR_NLA_POLICY_MAC_ADDR,
|
|
|
+};
|
|
|
+
|
|
|
/**
|
|
|
* hdd_twt_setup_req_type_to_cmd() - Converts twt setup request type to twt cmd
|
|
|
* @req_type: twt setup request type
|
|
@@ -785,6 +806,37 @@ wmi_twt_pause_status_to_vendor_twt_status(enum WMI_HOST_PAUSE_TWT_STATUS status)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * wmi_twt_nudge_status_to_vendor_twt_status() - convert from
|
|
|
+ * WMI_HOST_NUDGE_TWT_STATUS to qca_wlan_vendor_twt_status
|
|
|
+ * @status: WMI_HOST_NUDGE_TWT_STATUS value from firmware
|
|
|
+ *
|
|
|
+ * Return: qca_wlan_vendor_twt_status values corresponding
|
|
|
+ * to the firmware failure status
|
|
|
+ */
|
|
|
+static int
|
|
|
+wmi_twt_nudge_status_to_vendor_twt_status(enum WMI_HOST_NUDGE_TWT_STATUS status)
|
|
|
+{
|
|
|
+ switch (status) {
|
|
|
+ case WMI_HOST_NUDGE_TWT_STATUS_OK:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_OK;
|
|
|
+ case WMI_HOST_NUDGE_TWT_STATUS_DIALOG_ID_NOT_EXIST:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_SESSION_NOT_EXIST;
|
|
|
+ case WMI_HOST_NUDGE_TWT_STATUS_INVALID_PARAM:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_INVALID_PARAM;
|
|
|
+ case WMI_HOST_NUDGE_TWT_STATUS_DIALOG_ID_BUSY:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_SESSION_BUSY;
|
|
|
+ case WMI_HOST_NUDGE_TWT_STATUS_NO_RESOURCE:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_NO_RESOURCE;
|
|
|
+ case WMI_HOST_NUDGE_TWT_STATUS_NO_ACK:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_NO_ACK;
|
|
|
+ case WMI_HOST_NUDGE_TWT_STATUS_UNKNOWN_ERROR:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR;
|
|
|
+ default:
|
|
|
+ return QCA_WLAN_VENDOR_TWT_STATUS_UNKNOWN_ERROR;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* wmi_twt_add_cmd_to_vendor_twt_resp_type() - convert from
|
|
|
* WMI_HOST_TWT_COMMAND to qca_wlan_vendor_twt_setup_resp_type
|
|
@@ -1468,6 +1520,43 @@ hdd_twt_pause_dialog_comp_cb(void *context,
|
|
|
hdd_exit();
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * hdd_twt_nudge_dialog_comp_cb() - callback function
|
|
|
+ * to get twt nudge command complete event
|
|
|
+ * @context: private context
|
|
|
+ * @params: Pointer to nudge dialog complete event buffer
|
|
|
+ *
|
|
|
+ * Return: None
|
|
|
+ */
|
|
|
+static void
|
|
|
+hdd_twt_nudge_dialog_comp_cb(void *context,
|
|
|
+ struct wmi_twt_nudge_dialog_complete_event_param *params)
|
|
|
+{
|
|
|
+ struct osif_request *request;
|
|
|
+ struct twt_nudge_dialog_comp_ev_priv *priv;
|
|
|
+
|
|
|
+ hdd_enter();
|
|
|
+
|
|
|
+ request = osif_request_get(context);
|
|
|
+ if (!request) {
|
|
|
+ hdd_err("Obsolete request");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ priv = osif_request_priv(request);
|
|
|
+
|
|
|
+ priv->nudge_dialog_comp_ev_buf = *params;
|
|
|
+ osif_request_complete(request);
|
|
|
+ osif_request_put(request);
|
|
|
+
|
|
|
+ hdd_debug("TWT: nudge dialog_id:%d, status:%d vdev_id %d peer mac_addr "
|
|
|
+ QDF_MAC_ADDR_FMT, params->dialog_id,
|
|
|
+ params->status, params->vdev_id,
|
|
|
+ QDF_MAC_ADDR_REF(params->peer_macaddr));
|
|
|
+
|
|
|
+ hdd_exit();
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* hdd_twt_pause_pack_resp_nlmsg() - pack the skb with
|
|
|
* firmware response for twt pause command
|
|
@@ -1499,6 +1588,38 @@ hdd_twt_pause_pack_resp_nlmsg(struct sk_buff *reply_skb,
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * hdd_twt_nudge_pack_resp_nlmsg() - pack the skb with
|
|
|
+ * firmware response for twt nudge command
|
|
|
+ * @reply_skb: skb to store the response
|
|
|
+ * @params: Pointer to nudge dialog complete event buffer
|
|
|
+ *
|
|
|
+ * Return: QDF_STATUS_SUCCESS on Success, QDF_STATUS_E_FAILURE
|
|
|
+ * on failure
|
|
|
+ */
|
|
|
+static QDF_STATUS
|
|
|
+hdd_twt_nudge_pack_resp_nlmsg(struct sk_buff *reply_skb,
|
|
|
+ struct wmi_twt_nudge_dialog_complete_event_param *params)
|
|
|
+{
|
|
|
+ int vendor_status;
|
|
|
+
|
|
|
+ if (nla_put_u8(reply_skb, QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID,
|
|
|
+ params->dialog_id)) {
|
|
|
+ hdd_debug("TWT: Failed to put dialog_id");
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ vendor_status =
|
|
|
+ wmi_twt_nudge_status_to_vendor_twt_status(params->status);
|
|
|
+ if (nla_put_u8(reply_skb, QCA_WLAN_VENDOR_ATTR_TWT_SETUP_STATUS,
|
|
|
+ vendor_status)) {
|
|
|
+ hdd_err("TWT: Failed to put QCA_WLAN_TWT_NUDGE status");
|
|
|
+ return QDF_STATUS_E_FAILURE;
|
|
|
+ }
|
|
|
+
|
|
|
+ return QDF_STATUS_SUCCESS;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* hdd_send_twt_pause_dialog_cmd() - Send TWT pause dialog command to target
|
|
|
* @hdd_ctx: HDD Context
|
|
@@ -1638,6 +1759,164 @@ static int hdd_twt_pause_session(struct hdd_adapter *adapter,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * hdd_send_twt_nudge_dialog_cmd() - Send TWT nudge dialog command to target
|
|
|
+ * @hdd_ctx: HDD Context
|
|
|
+ * @twt_params: Pointer to nudge dialog cmd params structure
|
|
|
+ *
|
|
|
+ * Return: 0 on success, negative value on failure
|
|
|
+ */
|
|
|
+static
|
|
|
+int hdd_send_twt_nudge_dialog_cmd(struct hdd_context *hdd_ctx,
|
|
|
+ struct wmi_twt_nudge_dialog_cmd_param *twt_params)
|
|
|
+{
|
|
|
+ struct wmi_twt_nudge_dialog_complete_event_param *comp_ev_params;
|
|
|
+ struct twt_nudge_dialog_comp_ev_priv *priv;
|
|
|
+ static const struct osif_request_params osif_req_params = {
|
|
|
+ .priv_size = sizeof(*priv),
|
|
|
+ .timeout_ms = TWT_NUDGE_COMPLETE_TIMEOUT,
|
|
|
+ .dealloc = NULL,
|
|
|
+ };
|
|
|
+ struct osif_request *request;
|
|
|
+ struct sk_buff *reply_skb = NULL;
|
|
|
+ QDF_STATUS status;
|
|
|
+ void *cookie;
|
|
|
+ int skb_len;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ request = osif_request_alloc(&osif_req_params);
|
|
|
+ if (!request) {
|
|
|
+ hdd_err("twt osif request allocation failure");
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ cookie = osif_request_cookie(request);
|
|
|
+
|
|
|
+ status = sme_nudge_dialog_cmd(hdd_ctx->mac_handle,
|
|
|
+ hdd_twt_nudge_dialog_comp_cb,
|
|
|
+ twt_params, cookie);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
+ hdd_err("Failed to send nudge dialog command");
|
|
|
+ ret = qdf_status_to_os_return(status);
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = osif_request_wait_for_response(request);
|
|
|
+ if (ret) {
|
|
|
+ hdd_err("twt: nudge dialog req timedout");
|
|
|
+ ret = -ETIMEDOUT;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ priv = osif_request_priv(request);
|
|
|
+ comp_ev_params = &priv->nudge_dialog_comp_ev_buf;
|
|
|
+
|
|
|
+ skb_len = hdd_get_twt_event_len();
|
|
|
+ reply_skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
|
|
|
+ skb_len);
|
|
|
+ if (!reply_skb) {
|
|
|
+ hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ status = hdd_twt_nudge_pack_resp_nlmsg(reply_skb, comp_ev_params);
|
|
|
+ if (QDF_IS_STATUS_ERROR(status)) {
|
|
|
+ hdd_err("Failed to pack nl nudge dialog response");
|
|
|
+ ret = qdf_status_to_os_return(status);
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = wlan_cfg80211_vendor_cmd_reply(reply_skb);
|
|
|
+
|
|
|
+err:
|
|
|
+ if (request)
|
|
|
+ osif_request_put(request);
|
|
|
+ if (ret && reply_skb)
|
|
|
+ kfree_skb(reply_skb);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * hdd_twt_nudge_session - Process TWT nudge operation
|
|
|
+ * in the received vendor command and send it to firmware
|
|
|
+ * @adapter: adapter pointer
|
|
|
+ * @twt_param_attr: nl attributes
|
|
|
+ *
|
|
|
+ * Handles QCA_WLAN_TWT_NUDGE
|
|
|
+ *
|
|
|
+ * Return: 0 on success, negative value on failure
|
|
|
+ */
|
|
|
+static int hdd_twt_nudge_session(struct hdd_adapter *adapter,
|
|
|
+ struct nlattr *twt_param_attr)
|
|
|
+{
|
|
|
+ struct hdd_station_ctx *hdd_sta_ctx;
|
|
|
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAX + 1];
|
|
|
+ struct wmi_twt_nudge_dialog_cmd_param params = {0};
|
|
|
+ int id;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (adapter->device_mode != QDF_STA_MODE &&
|
|
|
+ adapter->device_mode != QDF_P2P_CLIENT_MODE) {
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+
|
|
|
+ hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
|
|
|
+ if (hdd_sta_ctx->conn_info.conn_state != eConnectionState_Associated) {
|
|
|
+ hdd_err_rl("Invalid state, vdev %d mode %d state %d",
|
|
|
+ adapter->vdev_id, adapter->device_mode,
|
|
|
+ hdd_sta_ctx->conn_info.conn_state);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ params.vdev_id = adapter->vdev_id;
|
|
|
+
|
|
|
+ ret = wlan_cfg80211_nla_parse_nested(tb,
|
|
|
+ QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAX,
|
|
|
+ twt_param_attr,
|
|
|
+ qca_wlan_vendor_twt_nudge_dialog_policy);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ id = QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR;
|
|
|
+ if (tb[id]) {
|
|
|
+ nla_memcpy(params.peer_macaddr, tb[id], QDF_MAC_ADDR_SIZE);
|
|
|
+ hdd_debug("peer mac_addr "QDF_MAC_ADDR_FMT,
|
|
|
+ QDF_MAC_ADDR_REF(params.peer_macaddr));
|
|
|
+ }
|
|
|
+
|
|
|
+ id = QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_FLOW_ID;
|
|
|
+ if (!tb[id]) {
|
|
|
+ hdd_debug("TWT: FLOW_ID not specified.");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ params.dialog_id = nla_get_u8(tb[id]);
|
|
|
+
|
|
|
+ id = QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME;
|
|
|
+ if (!tb[id]) {
|
|
|
+ hdd_debug("TWT: WAKE_TIME not specified.");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ params.suspend_duration = nla_get_u32(tb[id]);
|
|
|
+
|
|
|
+ id = QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE;
|
|
|
+ if (!tb[id]) {
|
|
|
+ hdd_debug("TWT: NEXT_TWT_SIZE not specified.");
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+ params.next_twt_size = nla_get_u32(tb[id]);
|
|
|
+
|
|
|
+ hdd_debug("twt_nudge: vdev_id %d dialog_id %d ", params.vdev_id,
|
|
|
+ params.dialog_id);
|
|
|
+ hdd_debug("twt_nudge: suspend_duration %d next_twt_size %d",
|
|
|
+ params.suspend_duration, params.next_twt_size);
|
|
|
+
|
|
|
+ ret = hdd_send_twt_nudge_dialog_cmd(adapter->hdd_ctx, ¶ms);
|
|
|
+
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* hdd_twt_resume_dialog_comp_cb() - callback function
|
|
|
* to get twt resume command complete event
|
|
@@ -1918,6 +2197,9 @@ static int hdd_twt_configure(struct hdd_adapter *adapter,
|
|
|
case QCA_WLAN_TWT_RESUME:
|
|
|
ret = hdd_twt_resume_session(adapter, twt_param_attr);
|
|
|
break;
|
|
|
+ case QCA_WLAN_TWT_NUDGE:
|
|
|
+ ret = hdd_twt_nudge_session(adapter, twt_param_attr);
|
|
|
+ break;
|
|
|
default:
|
|
|
hdd_err("Invalid TWT Operation");
|
|
|
ret = -EINVAL;
|