Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2010-2011 Atheros Communications Inc.
0003  *
0004  * Permission to use, copy, modify, and/or distribute this software for any
0005  * purpose with or without fee is hereby granted, provided that the above
0006  * copyright notice and this permission notice appear in all copies.
0007  *
0008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0015  */
0016 
0017 #include "htc.h"
0018 
0019 static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
0020 {
0021     switch (wmi_cmd) {
0022     case WMI_ECHO_CMDID:
0023         return "WMI_ECHO_CMDID";
0024     case WMI_ACCESS_MEMORY_CMDID:
0025         return "WMI_ACCESS_MEMORY_CMDID";
0026     case WMI_GET_FW_VERSION:
0027         return "WMI_GET_FW_VERSION";
0028     case WMI_DISABLE_INTR_CMDID:
0029         return "WMI_DISABLE_INTR_CMDID";
0030     case WMI_ENABLE_INTR_CMDID:
0031         return "WMI_ENABLE_INTR_CMDID";
0032     case WMI_ATH_INIT_CMDID:
0033         return "WMI_ATH_INIT_CMDID";
0034     case WMI_ABORT_TXQ_CMDID:
0035         return "WMI_ABORT_TXQ_CMDID";
0036     case WMI_STOP_TX_DMA_CMDID:
0037         return "WMI_STOP_TX_DMA_CMDID";
0038     case WMI_ABORT_TX_DMA_CMDID:
0039         return "WMI_ABORT_TX_DMA_CMDID";
0040     case WMI_DRAIN_TXQ_CMDID:
0041         return "WMI_DRAIN_TXQ_CMDID";
0042     case WMI_DRAIN_TXQ_ALL_CMDID:
0043         return "WMI_DRAIN_TXQ_ALL_CMDID";
0044     case WMI_START_RECV_CMDID:
0045         return "WMI_START_RECV_CMDID";
0046     case WMI_STOP_RECV_CMDID:
0047         return "WMI_STOP_RECV_CMDID";
0048     case WMI_FLUSH_RECV_CMDID:
0049         return "WMI_FLUSH_RECV_CMDID";
0050     case WMI_SET_MODE_CMDID:
0051         return "WMI_SET_MODE_CMDID";
0052     case WMI_NODE_CREATE_CMDID:
0053         return "WMI_NODE_CREATE_CMDID";
0054     case WMI_NODE_REMOVE_CMDID:
0055         return "WMI_NODE_REMOVE_CMDID";
0056     case WMI_VAP_REMOVE_CMDID:
0057         return "WMI_VAP_REMOVE_CMDID";
0058     case WMI_VAP_CREATE_CMDID:
0059         return "WMI_VAP_CREATE_CMDID";
0060     case WMI_REG_READ_CMDID:
0061         return "WMI_REG_READ_CMDID";
0062     case WMI_REG_WRITE_CMDID:
0063         return "WMI_REG_WRITE_CMDID";
0064     case WMI_REG_RMW_CMDID:
0065         return "WMI_REG_RMW_CMDID";
0066     case WMI_RC_STATE_CHANGE_CMDID:
0067         return "WMI_RC_STATE_CHANGE_CMDID";
0068     case WMI_RC_RATE_UPDATE_CMDID:
0069         return "WMI_RC_RATE_UPDATE_CMDID";
0070     case WMI_TARGET_IC_UPDATE_CMDID:
0071         return "WMI_TARGET_IC_UPDATE_CMDID";
0072     case WMI_TX_AGGR_ENABLE_CMDID:
0073         return "WMI_TX_AGGR_ENABLE_CMDID";
0074     case WMI_TGT_DETACH_CMDID:
0075         return "WMI_TGT_DETACH_CMDID";
0076     case WMI_NODE_UPDATE_CMDID:
0077         return "WMI_NODE_UPDATE_CMDID";
0078     case WMI_INT_STATS_CMDID:
0079         return "WMI_INT_STATS_CMDID";
0080     case WMI_TX_STATS_CMDID:
0081         return "WMI_TX_STATS_CMDID";
0082     case WMI_RX_STATS_CMDID:
0083         return "WMI_RX_STATS_CMDID";
0084     case WMI_BITRATE_MASK_CMDID:
0085         return "WMI_BITRATE_MASK_CMDID";
0086     }
0087 
0088     return "Bogus";
0089 }
0090 
0091 struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
0092 {
0093     struct wmi *wmi;
0094 
0095     wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL);
0096     if (!wmi)
0097         return NULL;
0098 
0099     wmi->drv_priv = priv;
0100     wmi->stopped = false;
0101     skb_queue_head_init(&wmi->wmi_event_queue);
0102     spin_lock_init(&wmi->wmi_lock);
0103     spin_lock_init(&wmi->event_lock);
0104     mutex_init(&wmi->op_mutex);
0105     mutex_init(&wmi->multi_write_mutex);
0106     mutex_init(&wmi->multi_rmw_mutex);
0107     init_completion(&wmi->cmd_wait);
0108     INIT_LIST_HEAD(&wmi->pending_tx_events);
0109     tasklet_setup(&wmi->wmi_event_tasklet, ath9k_wmi_event_tasklet);
0110 
0111     return wmi;
0112 }
0113 
0114 void ath9k_stop_wmi(struct ath9k_htc_priv *priv)
0115 {
0116     struct wmi *wmi = priv->wmi;
0117 
0118     mutex_lock(&wmi->op_mutex);
0119     wmi->stopped = true;
0120     mutex_unlock(&wmi->op_mutex);
0121 }
0122 
0123 void ath9k_destroy_wmi(struct ath9k_htc_priv *priv)
0124 {
0125     kfree(priv->wmi);
0126 }
0127 
0128 void ath9k_wmi_event_drain(struct ath9k_htc_priv *priv)
0129 {
0130     unsigned long flags;
0131 
0132     tasklet_kill(&priv->wmi->wmi_event_tasklet);
0133     spin_lock_irqsave(&priv->wmi->wmi_lock, flags);
0134     __skb_queue_purge(&priv->wmi->wmi_event_queue);
0135     spin_unlock_irqrestore(&priv->wmi->wmi_lock, flags);
0136 }
0137 
0138 void ath9k_wmi_event_tasklet(struct tasklet_struct *t)
0139 {
0140     struct wmi *wmi = from_tasklet(wmi, t, wmi_event_tasklet);
0141     struct ath9k_htc_priv *priv = wmi->drv_priv;
0142     struct wmi_cmd_hdr *hdr;
0143     void *wmi_event;
0144     struct wmi_event_swba *swba;
0145     struct sk_buff *skb = NULL;
0146     unsigned long flags;
0147     u16 cmd_id;
0148 
0149     do {
0150         spin_lock_irqsave(&wmi->wmi_lock, flags);
0151         skb = __skb_dequeue(&wmi->wmi_event_queue);
0152         if (!skb) {
0153             spin_unlock_irqrestore(&wmi->wmi_lock, flags);
0154             return;
0155         }
0156         spin_unlock_irqrestore(&wmi->wmi_lock, flags);
0157 
0158         hdr = (struct wmi_cmd_hdr *) skb->data;
0159         cmd_id = be16_to_cpu(hdr->command_id);
0160         wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
0161 
0162         switch (cmd_id) {
0163         case WMI_SWBA_EVENTID:
0164             swba = wmi_event;
0165             ath9k_htc_swba(priv, swba);
0166             break;
0167         case WMI_FATAL_EVENTID:
0168             ieee80211_queue_work(wmi->drv_priv->hw,
0169                          &wmi->drv_priv->fatal_work);
0170             break;
0171         case WMI_TXSTATUS_EVENTID:
0172             /* Check if ath9k_tx_init() completed. */
0173             if (!data_race(priv->tx.initialized))
0174                 break;
0175 
0176             spin_lock_bh(&priv->tx.tx_lock);
0177             if (priv->tx.flags & ATH9K_HTC_OP_TX_DRAIN) {
0178                 spin_unlock_bh(&priv->tx.tx_lock);
0179                 break;
0180             }
0181             spin_unlock_bh(&priv->tx.tx_lock);
0182 
0183             ath9k_htc_txstatus(priv, wmi_event);
0184             break;
0185         default:
0186             break;
0187         }
0188 
0189         kfree_skb(skb);
0190     } while (1);
0191 }
0192 
0193 void ath9k_fatal_work(struct work_struct *work)
0194 {
0195     struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
0196                            fatal_work);
0197     struct ath_common *common = ath9k_hw_common(priv->ah);
0198 
0199     ath_dbg(common, FATAL, "FATAL Event received, resetting device\n");
0200     ath9k_htc_reset(priv);
0201 }
0202 
0203 static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
0204 {
0205     skb_pull(skb, sizeof(struct wmi_cmd_hdr));
0206 
0207     if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0)
0208         memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len);
0209 
0210     complete(&wmi->cmd_wait);
0211 }
0212 
0213 static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
0214                   enum htc_endpoint_id epid)
0215 {
0216     struct wmi *wmi = priv;
0217     struct wmi_cmd_hdr *hdr;
0218     unsigned long flags;
0219     u16 cmd_id;
0220 
0221     if (unlikely(wmi->stopped))
0222         goto free_skb;
0223 
0224     hdr = (struct wmi_cmd_hdr *) skb->data;
0225     cmd_id = be16_to_cpu(hdr->command_id);
0226 
0227     if (cmd_id & 0x1000) {
0228         spin_lock_irqsave(&wmi->wmi_lock, flags);
0229         __skb_queue_tail(&wmi->wmi_event_queue, skb);
0230         spin_unlock_irqrestore(&wmi->wmi_lock, flags);
0231         tasklet_schedule(&wmi->wmi_event_tasklet);
0232         return;
0233     }
0234 
0235     /* Check if there has been a timeout. */
0236     spin_lock_irqsave(&wmi->wmi_lock, flags);
0237     if (be16_to_cpu(hdr->seq_no) != wmi->last_seq_id) {
0238         spin_unlock_irqrestore(&wmi->wmi_lock, flags);
0239         goto free_skb;
0240     }
0241     spin_unlock_irqrestore(&wmi->wmi_lock, flags);
0242 
0243     /* WMI command response */
0244     ath9k_wmi_rsp_callback(wmi, skb);
0245 
0246 free_skb:
0247     kfree_skb(skb);
0248 }
0249 
0250 static void ath9k_wmi_ctrl_tx(void *priv, struct sk_buff *skb,
0251                   enum htc_endpoint_id epid, bool txok)
0252 {
0253     kfree_skb(skb);
0254 }
0255 
0256 int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
0257               enum htc_endpoint_id *wmi_ctrl_epid)
0258 {
0259     struct htc_service_connreq connect;
0260     int ret;
0261 
0262     wmi->htc = htc;
0263 
0264     memset(&connect, 0, sizeof(connect));
0265 
0266     connect.ep_callbacks.priv = wmi;
0267     connect.ep_callbacks.tx = ath9k_wmi_ctrl_tx;
0268     connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx;
0269     connect.service_id = WMI_CONTROL_SVC;
0270 
0271     ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid);
0272     if (ret)
0273         return ret;
0274 
0275     *wmi_ctrl_epid = wmi->ctrl_epid;
0276 
0277     return 0;
0278 }
0279 
0280 static int ath9k_wmi_cmd_issue(struct wmi *wmi,
0281                    struct sk_buff *skb,
0282                    enum wmi_cmd_id cmd, u16 len)
0283 {
0284     struct wmi_cmd_hdr *hdr;
0285     unsigned long flags;
0286 
0287     hdr = skb_push(skb, sizeof(struct wmi_cmd_hdr));
0288     hdr->command_id = cpu_to_be16(cmd);
0289     hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id);
0290 
0291     spin_lock_irqsave(&wmi->wmi_lock, flags);
0292     wmi->last_seq_id = wmi->tx_seq_id;
0293     spin_unlock_irqrestore(&wmi->wmi_lock, flags);
0294 
0295     return htc_send_epid(wmi->htc, skb, wmi->ctrl_epid);
0296 }
0297 
0298 int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
0299           u8 *cmd_buf, u32 cmd_len,
0300           u8 *rsp_buf, u32 rsp_len,
0301           u32 timeout)
0302 {
0303     struct ath_hw *ah = wmi->drv_priv->ah;
0304     struct ath_common *common = ath9k_hw_common(ah);
0305     u16 headroom = sizeof(struct htc_frame_hdr) +
0306                sizeof(struct wmi_cmd_hdr);
0307     struct sk_buff *skb;
0308     unsigned long time_left;
0309     int ret = 0;
0310 
0311     if (ah->ah_flags & AH_UNPLUGGED)
0312         return 0;
0313 
0314     skb = alloc_skb(headroom + cmd_len, GFP_ATOMIC);
0315     if (!skb)
0316         return -ENOMEM;
0317 
0318     skb_reserve(skb, headroom);
0319 
0320     if (cmd_len != 0 && cmd_buf != NULL) {
0321         skb_put_data(skb, cmd_buf, cmd_len);
0322     }
0323 
0324     mutex_lock(&wmi->op_mutex);
0325 
0326     /* check if wmi stopped flag is set */
0327     if (unlikely(wmi->stopped)) {
0328         ret = -EPROTO;
0329         goto out;
0330     }
0331 
0332     /* record the rsp buffer and length */
0333     wmi->cmd_rsp_buf = rsp_buf;
0334     wmi->cmd_rsp_len = rsp_len;
0335 
0336     ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
0337     if (ret)
0338         goto out;
0339 
0340     time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout);
0341     if (!time_left) {
0342         ath_dbg(common, WMI, "Timeout waiting for WMI command: %s\n",
0343             wmi_cmd_to_name(cmd_id));
0344         mutex_unlock(&wmi->op_mutex);
0345         return -ETIMEDOUT;
0346     }
0347 
0348     mutex_unlock(&wmi->op_mutex);
0349 
0350     return 0;
0351 
0352 out:
0353     ath_dbg(common, WMI, "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id));
0354     mutex_unlock(&wmi->op_mutex);
0355     kfree_skb(skb);
0356 
0357     return ret;
0358 }