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, 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 (wl->state == WL1271_STATE_OFF) { 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 len = nla_total_size(buf_len); 112 skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); 113 if (!skb) { 114 ret = -ENOMEM; 115 goto out_sleep; 116 } 117 118 if (nla_put(skb, WL1271_TM_ATTR_DATA, buf_len, buf)) 119 goto nla_put_failure; 120 ret = cfg80211_testmode_reply(skb); 121 if (ret < 0) 122 goto out_sleep; 123 } 124 125 out_sleep: 126 wl1271_ps_elp_sleep(wl); 127 out: 128 mutex_unlock(&wl->mutex); 129 130 return ret; 131 132 nla_put_failure: 133 kfree_skb(skb); 134 ret = -EMSGSIZE; 135 goto out_sleep; 136 } 137 138 static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) 139 { 140 int ret; 141 struct wl1271_command *cmd; 142 struct sk_buff *skb; 143 u8 ie_id; 144 145 wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate"); 146 147 if (!tb[WL1271_TM_ATTR_IE_ID]) 148 return -EINVAL; 149 150 ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); 151 152 mutex_lock(&wl->mutex); 153 154 if (wl->state == WL1271_STATE_OFF) { 155 ret = -EINVAL; 156 goto out; 157 } 158 159 ret = wl1271_ps_elp_wakeup(wl); 160 if (ret < 0) 161 goto out; 162 163 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 164 if (!cmd) { 165 ret = -ENOMEM; 166 goto out_sleep; 167 } 168 169 ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); 170 if (ret < 0) { 171 wl1271_warning("testmode cmd interrogate failed: %d", ret); 172 goto out_free; 173 } 174 175 skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd)); 176 if (!skb) { 177 ret = -ENOMEM; 178 goto out_free; 179 } 180 181 if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd)) 182 goto nla_put_failure; 183 ret = cfg80211_testmode_reply(skb); 184 if (ret < 0) 185 goto out_free; 186 187 out_free: 188 kfree(cmd); 189 out_sleep: 190 wl1271_ps_elp_sleep(wl); 191 out: 192 mutex_unlock(&wl->mutex); 193 194 return ret; 195 196 nla_put_failure: 197 kfree_skb(skb); 198 ret = -EMSGSIZE; 199 goto out_free; 200 } 201 202 static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[]) 203 { 204 int buf_len, ret; 205 void *buf; 206 u8 ie_id; 207 208 wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure"); 209 210 if (!tb[WL1271_TM_ATTR_DATA]) 211 return -EINVAL; 212 if (!tb[WL1271_TM_ATTR_IE_ID]) 213 return -EINVAL; 214 215 ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); 216 buf = nla_data(tb[WL1271_TM_ATTR_DATA]); 217 buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); 218 219 if (buf_len > sizeof(struct wl1271_command)) 220 return -EMSGSIZE; 221 222 mutex_lock(&wl->mutex); 223 ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len); 224 mutex_unlock(&wl->mutex); 225 226 if (ret < 0) { 227 wl1271_warning("testmode cmd configure failed: %d", ret); 228 return ret; 229 } 230 231 return 0; 232 } 233 234 static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[]) 235 { 236 u32 val; 237 int ret; 238 239 wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode"); 240 241 if (!tb[WL1271_TM_ATTR_PLT_MODE]) 242 return -EINVAL; 243 244 val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]); 245 246 switch (val) { 247 case 0: 248 ret = wl1271_plt_stop(wl); 249 break; 250 case 1: 251 ret = wl1271_plt_start(wl); 252 break; 253 default: 254 ret = -EINVAL; 255 break; 256 } 257 258 return ret; 259 } 260 261 static int wl1271_tm_cmd_recover(struct wl1271 *wl, struct nlattr *tb[]) 262 { 263 wl1271_debug(DEBUG_TESTMODE, "testmode cmd recover"); 264 265 wl12xx_queue_recovery_work(wl); 266 267 return 0; 268 } 269 270 static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[]) 271 { 272 struct sk_buff *skb; 273 u8 mac_addr[ETH_ALEN]; 274 int ret = 0; 275 276 mutex_lock(&wl->mutex); 277 278 if (!wl->plt) { 279 ret = -EINVAL; 280 goto out; 281 } 282 283 if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) { 284 ret = -EOPNOTSUPP; 285 goto out; 286 } 287 288 mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16); 289 mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8); 290 mac_addr[2] = (u8) wl->fuse_oui_addr; 291 mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16); 292 mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8); 293 mac_addr[5] = (u8) wl->fuse_nic_addr; 294 295 skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN); 296 if (!skb) { 297 ret = -ENOMEM; 298 goto out; 299 } 300 301 if (nla_put(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr)) 302 goto nla_put_failure; 303 ret = cfg80211_testmode_reply(skb); 304 if (ret < 0) 305 goto out; 306 307 out: 308 mutex_unlock(&wl->mutex); 309 return ret; 310 311 nla_put_failure: 312 kfree_skb(skb); 313 ret = -EMSGSIZE; 314 goto out; 315 } 316 317 int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) 318 { 319 struct wl1271 *wl = hw->priv; 320 struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; 321 int err; 322 323 err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); 324 if (err) 325 return err; 326 327 if (!tb[WL1271_TM_ATTR_CMD_ID]) 328 return -EINVAL; 329 330 switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) { 331 case WL1271_TM_CMD_TEST: 332 return wl1271_tm_cmd_test(wl, tb); 333 case WL1271_TM_CMD_INTERROGATE: 334 return wl1271_tm_cmd_interrogate(wl, tb); 335 case WL1271_TM_CMD_CONFIGURE: 336 return wl1271_tm_cmd_configure(wl, tb); 337 case WL1271_TM_CMD_SET_PLT_MODE: 338 return wl1271_tm_cmd_set_plt_mode(wl, tb); 339 case WL1271_TM_CMD_RECOVER: 340 return wl1271_tm_cmd_recover(wl, tb); 341 case WL1271_TM_CMD_GET_MAC: 342 return wl12xx_tm_cmd_get_mac(wl, tb); 343 default: 344 return -EOPNOTSUPP; 345 } 346 } 347