162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2010-2011 Atheros Communications Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "htc.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic int htc_issue_send(struct htc_target *target, struct sk_buff* skb, 2262306a36Sopenharmony_ci u16 len, u8 flags, u8 epid) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci{ 2562306a36Sopenharmony_ci struct htc_frame_hdr *hdr; 2662306a36Sopenharmony_ci struct htc_endpoint *endpoint = &target->endpoint[epid]; 2762306a36Sopenharmony_ci int status; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci hdr = skb_push(skb, sizeof(struct htc_frame_hdr)); 3062306a36Sopenharmony_ci hdr->endpoint_id = epid; 3162306a36Sopenharmony_ci hdr->flags = flags; 3262306a36Sopenharmony_ci hdr->payload_len = cpu_to_be16(len); 3362306a36Sopenharmony_ci memset(hdr->control, 0, sizeof(hdr->control)); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci status = target->hif->send(target->hif_dev, endpoint->ul_pipeid, skb); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return status; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic struct htc_endpoint *get_next_avail_ep(struct htc_endpoint *endpoint) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci enum htc_endpoint_id avail_epid; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci for (avail_epid = (ENDPOINT_MAX - 1); avail_epid > ENDPOINT0; avail_epid--) 4562306a36Sopenharmony_ci if (endpoint[avail_epid].service_id == 0) 4662306a36Sopenharmony_ci return &endpoint[avail_epid]; 4762306a36Sopenharmony_ci return NULL; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic u8 service_to_ulpipe(u16 service_id) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci switch (service_id) { 5362306a36Sopenharmony_ci case WMI_CONTROL_SVC: 5462306a36Sopenharmony_ci return 4; 5562306a36Sopenharmony_ci case WMI_BEACON_SVC: 5662306a36Sopenharmony_ci case WMI_CAB_SVC: 5762306a36Sopenharmony_ci case WMI_UAPSD_SVC: 5862306a36Sopenharmony_ci case WMI_MGMT_SVC: 5962306a36Sopenharmony_ci case WMI_DATA_VO_SVC: 6062306a36Sopenharmony_ci case WMI_DATA_VI_SVC: 6162306a36Sopenharmony_ci case WMI_DATA_BE_SVC: 6262306a36Sopenharmony_ci case WMI_DATA_BK_SVC: 6362306a36Sopenharmony_ci return 1; 6462306a36Sopenharmony_ci default: 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cistatic u8 service_to_dlpipe(u16 service_id) 7062306a36Sopenharmony_ci{ 7162306a36Sopenharmony_ci switch (service_id) { 7262306a36Sopenharmony_ci case WMI_CONTROL_SVC: 7362306a36Sopenharmony_ci return 3; 7462306a36Sopenharmony_ci case WMI_BEACON_SVC: 7562306a36Sopenharmony_ci case WMI_CAB_SVC: 7662306a36Sopenharmony_ci case WMI_UAPSD_SVC: 7762306a36Sopenharmony_ci case WMI_MGMT_SVC: 7862306a36Sopenharmony_ci case WMI_DATA_VO_SVC: 7962306a36Sopenharmony_ci case WMI_DATA_VI_SVC: 8062306a36Sopenharmony_ci case WMI_DATA_BE_SVC: 8162306a36Sopenharmony_ci case WMI_DATA_BK_SVC: 8262306a36Sopenharmony_ci return 2; 8362306a36Sopenharmony_ci default: 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void htc_process_target_rdy(struct htc_target *target, 8962306a36Sopenharmony_ci void *buf) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct htc_endpoint *endpoint; 9262306a36Sopenharmony_ci struct htc_ready_msg *htc_ready_msg = (struct htc_ready_msg *) buf; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci target->credit_size = be16_to_cpu(htc_ready_msg->credit_size); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci endpoint = &target->endpoint[ENDPOINT0]; 9762306a36Sopenharmony_ci endpoint->service_id = HTC_CTRL_RSVD_SVC; 9862306a36Sopenharmony_ci endpoint->max_msglen = HTC_MAX_CONTROL_MESSAGE_LENGTH; 9962306a36Sopenharmony_ci atomic_inc(&target->tgt_ready); 10062306a36Sopenharmony_ci complete(&target->target_wait); 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic void htc_process_conn_rsp(struct htc_target *target, 10462306a36Sopenharmony_ci struct htc_frame_hdr *htc_hdr) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct htc_conn_svc_rspmsg *svc_rspmsg; 10762306a36Sopenharmony_ci struct htc_endpoint *endpoint, *tmp_endpoint = NULL; 10862306a36Sopenharmony_ci u16 service_id; 10962306a36Sopenharmony_ci u16 max_msglen; 11062306a36Sopenharmony_ci enum htc_endpoint_id epid, tepid; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci svc_rspmsg = (struct htc_conn_svc_rspmsg *) 11362306a36Sopenharmony_ci ((void *) htc_hdr + sizeof(struct htc_frame_hdr)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (svc_rspmsg->status == HTC_SERVICE_SUCCESS) { 11662306a36Sopenharmony_ci epid = svc_rspmsg->endpoint_id; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci /* Check that the received epid for the endpoint to attach 11962306a36Sopenharmony_ci * a new service is valid. ENDPOINT0 can't be used here as it 12062306a36Sopenharmony_ci * is already reserved for HTC_CTRL_RSVD_SVC service and thus 12162306a36Sopenharmony_ci * should not be modified. 12262306a36Sopenharmony_ci */ 12362306a36Sopenharmony_ci if (epid <= ENDPOINT0 || epid >= ENDPOINT_MAX) 12462306a36Sopenharmony_ci return; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci service_id = be16_to_cpu(svc_rspmsg->service_id); 12762306a36Sopenharmony_ci max_msglen = be16_to_cpu(svc_rspmsg->max_msg_len); 12862306a36Sopenharmony_ci endpoint = &target->endpoint[epid]; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci for (tepid = (ENDPOINT_MAX - 1); tepid > ENDPOINT0; tepid--) { 13162306a36Sopenharmony_ci tmp_endpoint = &target->endpoint[tepid]; 13262306a36Sopenharmony_ci if (tmp_endpoint->service_id == service_id) { 13362306a36Sopenharmony_ci tmp_endpoint->service_id = 0; 13462306a36Sopenharmony_ci break; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (tepid == ENDPOINT0) 13962306a36Sopenharmony_ci return; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci endpoint->service_id = service_id; 14262306a36Sopenharmony_ci endpoint->max_txqdepth = tmp_endpoint->max_txqdepth; 14362306a36Sopenharmony_ci endpoint->ep_callbacks = tmp_endpoint->ep_callbacks; 14462306a36Sopenharmony_ci endpoint->ul_pipeid = tmp_endpoint->ul_pipeid; 14562306a36Sopenharmony_ci endpoint->dl_pipeid = tmp_endpoint->dl_pipeid; 14662306a36Sopenharmony_ci endpoint->max_msglen = max_msglen; 14762306a36Sopenharmony_ci target->conn_rsp_epid = epid; 14862306a36Sopenharmony_ci complete(&target->cmd_wait); 14962306a36Sopenharmony_ci } else { 15062306a36Sopenharmony_ci target->conn_rsp_epid = ENDPOINT_UNUSED; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic int htc_config_pipe_credits(struct htc_target *target) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct sk_buff *skb; 15762306a36Sopenharmony_ci struct htc_config_pipe_msg *cp_msg; 15862306a36Sopenharmony_ci int ret; 15962306a36Sopenharmony_ci unsigned long time_left; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci skb = alloc_skb(50 + sizeof(struct htc_frame_hdr), GFP_ATOMIC); 16262306a36Sopenharmony_ci if (!skb) { 16362306a36Sopenharmony_ci dev_err(target->dev, "failed to allocate send buffer\n"); 16462306a36Sopenharmony_ci return -ENOMEM; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci skb_reserve(skb, sizeof(struct htc_frame_hdr)); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci cp_msg = skb_put(skb, sizeof(struct htc_config_pipe_msg)); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci cp_msg->message_id = cpu_to_be16(HTC_MSG_CONFIG_PIPE_ID); 17162306a36Sopenharmony_ci cp_msg->pipe_id = USB_WLAN_TX_PIPE; 17262306a36Sopenharmony_ci cp_msg->credits = target->credits; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci target->htc_flags |= HTC_OP_CONFIG_PIPE_CREDITS; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); 17762306a36Sopenharmony_ci if (ret) 17862306a36Sopenharmony_ci goto err; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); 18162306a36Sopenharmony_ci if (!time_left) { 18262306a36Sopenharmony_ci dev_err(target->dev, "HTC credit config timeout\n"); 18362306a36Sopenharmony_ci return -ETIMEDOUT; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return 0; 18762306a36Sopenharmony_cierr: 18862306a36Sopenharmony_ci kfree_skb(skb); 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int htc_setup_complete(struct htc_target *target) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct sk_buff *skb; 19562306a36Sopenharmony_ci struct htc_comp_msg *comp_msg; 19662306a36Sopenharmony_ci int ret = 0; 19762306a36Sopenharmony_ci unsigned long time_left; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci skb = alloc_skb(50 + sizeof(struct htc_frame_hdr), GFP_ATOMIC); 20062306a36Sopenharmony_ci if (!skb) { 20162306a36Sopenharmony_ci dev_err(target->dev, "failed to allocate send buffer\n"); 20262306a36Sopenharmony_ci return -ENOMEM; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci skb_reserve(skb, sizeof(struct htc_frame_hdr)); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci comp_msg = skb_put(skb, sizeof(struct htc_comp_msg)); 20762306a36Sopenharmony_ci comp_msg->msg_id = cpu_to_be16(HTC_MSG_SETUP_COMPLETE_ID); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci target->htc_flags |= HTC_OP_START_WAIT; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); 21262306a36Sopenharmony_ci if (ret) 21362306a36Sopenharmony_ci goto err; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); 21662306a36Sopenharmony_ci if (!time_left) { 21762306a36Sopenharmony_ci dev_err(target->dev, "HTC start timeout\n"); 21862306a36Sopenharmony_ci return -ETIMEDOUT; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cierr: 22462306a36Sopenharmony_ci kfree_skb(skb); 22562306a36Sopenharmony_ci return -EINVAL; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci/* HTC APIs */ 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciint htc_init(struct htc_target *target) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int ret; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = htc_config_pipe_credits(target); 23562306a36Sopenharmony_ci if (ret) 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci return htc_setup_complete(target); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciint htc_connect_service(struct htc_target *target, 24262306a36Sopenharmony_ci struct htc_service_connreq *service_connreq, 24362306a36Sopenharmony_ci enum htc_endpoint_id *conn_rsp_epid) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct sk_buff *skb; 24662306a36Sopenharmony_ci struct htc_endpoint *endpoint; 24762306a36Sopenharmony_ci struct htc_conn_svc_msg *conn_msg; 24862306a36Sopenharmony_ci int ret; 24962306a36Sopenharmony_ci unsigned long time_left; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Find an available endpoint */ 25262306a36Sopenharmony_ci endpoint = get_next_avail_ep(target->endpoint); 25362306a36Sopenharmony_ci if (!endpoint) { 25462306a36Sopenharmony_ci dev_err(target->dev, "Endpoint is not available for service %d\n", 25562306a36Sopenharmony_ci service_connreq->service_id); 25662306a36Sopenharmony_ci return -EINVAL; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci endpoint->service_id = service_connreq->service_id; 26062306a36Sopenharmony_ci endpoint->max_txqdepth = service_connreq->max_send_qdepth; 26162306a36Sopenharmony_ci endpoint->ul_pipeid = service_to_ulpipe(service_connreq->service_id); 26262306a36Sopenharmony_ci endpoint->dl_pipeid = service_to_dlpipe(service_connreq->service_id); 26362306a36Sopenharmony_ci endpoint->ep_callbacks = service_connreq->ep_callbacks; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci skb = alloc_skb(sizeof(struct htc_conn_svc_msg) + 26662306a36Sopenharmony_ci sizeof(struct htc_frame_hdr), GFP_ATOMIC); 26762306a36Sopenharmony_ci if (!skb) { 26862306a36Sopenharmony_ci dev_err(target->dev, "Failed to allocate buf to send" 26962306a36Sopenharmony_ci "service connect req\n"); 27062306a36Sopenharmony_ci return -ENOMEM; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci skb_reserve(skb, sizeof(struct htc_frame_hdr)); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci conn_msg = skb_put(skb, sizeof(struct htc_conn_svc_msg)); 27662306a36Sopenharmony_ci conn_msg->service_id = cpu_to_be16(service_connreq->service_id); 27762306a36Sopenharmony_ci conn_msg->msg_id = cpu_to_be16(HTC_MSG_CONNECT_SERVICE_ID); 27862306a36Sopenharmony_ci conn_msg->con_flags = cpu_to_be16(service_connreq->con_flags); 27962306a36Sopenharmony_ci conn_msg->dl_pipeid = endpoint->dl_pipeid; 28062306a36Sopenharmony_ci conn_msg->ul_pipeid = endpoint->ul_pipeid; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* To prevent infoleak */ 28362306a36Sopenharmony_ci conn_msg->svc_meta_len = 0; 28462306a36Sopenharmony_ci conn_msg->pad = 0; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci ret = htc_issue_send(target, skb, skb->len, 0, ENDPOINT0); 28762306a36Sopenharmony_ci if (ret) 28862306a36Sopenharmony_ci goto err; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci time_left = wait_for_completion_timeout(&target->cmd_wait, HZ); 29162306a36Sopenharmony_ci if (!time_left) { 29262306a36Sopenharmony_ci dev_err(target->dev, "Service connection timeout for: %d\n", 29362306a36Sopenharmony_ci service_connreq->service_id); 29462306a36Sopenharmony_ci return -ETIMEDOUT; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci *conn_rsp_epid = target->conn_rsp_epid; 29862306a36Sopenharmony_ci return 0; 29962306a36Sopenharmony_cierr: 30062306a36Sopenharmony_ci kfree_skb(skb); 30162306a36Sopenharmony_ci return ret; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ciint htc_send(struct htc_target *target, struct sk_buff *skb) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct ath9k_htc_tx_ctl *tx_ctl; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci tx_ctl = HTC_SKB_CB(skb); 30962306a36Sopenharmony_ci return htc_issue_send(target, skb, skb->len, 0, tx_ctl->epid); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ciint htc_send_epid(struct htc_target *target, struct sk_buff *skb, 31362306a36Sopenharmony_ci enum htc_endpoint_id epid) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci return htc_issue_send(target, skb, skb->len, 0, epid); 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_civoid htc_stop(struct htc_target *target) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci target->hif->stop(target->hif_dev); 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_civoid htc_start(struct htc_target *target) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci target->hif->start(target->hif_dev); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_civoid htc_sta_drain(struct htc_target *target, u8 idx) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci target->hif->sta_drain(target->hif_dev, idx); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_civoid ath9k_htc_txcompletion_cb(struct htc_target *htc_handle, 33462306a36Sopenharmony_ci struct sk_buff *skb, bool txok) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct htc_endpoint *endpoint; 33762306a36Sopenharmony_ci struct htc_frame_hdr *htc_hdr = NULL; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (htc_handle->htc_flags & HTC_OP_CONFIG_PIPE_CREDITS) { 34062306a36Sopenharmony_ci complete(&htc_handle->cmd_wait); 34162306a36Sopenharmony_ci htc_handle->htc_flags &= ~HTC_OP_CONFIG_PIPE_CREDITS; 34262306a36Sopenharmony_ci goto ret; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci if (htc_handle->htc_flags & HTC_OP_START_WAIT) { 34662306a36Sopenharmony_ci complete(&htc_handle->cmd_wait); 34762306a36Sopenharmony_ci htc_handle->htc_flags &= ~HTC_OP_START_WAIT; 34862306a36Sopenharmony_ci goto ret; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci if (skb) { 35262306a36Sopenharmony_ci htc_hdr = (struct htc_frame_hdr *) skb->data; 35362306a36Sopenharmony_ci if (htc_hdr->endpoint_id >= ARRAY_SIZE(htc_handle->endpoint)) 35462306a36Sopenharmony_ci goto ret; 35562306a36Sopenharmony_ci endpoint = &htc_handle->endpoint[htc_hdr->endpoint_id]; 35662306a36Sopenharmony_ci skb_pull(skb, sizeof(struct htc_frame_hdr)); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (endpoint->ep_callbacks.tx) { 35962306a36Sopenharmony_ci endpoint->ep_callbacks.tx(endpoint->ep_callbacks.priv, 36062306a36Sopenharmony_ci skb, htc_hdr->endpoint_id, 36162306a36Sopenharmony_ci txok); 36262306a36Sopenharmony_ci } else { 36362306a36Sopenharmony_ci kfree_skb(skb); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ciret: 36962306a36Sopenharmony_ci kfree_skb(skb); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic void ath9k_htc_fw_panic_report(struct htc_target *htc_handle, 37362306a36Sopenharmony_ci struct sk_buff *skb, u32 len) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci uint32_t *pattern = (uint32_t *)skb->data; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (*pattern == 0x33221199 && len >= sizeof(struct htc_panic_bad_vaddr)) { 37862306a36Sopenharmony_ci struct htc_panic_bad_vaddr *htc_panic; 37962306a36Sopenharmony_ci htc_panic = (struct htc_panic_bad_vaddr *) skb->data; 38062306a36Sopenharmony_ci dev_err(htc_handle->dev, "ath: firmware panic! " 38162306a36Sopenharmony_ci "exccause: 0x%08x; pc: 0x%08x; badvaddr: 0x%08x.\n", 38262306a36Sopenharmony_ci htc_panic->exccause, htc_panic->pc, 38362306a36Sopenharmony_ci htc_panic->badvaddr); 38462306a36Sopenharmony_ci return; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci if (*pattern == 0x33221299) { 38762306a36Sopenharmony_ci struct htc_panic_bad_epid *htc_panic; 38862306a36Sopenharmony_ci htc_panic = (struct htc_panic_bad_epid *) skb->data; 38962306a36Sopenharmony_ci dev_err(htc_handle->dev, "ath: firmware panic! " 39062306a36Sopenharmony_ci "bad epid: 0x%08x\n", htc_panic->epid); 39162306a36Sopenharmony_ci return; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci dev_err(htc_handle->dev, "ath: unknown panic pattern!\n"); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci/* 39762306a36Sopenharmony_ci * HTC Messages are handled directly here and the obtained SKB 39862306a36Sopenharmony_ci * is freed. 39962306a36Sopenharmony_ci * 40062306a36Sopenharmony_ci * Service messages (Data, WMI) are passed to the corresponding 40162306a36Sopenharmony_ci * endpoint RX handlers, which have to free the SKB. 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_civoid ath9k_htc_rx_msg(struct htc_target *htc_handle, 40462306a36Sopenharmony_ci struct sk_buff *skb, u32 len, u8 pipe_id) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct htc_frame_hdr *htc_hdr; 40762306a36Sopenharmony_ci enum htc_endpoint_id epid; 40862306a36Sopenharmony_ci struct htc_endpoint *endpoint; 40962306a36Sopenharmony_ci __be16 *msg_id; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (!htc_handle || !skb) 41262306a36Sopenharmony_ci return; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* A valid message requires len >= 8. 41562306a36Sopenharmony_ci * 41662306a36Sopenharmony_ci * sizeof(struct htc_frame_hdr) == 8 41762306a36Sopenharmony_ci * sizeof(struct htc_ready_msg) == 8 41862306a36Sopenharmony_ci * sizeof(struct htc_panic_bad_vaddr) == 16 41962306a36Sopenharmony_ci * sizeof(struct htc_panic_bad_epid) == 8 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci if (unlikely(len < sizeof(struct htc_frame_hdr))) 42262306a36Sopenharmony_ci goto invalid; 42362306a36Sopenharmony_ci htc_hdr = (struct htc_frame_hdr *) skb->data; 42462306a36Sopenharmony_ci epid = htc_hdr->endpoint_id; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if (epid == 0x99) { 42762306a36Sopenharmony_ci ath9k_htc_fw_panic_report(htc_handle, skb, len); 42862306a36Sopenharmony_ci kfree_skb(skb); 42962306a36Sopenharmony_ci return; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (epid < 0 || epid >= ENDPOINT_MAX) { 43362306a36Sopenharmony_ciinvalid: 43462306a36Sopenharmony_ci if (pipe_id != USB_REG_IN_PIPE) 43562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 43662306a36Sopenharmony_ci else 43762306a36Sopenharmony_ci kfree_skb(skb); 43862306a36Sopenharmony_ci return; 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (epid == ENDPOINT0) { 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Handle trailer */ 44462306a36Sopenharmony_ci if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) { 44562306a36Sopenharmony_ci if (be32_to_cpu(*(__be32 *) skb->data) == 0x00C60000) { 44662306a36Sopenharmony_ci /* Move past the Watchdog pattern */ 44762306a36Sopenharmony_ci htc_hdr = (struct htc_frame_hdr *)(skb->data + 4); 44862306a36Sopenharmony_ci len -= 4; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci /* Get the message ID */ 45362306a36Sopenharmony_ci if (unlikely(len < sizeof(struct htc_frame_hdr) + sizeof(__be16))) 45462306a36Sopenharmony_ci goto invalid; 45562306a36Sopenharmony_ci msg_id = (__be16 *) ((void *) htc_hdr + 45662306a36Sopenharmony_ci sizeof(struct htc_frame_hdr)); 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Now process HTC messages */ 45962306a36Sopenharmony_ci switch (be16_to_cpu(*msg_id)) { 46062306a36Sopenharmony_ci case HTC_MSG_READY_ID: 46162306a36Sopenharmony_ci if (unlikely(len < sizeof(struct htc_ready_msg))) 46262306a36Sopenharmony_ci goto invalid; 46362306a36Sopenharmony_ci htc_process_target_rdy(htc_handle, htc_hdr); 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci case HTC_MSG_CONNECT_SERVICE_RESPONSE_ID: 46662306a36Sopenharmony_ci if (unlikely(len < sizeof(struct htc_frame_hdr) + 46762306a36Sopenharmony_ci sizeof(struct htc_conn_svc_rspmsg))) 46862306a36Sopenharmony_ci goto invalid; 46962306a36Sopenharmony_ci htc_process_conn_rsp(htc_handle, htc_hdr); 47062306a36Sopenharmony_ci break; 47162306a36Sopenharmony_ci default: 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci kfree_skb(skb); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci } else { 47862306a36Sopenharmony_ci if (htc_hdr->flags & HTC_FLAGS_RECV_TRAILER) 47962306a36Sopenharmony_ci skb_trim(skb, len - htc_hdr->control[0]); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci skb_pull(skb, sizeof(struct htc_frame_hdr)); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci endpoint = &htc_handle->endpoint[epid]; 48462306a36Sopenharmony_ci if (endpoint->ep_callbacks.rx) 48562306a36Sopenharmony_ci endpoint->ep_callbacks.rx(endpoint->ep_callbacks.priv, 48662306a36Sopenharmony_ci skb, epid); 48762306a36Sopenharmony_ci else 48862306a36Sopenharmony_ci goto invalid; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistruct htc_target *ath9k_htc_hw_alloc(void *hif_handle, 49362306a36Sopenharmony_ci struct ath9k_htc_hif *hif, 49462306a36Sopenharmony_ci struct device *dev) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct htc_endpoint *endpoint; 49762306a36Sopenharmony_ci struct htc_target *target; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci target = kzalloc(sizeof(struct htc_target), GFP_KERNEL); 50062306a36Sopenharmony_ci if (!target) 50162306a36Sopenharmony_ci return NULL; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci init_completion(&target->target_wait); 50462306a36Sopenharmony_ci init_completion(&target->cmd_wait); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci target->hif = hif; 50762306a36Sopenharmony_ci target->hif_dev = hif_handle; 50862306a36Sopenharmony_ci target->dev = dev; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* Assign control endpoint pipe IDs */ 51162306a36Sopenharmony_ci endpoint = &target->endpoint[ENDPOINT0]; 51262306a36Sopenharmony_ci endpoint->ul_pipeid = hif->control_ul_pipe; 51362306a36Sopenharmony_ci endpoint->dl_pipeid = hif->control_dl_pipe; 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci atomic_set(&target->tgt_ready, 0); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return target; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_civoid ath9k_htc_hw_free(struct htc_target *htc) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci kfree(htc); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ciint ath9k_htc_hw_init(struct htc_target *target, 52662306a36Sopenharmony_ci struct device *dev, u16 devid, 52762306a36Sopenharmony_ci char *product, u32 drv_info) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci if (ath9k_htc_probe_device(target, dev, devid, product, drv_info)) { 53062306a36Sopenharmony_ci pr_err("Failed to initialize the device\n"); 53162306a36Sopenharmony_ci return -ENODEV; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_civoid ath9k_htc_hw_deinit(struct htc_target *target, bool hot_unplug) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci if (target) 54062306a36Sopenharmony_ci ath9k_htc_disconnect_device(target, hot_unplug); 54162306a36Sopenharmony_ci} 542