linux/drivers/net/wireless/ti/wlcore/testmode.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * This file is part of wl1271
   4 *
   5 * Copyright (C) 2010 Nokia Corporation
   6 *
   7 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
   8 */
   9#include "testmode.h"
  10
  11#include <linux/pm_runtime.h>
  12#include <linux/slab.h>
  13#include <net/genetlink.h>
  14
  15#include "wlcore.h"
  16#include "debug.h"
  17#include "acx.h"
  18#include "io.h"
  19
  20#define WL1271_TM_MAX_DATA_LENGTH 1024
  21
  22enum wl1271_tm_commands {
  23        WL1271_TM_CMD_UNSPEC,
  24        WL1271_TM_CMD_TEST,
  25        WL1271_TM_CMD_INTERROGATE,
  26        WL1271_TM_CMD_CONFIGURE,
  27        WL1271_TM_CMD_NVS_PUSH,         /* Not in use. Keep to not break ABI */
  28        WL1271_TM_CMD_SET_PLT_MODE,
  29        WL1271_TM_CMD_RECOVER,          /* Not in use. Keep to not break ABI */
  30        WL1271_TM_CMD_GET_MAC,
  31
  32        __WL1271_TM_CMD_AFTER_LAST
  33};
  34#define WL1271_TM_CMD_MAX (__WL1271_TM_CMD_AFTER_LAST - 1)
  35
  36enum wl1271_tm_attrs {
  37        WL1271_TM_ATTR_UNSPEC,
  38        WL1271_TM_ATTR_CMD_ID,
  39        WL1271_TM_ATTR_ANSWER,
  40        WL1271_TM_ATTR_DATA,
  41        WL1271_TM_ATTR_IE_ID,
  42        WL1271_TM_ATTR_PLT_MODE,
  43
  44        __WL1271_TM_ATTR_AFTER_LAST
  45};
  46#define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1)
  47
  48static struct nla_policy wl1271_tm_policy[WL1271_TM_ATTR_MAX + 1] = {
  49        [WL1271_TM_ATTR_CMD_ID] =       { .type = NLA_U32 },
  50        [WL1271_TM_ATTR_ANSWER] =       { .type = NLA_U8 },
  51        [WL1271_TM_ATTR_DATA] =         { .type = NLA_BINARY,
  52                                          .len = WL1271_TM_MAX_DATA_LENGTH },
  53        [WL1271_TM_ATTR_IE_ID] =        { .type = NLA_U32 },
  54        [WL1271_TM_ATTR_PLT_MODE] =     { .type = NLA_U32 },
  55};
  56
  57
  58static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
  59{
  60        int buf_len, ret, len;
  61        struct sk_buff *skb;
  62        void *buf;
  63        u8 answer = 0;
  64
  65        wl1271_debug(DEBUG_TESTMODE, "testmode cmd test");
  66
  67        if (!tb[WL1271_TM_ATTR_DATA])
  68                return -EINVAL;
  69
  70        buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
  71        buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
  72
  73        if (tb[WL1271_TM_ATTR_ANSWER])
  74                answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]);
  75
  76        if (buf_len > sizeof(struct wl1271_command))
  77                return -EMSGSIZE;
  78
  79        mutex_lock(&wl->mutex);
  80
  81        if (unlikely(wl->state != WLCORE_STATE_ON)) {
  82                ret = -EINVAL;
  83                goto out;
  84        }
  85
  86        ret = pm_runtime_get_sync(wl->dev);
  87        if (ret < 0) {
  88                pm_runtime_put_noidle(wl->dev);
  89                goto out;
  90        }
  91
  92        ret = wl1271_cmd_test(wl, buf, buf_len, answer);
  93        if (ret < 0) {
  94                wl1271_warning("testmode cmd test failed: %d", ret);
  95                goto out_sleep;
  96        }
  97
  98        if (answer) {
  99                /* If we got bip calibration answer print radio status */
 100                struct wl1271_cmd_cal_p2g *params =
 101                        (struct wl1271_cmd_cal_p2g *) buf;
 102
 103                s16 radio_status = (s16) le16_to_cpu(params->radio_status);
 104
 105                if (params->test.id == TEST_CMD_P2G_CAL &&
 106                    radio_status < 0)
 107                        wl1271_warning("testmode cmd: radio status=%d",
 108                                        radio_status);
 109                else
 110                        wl1271_info("testmode cmd: radio status=%d",
 111                                        radio_status);
 112
 113                len = nla_total_size(buf_len);
 114                skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
 115                if (!skb) {
 116                        ret = -ENOMEM;
 117                        goto out_sleep;
 118                }
 119
 120                if (nla_put(skb, WL1271_TM_ATTR_DATA, buf_len, buf)) {
 121                        kfree_skb(skb);
 122                        ret = -EMSGSIZE;
 123                        goto out_sleep;
 124                }
 125
 126                ret = cfg80211_testmode_reply(skb);
 127                if (ret < 0)
 128                        goto out_sleep;
 129        }
 130
 131out_sleep:
 132        pm_runtime_mark_last_busy(wl->dev);
 133        pm_runtime_put_autosuspend(wl->dev);
 134out:
 135        mutex_unlock(&wl->mutex);
 136
 137        return ret;
 138}
 139
 140static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
 141{
 142        int ret;
 143        struct wl1271_command *cmd;
 144        struct sk_buff *skb;
 145        u8 ie_id;
 146
 147        wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate");
 148
 149        if (!tb[WL1271_TM_ATTR_IE_ID])
 150                return -EINVAL;
 151
 152        ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
 153
 154        mutex_lock(&wl->mutex);
 155
 156        if (unlikely(wl->state != WLCORE_STATE_ON)) {
 157                ret = -EINVAL;
 158                goto out;
 159        }
 160
 161        ret = pm_runtime_get_sync(wl->dev);
 162        if (ret < 0) {
 163                pm_runtime_put_noidle(wl->dev);
 164                goto out;
 165        }
 166
 167        cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
 168        if (!cmd) {
 169                ret = -ENOMEM;
 170                goto out_sleep;
 171        }
 172
 173        ret = wl1271_cmd_interrogate(wl, ie_id, cmd,
 174                                     sizeof(struct acx_header), sizeof(*cmd));
 175        if (ret < 0) {
 176                wl1271_warning("testmode cmd interrogate failed: %d", ret);
 177                goto out_free;
 178        }
 179
 180        skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd));
 181        if (!skb) {
 182                ret = -ENOMEM;
 183                goto out_free;
 184        }
 185
 186        if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd)) {
 187                kfree_skb(skb);
 188                ret = -EMSGSIZE;
 189                goto out_free;
 190        }
 191
 192        ret = cfg80211_testmode_reply(skb);
 193        if (ret < 0)
 194                goto out_free;
 195
 196out_free:
 197        kfree(cmd);
 198out_sleep:
 199        pm_runtime_mark_last_busy(wl->dev);
 200        pm_runtime_put_autosuspend(wl->dev);
 201out:
 202        mutex_unlock(&wl->mutex);
 203
 204        return ret;
 205}
 206
 207static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[])
 208{
 209        int buf_len, ret;
 210        void *buf;
 211        u8 ie_id;
 212
 213        wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure");
 214
 215        if (!tb[WL1271_TM_ATTR_DATA])
 216                return -EINVAL;
 217        if (!tb[WL1271_TM_ATTR_IE_ID])
 218                return -EINVAL;
 219
 220        ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
 221        buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
 222        buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
 223
 224        if (buf_len > sizeof(struct wl1271_command))
 225                return -EMSGSIZE;
 226
 227        mutex_lock(&wl->mutex);
 228        ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len);
 229        mutex_unlock(&wl->mutex);
 230
 231        if (ret < 0) {
 232                wl1271_warning("testmode cmd configure failed: %d", ret);
 233                return ret;
 234        }
 235
 236        return 0;
 237}
 238
 239static int wl1271_tm_detect_fem(struct wl1271 *wl, struct nlattr *tb[])
 240{
 241        /* return FEM type */
 242        int ret, len;
 243        struct sk_buff *skb;
 244
 245        ret = wl1271_plt_start(wl, PLT_FEM_DETECT);
 246        if (ret < 0)
 247                goto out;
 248
 249        mutex_lock(&wl->mutex);
 250
 251        len = nla_total_size(sizeof(wl->fem_manuf));
 252        skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
 253        if (!skb) {
 254                ret = -ENOMEM;
 255                goto out_mutex;
 256        }
 257
 258        if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(wl->fem_manuf),
 259                                              &wl->fem_manuf)) {
 260                kfree_skb(skb);
 261                ret = -EMSGSIZE;
 262                goto out_mutex;
 263        }
 264
 265        ret = cfg80211_testmode_reply(skb);
 266
 267out_mutex:
 268        mutex_unlock(&wl->mutex);
 269
 270        /* We always stop plt after DETECT mode */
 271        wl1271_plt_stop(wl);
 272out:
 273        return ret;
 274}
 275
 276static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[])
 277{
 278        u32 val;
 279        int ret;
 280
 281        wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode");
 282
 283        if (!tb[WL1271_TM_ATTR_PLT_MODE])
 284                return -EINVAL;
 285
 286        val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]);
 287
 288        switch (val) {
 289        case PLT_OFF:
 290                ret = wl1271_plt_stop(wl);
 291                break;
 292        case PLT_ON:
 293        case PLT_CHIP_AWAKE:
 294                ret = wl1271_plt_start(wl, val);
 295                break;
 296        case PLT_FEM_DETECT:
 297                ret = wl1271_tm_detect_fem(wl, tb);
 298                break;
 299        default:
 300                ret = -EINVAL;
 301                break;
 302        }
 303
 304        return ret;
 305}
 306
 307static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[])
 308{
 309        struct sk_buff *skb;
 310        u8 mac_addr[ETH_ALEN];
 311        int ret = 0;
 312
 313        mutex_lock(&wl->mutex);
 314
 315        if (!wl->plt) {
 316                ret = -EINVAL;
 317                goto out;
 318        }
 319
 320        if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) {
 321                ret = -EOPNOTSUPP;
 322                goto out;
 323        }
 324
 325        mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16);
 326        mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8);
 327        mac_addr[2] = (u8) wl->fuse_oui_addr;
 328        mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16);
 329        mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8);
 330        mac_addr[5] = (u8) wl->fuse_nic_addr;
 331
 332        skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN);
 333        if (!skb) {
 334                ret = -ENOMEM;
 335                goto out;
 336        }
 337
 338        if (nla_put(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr)) {
 339                kfree_skb(skb);
 340                ret = -EMSGSIZE;
 341                goto out;
 342        }
 343
 344        ret = cfg80211_testmode_reply(skb);
 345        if (ret < 0)
 346                goto out;
 347
 348out:
 349        mutex_unlock(&wl->mutex);
 350        return ret;
 351}
 352
 353int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 354                  void *data, int len)
 355{
 356        struct wl1271 *wl = hw->priv;
 357        struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
 358        u32 nla_cmd;
 359        int err;
 360
 361        err = nla_parse_deprecated(tb, WL1271_TM_ATTR_MAX, data, len,
 362                                   wl1271_tm_policy, NULL);
 363        if (err)
 364                return err;
 365
 366        if (!tb[WL1271_TM_ATTR_CMD_ID])
 367                return -EINVAL;
 368
 369        nla_cmd = nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID]);
 370
 371        /* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */
 372        if (wl->plt_mode == PLT_CHIP_AWAKE &&
 373            nla_cmd != WL1271_TM_CMD_SET_PLT_MODE)
 374                return -EOPNOTSUPP;
 375
 376        switch (nla_cmd) {
 377        case WL1271_TM_CMD_TEST:
 378                return wl1271_tm_cmd_test(wl, tb);
 379        case WL1271_TM_CMD_INTERROGATE:
 380                return wl1271_tm_cmd_interrogate(wl, tb);
 381        case WL1271_TM_CMD_CONFIGURE:
 382                return wl1271_tm_cmd_configure(wl, tb);
 383        case WL1271_TM_CMD_SET_PLT_MODE:
 384                return wl1271_tm_cmd_set_plt_mode(wl, tb);
 385        case WL1271_TM_CMD_GET_MAC:
 386                return wl12xx_tm_cmd_get_mac(wl, tb);
 387        default:
 388                return -EOPNOTSUPP;
 389        }
 390}
 391