static bool ath10k_htc_pktlog_svc_supported(struct ath10k *ar) { u8 ul_pipe_id; u8 dl_pipe_id; int status; status = ath10k_hif_map_service_to_pipe(ar, ATH10K_HTC_SVC_ID_HTT_LOG_MSG, &ul_pipe_id, &dl_pipe_id); if (status) { ath10k_warn(ar, "unsupported HTC service id: %d\n", ATH10K_HTC_SVC_ID_HTT_LOG_MSG); return false; } return true; }
int ath10k_htc_connect_service(struct ath10k_htc *htc, struct ath10k_htc_svc_conn_req *conn_req, struct ath10k_htc_svc_conn_resp *conn_resp) { struct ath10k *ar = htc->ar; struct ath10k_htc_msg *msg; struct ath10k_htc_conn_svc *req_msg; struct ath10k_htc_conn_svc_response resp_msg_dummy; struct ath10k_htc_conn_svc_response *resp_msg = &resp_msg_dummy; enum ath10k_htc_ep_id assigned_eid = ATH10K_HTC_EP_COUNT; struct ath10k_htc_ep *ep; struct sk_buff *skb; unsigned int max_msg_size = 0; int length, status; unsigned long time_left; bool disable_credit_flow_ctrl = false; u16 message_id, service_id, flags = 0; u8 tx_alloc = 0; /* special case for HTC pseudo control service */ if (conn_req->service_id == ATH10K_HTC_SVC_ID_RSVD_CTRL) { disable_credit_flow_ctrl = true; assigned_eid = ATH10K_HTC_EP_0; max_msg_size = ATH10K_HTC_MAX_CTRL_MSG_LEN; memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy)); goto setup; } tx_alloc = ath10k_htc_get_credit_allocation(htc, conn_req->service_id); if (!tx_alloc) ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc service %s does not allocate target credits\n", htc_service_name(conn_req->service_id)); skb = ath10k_htc_build_tx_ctrl_skb(htc->ar); if (!skb) { ath10k_err(ar, "Failed to allocate HTC packet\n"); return -ENOMEM; } length = sizeof(msg->hdr) + sizeof(msg->connect_service); skb_put(skb, length); memset(skb->data, 0, length); msg = (struct ath10k_htc_msg *)skb->data; msg->hdr.message_id = __cpu_to_le16(ATH10K_HTC_MSG_CONNECT_SERVICE_ID); flags |= SM(tx_alloc, ATH10K_HTC_CONN_FLAGS_RECV_ALLOC); /* Only enable credit flow control for WMI ctrl service */ if (conn_req->service_id != ATH10K_HTC_SVC_ID_WMI_CONTROL) { flags |= ATH10K_HTC_CONN_FLAGS_DISABLE_CREDIT_FLOW_CTRL; disable_credit_flow_ctrl = true; } req_msg = &msg->connect_service; req_msg->flags = __cpu_to_le16(flags); req_msg->service_id = __cpu_to_le16(conn_req->service_id); reinit_completion(&htc->ctl_resp); status = ath10k_htc_send(htc, ATH10K_HTC_EP_0, skb); if (status) { kfree_skb(skb); return status; } /* wait for response */ time_left = wait_for_completion_timeout(&htc->ctl_resp, ATH10K_HTC_CONN_SVC_TIMEOUT_HZ); if (!time_left) { ath10k_err(ar, "Service connect timeout\n"); return -ETIMEDOUT; } /* we controlled the buffer creation, it's aligned */ msg = (struct ath10k_htc_msg *)htc->control_resp_buffer; resp_msg = &msg->connect_service_response; message_id = __le16_to_cpu(msg->hdr.message_id); service_id = __le16_to_cpu(resp_msg->service_id); if ((message_id != ATH10K_HTC_MSG_CONNECT_SERVICE_RESP_ID) || (htc->control_resp_len < sizeof(msg->hdr) + sizeof(msg->connect_service_response))) { ath10k_err(ar, "Invalid resp message ID 0x%x", message_id); return -EPROTO; } ath10k_dbg(ar, ATH10K_DBG_HTC, "HTC Service %s connect response: status: 0x%x, assigned ep: 0x%x\n", htc_service_name(service_id), resp_msg->status, resp_msg->eid); conn_resp->connect_resp_code = resp_msg->status; /* check response status */ if (resp_msg->status != ATH10K_HTC_CONN_SVC_STATUS_SUCCESS) { ath10k_err(ar, "HTC Service %s connect request failed: 0x%x)\n", htc_service_name(service_id), resp_msg->status); return -EPROTO; } assigned_eid = (enum ath10k_htc_ep_id)resp_msg->eid; max_msg_size = __le16_to_cpu(resp_msg->max_msg_size); setup: if (assigned_eid >= ATH10K_HTC_EP_COUNT) return -EPROTO; if (max_msg_size == 0) return -EPROTO; ep = &htc->endpoint[assigned_eid]; ep->eid = assigned_eid; if (ep->service_id != ATH10K_HTC_SVC_ID_UNUSED) return -EPROTO; /* return assigned endpoint to caller */ conn_resp->eid = assigned_eid; conn_resp->max_msg_len = __le16_to_cpu(resp_msg->max_msg_size); /* setup the endpoint */ ep->service_id = conn_req->service_id; ep->max_tx_queue_depth = conn_req->max_send_queue_depth; ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size); ep->tx_credits = tx_alloc; ep->tx_credit_size = htc->target_credit_size; ep->tx_credits_per_max_message = ep->max_ep_message_len / htc->target_credit_size; if (ep->max_ep_message_len % htc->target_credit_size) ep->tx_credits_per_max_message++; /* copy all the callbacks */ ep->ep_ops = conn_req->ep_ops; status = ath10k_hif_map_service_to_pipe(htc->ar, ep->service_id, &ep->ul_pipe_id, &ep->dl_pipe_id); if (status) return status; ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc service '%s' ul pipe %d dl pipe %d eid %d ready\n", htc_service_name(ep->service_id), ep->ul_pipe_id, ep->dl_pipe_id, ep->eid); if (disable_credit_flow_ctrl && ep->tx_credit_flow_enabled) { ep->tx_credit_flow_enabled = false; ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot htc service '%s' eid %d TX flow control disabled\n", htc_service_name(ep->service_id), assigned_eid); } return status; }