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