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 "wl1271_testmode.h" 24 25#include <linux/slab.h> 26#include <net/genetlink.h> 27 28#include "wl1271.h" 29#include "wl1271_acx.h" 30 31#define WL1271_TM_MAX_DATA_LENGTH 1024 32 33enum wl1271_tm_commands { 34 WL1271_TM_CMD_UNSPEC, 35 WL1271_TM_CMD_TEST, 36 WL1271_TM_CMD_INTERROGATE, 37 WL1271_TM_CMD_CONFIGURE, 38 WL1271_TM_CMD_NVS_PUSH, 39 WL1271_TM_CMD_SET_PLT_MODE, 40 41 __WL1271_TM_CMD_AFTER_LAST 42}; 43#define WL1271_TM_CMD_MAX (__WL1271_TM_CMD_AFTER_LAST - 1) 44 45enum wl1271_tm_attrs { 46 WL1271_TM_ATTR_UNSPEC, 47 WL1271_TM_ATTR_CMD_ID, 48 WL1271_TM_ATTR_ANSWER, 49 WL1271_TM_ATTR_DATA, 50 WL1271_TM_ATTR_IE_ID, 51 WL1271_TM_ATTR_PLT_MODE, 52 53 __WL1271_TM_ATTR_AFTER_LAST 54}; 55#define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1) 56 57static struct nla_policy wl1271_tm_policy[WL1271_TM_ATTR_MAX + 1] = { 58 [WL1271_TM_ATTR_CMD_ID] = { .type = NLA_U32 }, 59 [WL1271_TM_ATTR_ANSWER] = { .type = NLA_U8 }, 60 [WL1271_TM_ATTR_DATA] = { .type = NLA_BINARY, 61 .len = WL1271_TM_MAX_DATA_LENGTH }, 62 [WL1271_TM_ATTR_IE_ID] = { .type = NLA_U32 }, 63 [WL1271_TM_ATTR_PLT_MODE] = { .type = NLA_U32 }, 64}; 65 66 67static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[]) 68{ 69 int buf_len, ret, len; 70 struct sk_buff *skb; 71 void *buf; 72 u8 answer = 0; 73 74 wl1271_debug(DEBUG_TESTMODE, "testmode cmd test"); 75 76 if (!tb[WL1271_TM_ATTR_DATA]) 77 return -EINVAL; 78 79 buf = nla_data(tb[WL1271_TM_ATTR_DATA]); 80 buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); 81 82 if (tb[WL1271_TM_ATTR_ANSWER]) 83 answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]); 84 85 if (buf_len > sizeof(struct wl1271_command)) 86 return -EMSGSIZE; 87 88 mutex_lock(&wl->mutex); 89 ret = wl1271_cmd_test(wl, buf, buf_len, answer); 90 mutex_unlock(&wl->mutex); 91 92 if (ret < 0) { 93 wl1271_warning("testmode cmd test failed: %d", ret); 94 return ret; 95 } 96 97 if (answer) { 98 len = nla_total_size(buf_len); 99 skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len); 100 if (!skb) 101 return -ENOMEM; 102 103 NLA_PUT(skb, WL1271_TM_ATTR_DATA, buf_len, buf); 104 ret = cfg80211_testmode_reply(skb); 105 if (ret < 0) 106 return ret; 107 } 108 109 return 0; 110 111nla_put_failure: 112 kfree_skb(skb); 113 return -EMSGSIZE; 114} 115 116static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[]) 117{ 118 int ret; 119 struct wl1271_command *cmd; 120 struct sk_buff *skb; 121 u8 ie_id; 122 123 wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate"); 124 125 if (!tb[WL1271_TM_ATTR_IE_ID]) 126 return -EINVAL; 127 128 ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); 129 130 cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); 131 if (!cmd) 132 return -ENOMEM; 133 134 mutex_lock(&wl->mutex); 135 ret = wl1271_cmd_interrogate(wl, ie_id, cmd, sizeof(*cmd)); 136 mutex_unlock(&wl->mutex); 137 138 if (ret < 0) { 139 wl1271_warning("testmode cmd interrogate failed: %d", ret); 140 return ret; 141 } 142 143 skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd)); 144 if (!skb) 145 return -ENOMEM; 146 147 NLA_PUT(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd); 148 149 return 0; 150 151nla_put_failure: 152 kfree_skb(skb); 153 return -EMSGSIZE; 154} 155 156static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[]) 157{ 158 int buf_len, ret; 159 void *buf; 160 u8 ie_id; 161 162 wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure"); 163 164 if (!tb[WL1271_TM_ATTR_DATA]) 165 return -EINVAL; 166 if (!tb[WL1271_TM_ATTR_IE_ID]) 167 return -EINVAL; 168 169 ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]); 170 buf = nla_data(tb[WL1271_TM_ATTR_DATA]); 171 buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]); 172 173 if (buf_len > sizeof(struct wl1271_command)) 174 return -EMSGSIZE; 175 176 mutex_lock(&wl->mutex); 177 ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len); 178 mutex_unlock(&wl->mutex); 179 180 if (ret < 0) { 181 wl1271_warning("testmode cmd configure failed: %d", ret); 182 return ret; 183 } 184 185 return 0; 186} 187 188static int wl1271_tm_cmd_nvs_push(struct wl1271 *wl, struct nlattr *tb[]) 189{ 190 int ret = 0; 191 size_t len; 192 void *buf; 193 194 wl1271_debug(DEBUG_TESTMODE, "testmode cmd nvs push"); 195 196 if (!tb[WL1271_TM_ATTR_DATA]) 197 return -EINVAL; 198 199 buf = nla_data(tb[WL1271_TM_ATTR_DATA]); 200 len = nla_len(tb[WL1271_TM_ATTR_DATA]); 201 202 if (len != sizeof(struct wl1271_nvs_file) && 203 (len != WL1271_INI_LEGACY_NVS_FILE_SIZE || 204 wl1271_11a_enabled())) { 205 wl1271_error("nvs size is not as expected: %zu != %zu", 206 len, sizeof(struct wl1271_nvs_file)); 207 return -EMSGSIZE; 208 } 209 210 mutex_lock(&wl->mutex); 211 212 kfree(wl->nvs); 213 214 wl->nvs = kzalloc(sizeof(struct wl1271_nvs_file), GFP_KERNEL); 215 if (!wl->nvs) { 216 wl1271_error("could not allocate memory for the nvs file"); 217 ret = -ENOMEM; 218 goto out; 219 } 220 221 memcpy(wl->nvs, buf, len); 222 223 wl1271_debug(DEBUG_TESTMODE, "testmode pushed nvs"); 224 225out: 226 mutex_unlock(&wl->mutex); 227 228 return ret; 229} 230 231static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[]) 232{ 233 u32 val; 234 int ret; 235 236 wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode"); 237 238 if (!tb[WL1271_TM_ATTR_PLT_MODE]) 239 return -EINVAL; 240 241 val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]); 242 243 switch (val) { 244 case 0: 245 ret = wl1271_plt_stop(wl); 246 break; 247 case 1: 248 ret = wl1271_plt_start(wl); 249 break; 250 default: 251 ret = -EINVAL; 252 break; 253 } 254 255 return ret; 256} 257 258int wl1271_tm_cmd(struct ieee80211_hw *hw, void *data, int len) 259{ 260 struct wl1271 *wl = hw->priv; 261 struct nlattr *tb[WL1271_TM_ATTR_MAX + 1]; 262 int err; 263 264 err = nla_parse(tb, WL1271_TM_ATTR_MAX, data, len, wl1271_tm_policy); 265 if (err) 266 return err; 267 268 if (!tb[WL1271_TM_ATTR_CMD_ID]) 269 return -EINVAL; 270 271 switch (nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID])) { 272 case WL1271_TM_CMD_TEST: 273 return wl1271_tm_cmd_test(wl, tb); 274 case WL1271_TM_CMD_INTERROGATE: 275 return wl1271_tm_cmd_interrogate(wl, tb); 276 case WL1271_TM_CMD_CONFIGURE: 277 return wl1271_tm_cmd_configure(wl, tb); 278 case WL1271_TM_CMD_NVS_PUSH: 279 return wl1271_tm_cmd_nvs_push(wl, tb); 280 case WL1271_TM_CMD_SET_PLT_MODE: 281 return wl1271_tm_cmd_set_plt_mode(wl, tb); 282 default: 283 return -EOPNOTSUPP; 284 } 285} 286