linux/drivers/net/wireless/ath/ath9k/wmi.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Atheros Communications Inc.
   3 *
   4 * Permission to use, copy, modify, and/or distribute this software for any
   5 * purpose with or without fee is hereby granted, provided that the above
   6 * copyright notice and this permission notice appear in all copies.
   7 *
   8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15 */
  16
  17#include "htc.h"
  18
  19static const char *wmi_cmd_to_name(enum wmi_cmd_id wmi_cmd)
  20{
  21        switch (wmi_cmd) {
  22        case WMI_ECHO_CMDID:
  23                return "WMI_ECHO_CMDID";
  24        case WMI_ACCESS_MEMORY_CMDID:
  25                return "WMI_ACCESS_MEMORY_CMDID";
  26        case WMI_DISABLE_INTR_CMDID:
  27                return "WMI_DISABLE_INTR_CMDID";
  28        case WMI_ENABLE_INTR_CMDID:
  29                return "WMI_ENABLE_INTR_CMDID";
  30        case WMI_RX_LINK_CMDID:
  31                return "WMI_RX_LINK_CMDID";
  32        case WMI_ATH_INIT_CMDID:
  33                return "WMI_ATH_INIT_CMDID";
  34        case WMI_ABORT_TXQ_CMDID:
  35                return "WMI_ABORT_TXQ_CMDID";
  36        case WMI_STOP_TX_DMA_CMDID:
  37                return "WMI_STOP_TX_DMA_CMDID";
  38        case WMI_STOP_DMA_RECV_CMDID:
  39                return "WMI_STOP_DMA_RECV_CMDID";
  40        case WMI_ABORT_TX_DMA_CMDID:
  41                return "WMI_ABORT_TX_DMA_CMDID";
  42        case WMI_DRAIN_TXQ_CMDID:
  43                return "WMI_DRAIN_TXQ_CMDID";
  44        case WMI_DRAIN_TXQ_ALL_CMDID:
  45                return "WMI_DRAIN_TXQ_ALL_CMDID";
  46        case WMI_START_RECV_CMDID:
  47                return "WMI_START_RECV_CMDID";
  48        case WMI_STOP_RECV_CMDID:
  49                return "WMI_STOP_RECV_CMDID";
  50        case WMI_FLUSH_RECV_CMDID:
  51                return "WMI_FLUSH_RECV_CMDID";
  52        case WMI_SET_MODE_CMDID:
  53                return "WMI_SET_MODE_CMDID";
  54        case WMI_RESET_CMDID:
  55                return "WMI_RESET_CMDID";
  56        case WMI_NODE_CREATE_CMDID:
  57                return "WMI_NODE_CREATE_CMDID";
  58        case WMI_NODE_REMOVE_CMDID:
  59                return "WMI_NODE_REMOVE_CMDID";
  60        case WMI_VAP_REMOVE_CMDID:
  61                return "WMI_VAP_REMOVE_CMDID";
  62        case WMI_VAP_CREATE_CMDID:
  63                return "WMI_VAP_CREATE_CMDID";
  64        case WMI_BEACON_UPDATE_CMDID:
  65                return "WMI_BEACON_UPDATE_CMDID";
  66        case WMI_REG_READ_CMDID:
  67                return "WMI_REG_READ_CMDID";
  68        case WMI_REG_WRITE_CMDID:
  69                return "WMI_REG_WRITE_CMDID";
  70        case WMI_RC_STATE_CHANGE_CMDID:
  71                return "WMI_RC_STATE_CHANGE_CMDID";
  72        case WMI_RC_RATE_UPDATE_CMDID:
  73                return "WMI_RC_RATE_UPDATE_CMDID";
  74        case WMI_DEBUG_INFO_CMDID:
  75                return "WMI_DEBUG_INFO_CMDID";
  76        case WMI_HOST_ATTACH:
  77                return "WMI_HOST_ATTACH";
  78        case WMI_TARGET_IC_UPDATE_CMDID:
  79                return "WMI_TARGET_IC_UPDATE_CMDID";
  80        case WMI_TGT_STATS_CMDID:
  81                return "WMI_TGT_STATS_CMDID";
  82        case WMI_TX_AGGR_ENABLE_CMDID:
  83                return "WMI_TX_AGGR_ENABLE_CMDID";
  84        case WMI_TGT_DETACH_CMDID:
  85                return "WMI_TGT_DETACH_CMDID";
  86        case WMI_TGT_TXQ_ENABLE_CMDID:
  87                return "WMI_TGT_TXQ_ENABLE_CMDID";
  88        case WMI_AGGR_LIMIT_CMD:
  89                return "WMI_AGGR_LIMIT_CMD";
  90        }
  91
  92        return "Bogus";
  93}
  94
  95struct wmi *ath9k_init_wmi(struct ath9k_htc_priv *priv)
  96{
  97        struct wmi *wmi;
  98
  99        wmi = kzalloc(sizeof(struct wmi), GFP_KERNEL);
 100        if (!wmi)
 101                return NULL;
 102
 103        wmi->drv_priv = priv;
 104        wmi->stopped = false;
 105        mutex_init(&wmi->op_mutex);
 106        mutex_init(&wmi->multi_write_mutex);
 107        init_completion(&wmi->cmd_wait);
 108
 109        return wmi;
 110}
 111
 112void ath9k_deinit_wmi(struct ath9k_htc_priv *priv)
 113{
 114        struct wmi *wmi = priv->wmi;
 115
 116        mutex_lock(&wmi->op_mutex);
 117        wmi->stopped = true;
 118        mutex_unlock(&wmi->op_mutex);
 119
 120        kfree(priv->wmi);
 121}
 122
 123void ath9k_swba_tasklet(unsigned long data)
 124{
 125        struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data;
 126        struct ath_common *common = ath9k_hw_common(priv->ah);
 127
 128        ath_dbg(common, ATH_DBG_WMI, "SWBA Event received\n");
 129
 130        ath9k_htc_swba(priv, priv->wmi->beacon_pending);
 131
 132}
 133
 134void ath9k_fatal_work(struct work_struct *work)
 135{
 136        struct ath9k_htc_priv *priv = container_of(work, struct ath9k_htc_priv,
 137                                                   fatal_work);
 138        struct ath_common *common = ath9k_hw_common(priv->ah);
 139
 140        ath_dbg(common, ATH_DBG_FATAL, "FATAL Event received, resetting device\n");
 141        ath9k_htc_reset(priv);
 142}
 143
 144static void ath9k_wmi_rsp_callback(struct wmi *wmi, struct sk_buff *skb)
 145{
 146        skb_pull(skb, sizeof(struct wmi_cmd_hdr));
 147
 148        if (wmi->cmd_rsp_buf != NULL && wmi->cmd_rsp_len != 0)
 149                memcpy(wmi->cmd_rsp_buf, skb->data, wmi->cmd_rsp_len);
 150
 151        complete(&wmi->cmd_wait);
 152}
 153
 154static void ath9k_wmi_ctrl_rx(void *priv, struct sk_buff *skb,
 155                              enum htc_endpoint_id epid)
 156{
 157        struct wmi *wmi = (struct wmi *) priv;
 158        struct wmi_cmd_hdr *hdr;
 159        u16 cmd_id;
 160        void *wmi_event;
 161#ifdef CONFIG_ATH9K_HTC_DEBUGFS
 162        __be32 txrate;
 163#endif
 164
 165        if (unlikely(wmi->stopped))
 166                goto free_skb;
 167
 168        hdr = (struct wmi_cmd_hdr *) skb->data;
 169        cmd_id = be16_to_cpu(hdr->command_id);
 170
 171        if (cmd_id & 0x1000) {
 172                wmi_event = skb_pull(skb, sizeof(struct wmi_cmd_hdr));
 173                switch (cmd_id) {
 174                case WMI_SWBA_EVENTID:
 175                        wmi->beacon_pending = *(u8 *)wmi_event;
 176                        tasklet_schedule(&wmi->drv_priv->swba_tasklet);
 177                        break;
 178                case WMI_FATAL_EVENTID:
 179                        ieee80211_queue_work(wmi->drv_priv->hw,
 180                                             &wmi->drv_priv->fatal_work);
 181                        break;
 182                case WMI_TXRATE_EVENTID:
 183#ifdef CONFIG_ATH9K_HTC_DEBUGFS
 184                        txrate = ((struct wmi_event_txrate *)wmi_event)->txrate;
 185                        wmi->drv_priv->debug.txrate = be32_to_cpu(txrate);
 186#endif
 187                        break;
 188                default:
 189                        break;
 190                }
 191                kfree_skb(skb);
 192                return;
 193        }
 194
 195        /* Check if there has been a timeout. */
 196        spin_lock(&wmi->wmi_lock);
 197        if (cmd_id != wmi->last_cmd_id) {
 198                spin_unlock(&wmi->wmi_lock);
 199                goto free_skb;
 200        }
 201        spin_unlock(&wmi->wmi_lock);
 202
 203        /* WMI command response */
 204        ath9k_wmi_rsp_callback(wmi, skb);
 205
 206free_skb:
 207        kfree_skb(skb);
 208}
 209
 210static void ath9k_wmi_ctrl_tx(void *priv, struct sk_buff *skb,
 211                              enum htc_endpoint_id epid, bool txok)
 212{
 213        kfree_skb(skb);
 214}
 215
 216int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
 217                      enum htc_endpoint_id *wmi_ctrl_epid)
 218{
 219        struct htc_service_connreq connect;
 220        int ret;
 221
 222        wmi->htc = htc;
 223
 224        memset(&connect, 0, sizeof(connect));
 225
 226        connect.ep_callbacks.priv = wmi;
 227        connect.ep_callbacks.tx = ath9k_wmi_ctrl_tx;
 228        connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx;
 229        connect.service_id = WMI_CONTROL_SVC;
 230
 231        ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid);
 232        if (ret)
 233                return ret;
 234
 235        *wmi_ctrl_epid = wmi->ctrl_epid;
 236
 237        return 0;
 238}
 239
 240static int ath9k_wmi_cmd_issue(struct wmi *wmi,
 241                               struct sk_buff *skb,
 242                               enum wmi_cmd_id cmd, u16 len)
 243{
 244        struct wmi_cmd_hdr *hdr;
 245
 246        hdr = (struct wmi_cmd_hdr *) skb_push(skb, sizeof(struct wmi_cmd_hdr));
 247        hdr->command_id = cpu_to_be16(cmd);
 248        hdr->seq_no = cpu_to_be16(++wmi->tx_seq_id);
 249
 250        return htc_send(wmi->htc, skb, wmi->ctrl_epid, NULL);
 251}
 252
 253int ath9k_wmi_cmd(struct wmi *wmi, enum wmi_cmd_id cmd_id,
 254                  u8 *cmd_buf, u32 cmd_len,
 255                  u8 *rsp_buf, u32 rsp_len,
 256                  u32 timeout)
 257{
 258        struct ath_hw *ah = wmi->drv_priv->ah;
 259        struct ath_common *common = ath9k_hw_common(ah);
 260        u16 headroom = sizeof(struct htc_frame_hdr) +
 261                       sizeof(struct wmi_cmd_hdr);
 262        struct sk_buff *skb;
 263        u8 *data;
 264        int time_left, ret = 0;
 265        unsigned long flags;
 266
 267        if (ah->ah_flags & AH_UNPLUGGED)
 268                return 0;
 269
 270        skb = alloc_skb(headroom + cmd_len, GFP_ATOMIC);
 271        if (!skb)
 272                return -ENOMEM;
 273
 274        skb_reserve(skb, headroom);
 275
 276        if (cmd_len != 0 && cmd_buf != NULL) {
 277                data = (u8 *) skb_put(skb, cmd_len);
 278                memcpy(data, cmd_buf, cmd_len);
 279        }
 280
 281        mutex_lock(&wmi->op_mutex);
 282
 283        /* check if wmi stopped flag is set */
 284        if (unlikely(wmi->stopped)) {
 285                ret = -EPROTO;
 286                goto out;
 287        }
 288
 289        /* record the rsp buffer and length */
 290        wmi->cmd_rsp_buf = rsp_buf;
 291        wmi->cmd_rsp_len = rsp_len;
 292
 293        spin_lock_irqsave(&wmi->wmi_lock, flags);
 294        wmi->last_cmd_id = cmd_id;
 295        spin_unlock_irqrestore(&wmi->wmi_lock, flags);
 296
 297        ret = ath9k_wmi_cmd_issue(wmi, skb, cmd_id, cmd_len);
 298        if (ret)
 299                goto out;
 300
 301        time_left = wait_for_completion_timeout(&wmi->cmd_wait, timeout);
 302        if (!time_left) {
 303                ath_dbg(common, ATH_DBG_WMI,
 304                        "Timeout waiting for WMI command: %s\n",
 305                        wmi_cmd_to_name(cmd_id));
 306                mutex_unlock(&wmi->op_mutex);
 307                return -ETIMEDOUT;
 308        }
 309
 310        mutex_unlock(&wmi->op_mutex);
 311
 312        return 0;
 313
 314out:
 315        ath_dbg(common, ATH_DBG_WMI,
 316                "WMI failure for: %s\n", wmi_cmd_to_name(cmd_id));
 317        mutex_unlock(&wmi->op_mutex);
 318        kfree_skb(skb);
 319
 320        return ret;
 321}
 322