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