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 36 enum 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 50 enum 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 62 static 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 72 static 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 143 out_sleep: 144 wl1271_ps_elp_sleep(wl); 145 out: 146 mutex_unlock(&wl->mutex); 147 148 return ret; 149 } 150 151 static 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 204 out_free: 205 kfree(cmd); 206 out_sleep: 207 wl1271_ps_elp_sleep(wl); 208 out: 209 mutex_unlock(&wl->mutex); 210 211 return ret; 212 } 213 214 static 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 246 static 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 274 out_mutex: 275 mutex_unlock(&wl->mutex); 276 277 /* We always stop plt after DETECT mode */ 278 wl1271_plt_stop(wl); 279 out: 280 return ret; 281 } 282 283 static 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 313 static 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 354 out: 355 mutex_unlock(&wl->mutex); 356 return ret; 357 } 358 359 int 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