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,
 183                                     sizeof(struct acx_header), sizeof(*cmd));
 184        if (ret < 0) {
 185                wl1271_warning("testmode cmd interrogate failed: %d", ret);
 186                goto out_free;
 187        }
 188
 189        skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd));
 190        if (!skb) {
 191                ret = -ENOMEM;
 192                goto out_free;
 193        }
 194
 195        if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd)) {
 196                kfree_skb(skb);
 197                ret = -EMSGSIZE;
 198                goto out_free;
 199        }
 200
 201        ret = cfg80211_testmode_reply(skb);
 202        if (ret < 0)
 203                goto out_free;
 204
 205out_free:
 206        kfree(cmd);
 207out_sleep:
 208        wl1271_ps_elp_sleep(wl);
 209out:
 210        mutex_unlock(&wl->mutex);
 211
 212        return ret;
 213}
 214
 215static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[])
 216{
 217        int buf_len, ret;
 218        void *buf;
 219        u8 ie_id;
 220
 221        wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure");
 222
 223        if (!tb[WL1271_TM_ATTR_DATA])
 224                return -EINVAL;
 225        if (!tb[WL1271_TM_ATTR_IE_ID])
 226                return -EINVAL;
 227
 228        ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
 229        buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
 230        buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
 231
 232        if (buf_len > sizeof(struct wl1271_command))
 233                return -EMSGSIZE;
 234
 235        mutex_lock(&wl->mutex);
 236        ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len);
 237        mutex_unlock(&wl->mutex);
 238
 239        if (ret < 0) {
 240                wl1271_warning("testmode cmd configure failed: %d", ret);
 241                return ret;
 242        }
 243
 244        return 0;
 245}
 246
 247static int wl1271_tm_detect_fem(struct wl1271 *wl, struct nlattr *tb[])
 248{
 249        /* return FEM type */
 250        int ret, len;
 251        struct sk_buff *skb;
 252
 253        ret = wl1271_plt_start(wl, PLT_FEM_DETECT);
 254        if (ret < 0)
 255                goto out;
 256
 257        mutex_lock(&wl->mutex);
 258
 259        len = nla_total_size(sizeof(wl->fem_manuf));
 260        skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
 261        if (!skb) {
 262                ret = -ENOMEM;
 263                goto out_mutex;
 264        }
 265
 266        if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(wl->fem_manuf),
 267                                              &wl->fem_manuf)) {
 268                kfree_skb(skb);
 269                ret = -EMSGSIZE;
 270                goto out_mutex;
 271        }
 272
 273        ret = cfg80211_testmode_reply(skb);
 274
 275out_mutex:
 276        mutex_unlock(&wl->mutex);
 277
 278        /* We always stop plt after DETECT mode */
 279        wl1271_plt_stop(wl);
 280out:
 281        return ret;
 282}
 283
 284static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[])
 285{
 286        u32 val;
 287        int ret;
 288
 289        wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode");
 290
 291        if (!tb[WL1271_TM_ATTR_PLT_MODE])
 292                return -EINVAL;
 293
 294        val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]);
 295
 296        switch (val) {
 297        case PLT_OFF:
 298                ret = wl1271_plt_stop(wl);
 299                break;
 300        case PLT_ON:
 301        case PLT_CHIP_AWAKE:
 302                ret = wl1271_plt_start(wl, val);
 303                break;
 304        case PLT_FEM_DETECT:
 305                ret = wl1271_tm_detect_fem(wl, tb);
 306                break;
 307        default:
 308                ret = -EINVAL;
 309                break;
 310        }
 311
 312        return ret;
 313}
 314
 315static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[])
 316{
 317        struct sk_buff *skb;
 318        u8 mac_addr[ETH_ALEN];
 319        int ret = 0;
 320
 321        mutex_lock(&wl->mutex);
 322
 323        if (!wl->plt) {
 324                ret = -EINVAL;
 325                goto out;
 326        }
 327
 328        if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) {
 329                ret = -EOPNOTSUPP;
 330                goto out;
 331        }
 332
 333        mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16);
 334        mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8);
 335        mac_addr[2] = (u8) wl->fuse_oui_addr;
 336        mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16);
 337        mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8);
 338        mac_addr[5] = (u8) wl->fuse_nic_addr;
 339
 340        skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN);
 341        if (!skb) {
 342                ret = -ENOMEM;
 343                goto out;
 344        }
 345
 346        if (nla_put(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr)) {
 347                kfree_skb(skb);
 348                ret = -EMSGSIZE;
 349                goto out;
 350        }
 351
 352        ret = cfg80211_testmode_reply(skb);
 353        if (ret < 0)
 354                goto out;
 355
 356out:
 357        mutex_unlock(&wl->mutex);
 358        return ret;
 359}
 360
 361int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
 362                  void *data, int len)
 363{
 364        struct wl1271 *wl = hw->priv;
 365        struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
 366        u32 nla_cmd;
 367        int err;
 368
 369        err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy);
 370        if (err)
 371                return err;
 372
 373        if (!tb[WL1271_TM_ATTR_CMD_ID])
 374                return -EINVAL;
 375
 376        nla_cmd = nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID]);
 377
 378        /* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */
 379        if (wl->plt_mode == PLT_CHIP_AWAKE &&
 380            nla_cmd != WL1271_TM_CMD_SET_PLT_MODE)
 381                return -EOPNOTSUPP;
 382
 383        switch (nla_cmd) {
 384        case WL1271_TM_CMD_TEST:
 385                return wl1271_tm_cmd_test(wl, tb);
 386        case WL1271_TM_CMD_INTERROGATE:
 387                return wl1271_tm_cmd_interrogate(wl, tb);
 388        case WL1271_TM_CMD_CONFIGURE:
 389                return wl1271_tm_cmd_configure(wl, tb);
 390        case WL1271_TM_CMD_SET_PLT_MODE:
 391                return wl1271_tm_cmd_set_plt_mode(wl, tb);
 392        case WL1271_TM_CMD_GET_MAC:
 393                return wl12xx_tm_cmd_get_mac(wl, tb);
 394        default:
 395                return -EOPNOTSUPP;
 396        }
 397}
 398