// SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/ #include <linux/skbuff.h> #include <linux/ctype.h>
if (ep->ep_ops.ep_tx_credits) {
spin_unlock_bh(&htc->tx_lock);
ep->ep_ops.ep_tx_credits(htc->ab);
spin_lock_bh(&htc->tx_lock);
}
}
spin_unlock_bh(&htc->tx_lock);
}
staticint ath11k_htc_process_trailer(struct ath11k_htc *htc,
u8 *buffer, int length, enum ath11k_htc_ep_id src_eid)
{ struct ath11k_base *ab = htc->ab; int status = 0; struct ath11k_htc_record *record;
size_t len;
while (length > 0) {
record = (struct ath11k_htc_record *)buffer;
if (length < sizeof(record->hdr)) {
status = -EINVAL; break;
}
if (record->hdr.len > length) { /* no room left in buffer for record */
ath11k_warn(ab, "Invalid record length: %d\n",
record->hdr.len);
status = -EINVAL; break;
}
if (ab->hw_params.credit_flow) { switch (record->hdr.id) { case ATH11K_HTC_RECORD_CREDITS:
len = sizeof(struct ath11k_htc_credit_report); if (record->hdr.len < len) {
ath11k_warn(ab, "Credit report too long\n");
status = -EINVAL; break;
}
ath11k_htc_process_credit_report(htc,
record->credit_report,
record->hdr.len,
src_eid); break; default:
ath11k_warn(ab, "Unhandled record: id:%d length:%d\n",
record->hdr.id, record->hdr.len); break;
}
}
if (status) break;
/* multiple records may be present in a trailer */
buffer += sizeof(record->hdr) + record->hdr.len;
length -= sizeof(record->hdr) + record->hdr.len;
}
switch (message_id) { case ATH11K_HTC_MSG_READY_ID: case ATH11K_HTC_MSG_CONNECT_SERVICE_RESP_ID: /* handle HTC control message */ if (completion_done(&htc->ctl_resp)) { /* this is a fatal error, target should not be * sending unsolicited messages on the ep 0
*/
ath11k_warn(ab, "HTC rx ctrl still processing\n");
complete(&htc->ctl_resp); goto out;
}
/* skb is now owned by the rx completion handler */
skb = NULL;
out:
kfree_skb(skb);
}
staticvoid ath11k_htc_control_rx_complete(struct ath11k_base *ab, struct sk_buff *skb)
{ /* This is unexpected. FW is not supposed to send regular rx on this * endpoint.
*/
ath11k_warn(ab, "unexpected htc rx\n");
kfree_skb(skb);
}
staticconstchar *htc_service_name(enum ath11k_htc_svc_id id)
{ switch (id) { case ATH11K_HTC_SVC_ID_RESERVED: return"Reserved"; case ATH11K_HTC_SVC_ID_RSVD_CTRL: return"Control"; case ATH11K_HTC_SVC_ID_WMI_CONTROL: return"WMI"; case ATH11K_HTC_SVC_ID_WMI_DATA_BE: return"DATA BE"; case ATH11K_HTC_SVC_ID_WMI_DATA_BK: return"DATA BK"; case ATH11K_HTC_SVC_ID_WMI_DATA_VI: return"DATA VI"; case ATH11K_HTC_SVC_ID_WMI_DATA_VO: return"DATA VO"; case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1: return"WMI MAC1"; case ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2: return"WMI MAC2"; case ATH11K_HTC_SVC_ID_NMI_CONTROL: return"NMI Control"; case ATH11K_HTC_SVC_ID_NMI_DATA: return"NMI Data"; case ATH11K_HTC_SVC_ID_HTT_DATA_MSG: return"HTT Data"; case ATH11K_HTC_SVC_ID_TEST_RAW_STREAMS: return"RAW"; case ATH11K_HTC_SVC_ID_IPA_TX: return"IPA TX"; case ATH11K_HTC_SVC_ID_PKT_LOG: return"PKT LOG";
}
return"Unknown";
}
staticvoid ath11k_htc_reset_endpoint_states(struct ath11k_htc *htc)
{ struct ath11k_htc_ep *ep; int i;
for (i = ATH11K_HTC_EP_0; i < ATH11K_HTC_EP_COUNT; i++) {
ep = &htc->endpoint[i];
ep->service_id = ATH11K_HTC_SVC_ID_UNUSED;
ep->max_ep_message_len = 0;
ep->max_tx_queue_depth = 0;
ep->eid = i;
ep->htc = htc;
ep->tx_credit_flow_enabled = true;
}
}
for (i = 0; i < ATH11K_HTC_MAX_SERVICE_ALLOC_ENTRIES; i++) { if (htc->service_alloc_table[i].service_id == service_id) {
allocation =
htc->service_alloc_table[i].credit_allocation;
}
}
return allocation;
}
staticint ath11k_htc_setup_target_buffer_assignments(struct ath11k_htc *htc)
{ struct ath11k_htc_svc_tx_credits *serv_entry; staticconst u32 svc_id[] = {
ATH11K_HTC_SVC_ID_WMI_CONTROL,
ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC1,
ATH11K_HTC_SVC_ID_WMI_CONTROL_MAC2,
}; int i, credits;
if ((htc->wmi_ep_count == 0) ||
(htc->wmi_ep_count > ARRAY_SIZE(svc_id))) return -EINVAL;
/* Divide credits among number of endpoints for WMI */
credits = credits / htc->wmi_ep_count; for (i = 0; i < htc->wmi_ep_count; i++) {
serv_entry[i].service_id = svc_id[i];
serv_entry[i].credit_allocation = credits;
}
return 0;
}
int ath11k_htc_wait_target(struct ath11k_htc *htc)
{ int i, status = 0; struct ath11k_base *ab = htc->ab; unsignedlong time_left; struct ath11k_htc_ready *ready;
u16 message_id;
u16 credit_count;
u16 credit_size;
time_left = wait_for_completion_timeout(&htc->ctl_resp,
ATH11K_HTC_WAIT_TIMEOUT_HZ); if (!time_left) {
ath11k_warn(ab, "failed to receive control response completion, polling..\n");
for (i = 0; i < ab->hw_params.ce_count; i++)
ath11k_ce_per_engine_service(htc->ab, i);
/* special case for HTC pseudo control service */ if (conn_req->service_id == ATH11K_HTC_SVC_ID_RSVD_CTRL) {
disable_credit_flow_ctrl = true;
assigned_eid = ATH11K_HTC_EP_0;
max_msg_size = ATH11K_HTC_MAX_CTRL_MSG_LEN;
memset(&resp_msg_dummy, 0, sizeof(resp_msg_dummy)); goto setup;
}
tx_alloc = ath11k_htc_get_credit_allocation(htc,
conn_req->service_id); if (!tx_alloc)
ath11k_dbg(ab, ATH11K_DBG_BOOT, "htc service %s does not allocate target credits\n",
htc_service_name(conn_req->service_id));
skb = ath11k_htc_build_tx_ctrl_skb(htc->ab); if (!skb) {
ath11k_warn(ab, "Failed to allocate HTC packet\n"); return -ENOMEM;
}
/* connect fake service */
ret = ath11k_htc_connect_service(htc, &conn_req, &conn_resp); if (ret) {
ath11k_err(ab, "could not connect to htc service (%d)\n", ret); return ret;
}
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.