12b27bdccSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
27b3115f2SLuciano Coelho /*
37b3115f2SLuciano Coelho * This file is part of wl1271
47b3115f2SLuciano Coelho *
57b3115f2SLuciano Coelho * Copyright (C) 2010 Nokia Corporation
67b3115f2SLuciano Coelho *
77b3115f2SLuciano Coelho * Contact: Luciano Coelho <luciano.coelho@nokia.com>
87b3115f2SLuciano Coelho */
97b3115f2SLuciano Coelho #include "testmode.h"
107b3115f2SLuciano Coelho
11fa2648a3STony Lindgren #include <linux/pm_runtime.h>
127b3115f2SLuciano Coelho #include <linux/slab.h>
137b3115f2SLuciano Coelho #include <net/genetlink.h>
147b3115f2SLuciano Coelho
15c31be25aSLuciano Coelho #include "wlcore.h"
167b3115f2SLuciano Coelho #include "debug.h"
177b3115f2SLuciano Coelho #include "acx.h"
187b3115f2SLuciano Coelho #include "io.h"
197b3115f2SLuciano Coelho
207b3115f2SLuciano Coelho #define WL1271_TM_MAX_DATA_LENGTH 1024
217b3115f2SLuciano Coelho
227b3115f2SLuciano Coelho enum wl1271_tm_commands {
237b3115f2SLuciano Coelho WL1271_TM_CMD_UNSPEC,
247b3115f2SLuciano Coelho WL1271_TM_CMD_TEST,
257b3115f2SLuciano Coelho WL1271_TM_CMD_INTERROGATE,
267b3115f2SLuciano Coelho WL1271_TM_CMD_CONFIGURE,
277b3115f2SLuciano Coelho WL1271_TM_CMD_NVS_PUSH, /* Not in use. Keep to not break ABI */
287b3115f2SLuciano Coelho WL1271_TM_CMD_SET_PLT_MODE,
29fd92dc5dSArik Nemtsov WL1271_TM_CMD_RECOVER, /* Not in use. Keep to not break ABI */
307b3115f2SLuciano Coelho WL1271_TM_CMD_GET_MAC,
317b3115f2SLuciano Coelho
327b3115f2SLuciano Coelho __WL1271_TM_CMD_AFTER_LAST
337b3115f2SLuciano Coelho };
347b3115f2SLuciano Coelho #define WL1271_TM_CMD_MAX (__WL1271_TM_CMD_AFTER_LAST - 1)
357b3115f2SLuciano Coelho
367b3115f2SLuciano Coelho enum wl1271_tm_attrs {
377b3115f2SLuciano Coelho WL1271_TM_ATTR_UNSPEC,
387b3115f2SLuciano Coelho WL1271_TM_ATTR_CMD_ID,
397b3115f2SLuciano Coelho WL1271_TM_ATTR_ANSWER,
407b3115f2SLuciano Coelho WL1271_TM_ATTR_DATA,
417b3115f2SLuciano Coelho WL1271_TM_ATTR_IE_ID,
427b3115f2SLuciano Coelho WL1271_TM_ATTR_PLT_MODE,
437b3115f2SLuciano Coelho
447b3115f2SLuciano Coelho __WL1271_TM_ATTR_AFTER_LAST
457b3115f2SLuciano Coelho };
467b3115f2SLuciano Coelho #define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1)
477b3115f2SLuciano Coelho
487b3115f2SLuciano Coelho static struct nla_policy wl1271_tm_policy[WL1271_TM_ATTR_MAX + 1] = {
497b3115f2SLuciano Coelho [WL1271_TM_ATTR_CMD_ID] = { .type = NLA_U32 },
507b3115f2SLuciano Coelho [WL1271_TM_ATTR_ANSWER] = { .type = NLA_U8 },
517b3115f2SLuciano Coelho [WL1271_TM_ATTR_DATA] = { .type = NLA_BINARY,
527b3115f2SLuciano Coelho .len = WL1271_TM_MAX_DATA_LENGTH },
537b3115f2SLuciano Coelho [WL1271_TM_ATTR_IE_ID] = { .type = NLA_U32 },
547b3115f2SLuciano Coelho [WL1271_TM_ATTR_PLT_MODE] = { .type = NLA_U32 },
557b3115f2SLuciano Coelho };
567b3115f2SLuciano Coelho
577b3115f2SLuciano Coelho
wl1271_tm_cmd_test(struct wl1271 * wl,struct nlattr * tb[])587b3115f2SLuciano Coelho static int wl1271_tm_cmd_test(struct wl1271 *wl, struct nlattr *tb[])
597b3115f2SLuciano Coelho {
607b3115f2SLuciano Coelho int buf_len, ret, len;
617b3115f2SLuciano Coelho struct sk_buff *skb;
627b3115f2SLuciano Coelho void *buf;
637b3115f2SLuciano Coelho u8 answer = 0;
647b3115f2SLuciano Coelho
657b3115f2SLuciano Coelho wl1271_debug(DEBUG_TESTMODE, "testmode cmd test");
667b3115f2SLuciano Coelho
677b3115f2SLuciano Coelho if (!tb[WL1271_TM_ATTR_DATA])
687b3115f2SLuciano Coelho return -EINVAL;
697b3115f2SLuciano Coelho
707b3115f2SLuciano Coelho buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
717b3115f2SLuciano Coelho buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
727b3115f2SLuciano Coelho
737b3115f2SLuciano Coelho if (tb[WL1271_TM_ATTR_ANSWER])
747b3115f2SLuciano Coelho answer = nla_get_u8(tb[WL1271_TM_ATTR_ANSWER]);
757b3115f2SLuciano Coelho
767b3115f2SLuciano Coelho if (buf_len > sizeof(struct wl1271_command))
777b3115f2SLuciano Coelho return -EMSGSIZE;
787b3115f2SLuciano Coelho
797b3115f2SLuciano Coelho mutex_lock(&wl->mutex);
807b3115f2SLuciano Coelho
814cc53383SIdo Yariv if (unlikely(wl->state != WLCORE_STATE_ON)) {
827b3115f2SLuciano Coelho ret = -EINVAL;
837b3115f2SLuciano Coelho goto out;
847b3115f2SLuciano Coelho }
857b3115f2SLuciano Coelho
86*3447eebeSMinghao Chi ret = pm_runtime_resume_and_get(wl->dev);
87*3447eebeSMinghao Chi if (ret < 0)
887b3115f2SLuciano Coelho goto out;
897b3115f2SLuciano Coelho
907b3115f2SLuciano Coelho ret = wl1271_cmd_test(wl, buf, buf_len, answer);
917b3115f2SLuciano Coelho if (ret < 0) {
927b3115f2SLuciano Coelho wl1271_warning("testmode cmd test failed: %d", ret);
937b3115f2SLuciano Coelho goto out_sleep;
947b3115f2SLuciano Coelho }
957b3115f2SLuciano Coelho
967b3115f2SLuciano Coelho if (answer) {
97b0b09e31SYair Shapira /* If we got bip calibration answer print radio status */
98b0b09e31SYair Shapira struct wl1271_cmd_cal_p2g *params =
99b0b09e31SYair Shapira (struct wl1271_cmd_cal_p2g *) buf;
100b0b09e31SYair Shapira
101b0b09e31SYair Shapira s16 radio_status = (s16) le16_to_cpu(params->radio_status);
102b0b09e31SYair Shapira
103b0b09e31SYair Shapira if (params->test.id == TEST_CMD_P2G_CAL &&
104b0b09e31SYair Shapira radio_status < 0)
105b0b09e31SYair Shapira wl1271_warning("testmode cmd: radio status=%d",
106b0b09e31SYair Shapira radio_status);
107b0b09e31SYair Shapira else
108b0b09e31SYair Shapira wl1271_info("testmode cmd: radio status=%d",
109b0b09e31SYair Shapira radio_status);
110b0b09e31SYair Shapira
1117b3115f2SLuciano Coelho len = nla_total_size(buf_len);
1127b3115f2SLuciano Coelho skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
1137b3115f2SLuciano Coelho if (!skb) {
1147b3115f2SLuciano Coelho ret = -ENOMEM;
1157b3115f2SLuciano Coelho goto out_sleep;
1167b3115f2SLuciano Coelho }
1177b3115f2SLuciano Coelho
118bf722d1dSYair Shapira if (nla_put(skb, WL1271_TM_ATTR_DATA, buf_len, buf)) {
119bf722d1dSYair Shapira kfree_skb(skb);
120bf722d1dSYair Shapira ret = -EMSGSIZE;
121bf722d1dSYair Shapira goto out_sleep;
122bf722d1dSYair Shapira }
123bf722d1dSYair Shapira
1247b3115f2SLuciano Coelho ret = cfg80211_testmode_reply(skb);
1257b3115f2SLuciano Coelho if (ret < 0)
1267b3115f2SLuciano Coelho goto out_sleep;
1277b3115f2SLuciano Coelho }
1287b3115f2SLuciano Coelho
1297b3115f2SLuciano Coelho out_sleep:
1309b71578dSTony Lindgren pm_runtime_mark_last_busy(wl->dev);
1319b71578dSTony Lindgren pm_runtime_put_autosuspend(wl->dev);
1327b3115f2SLuciano Coelho out:
1337b3115f2SLuciano Coelho mutex_unlock(&wl->mutex);
1347b3115f2SLuciano Coelho
1357b3115f2SLuciano Coelho return ret;
1367b3115f2SLuciano Coelho }
1377b3115f2SLuciano Coelho
wl1271_tm_cmd_interrogate(struct wl1271 * wl,struct nlattr * tb[])1387b3115f2SLuciano Coelho static int wl1271_tm_cmd_interrogate(struct wl1271 *wl, struct nlattr *tb[])
1397b3115f2SLuciano Coelho {
1407b3115f2SLuciano Coelho int ret;
1417b3115f2SLuciano Coelho struct wl1271_command *cmd;
1427b3115f2SLuciano Coelho struct sk_buff *skb;
1437b3115f2SLuciano Coelho u8 ie_id;
1447b3115f2SLuciano Coelho
1457b3115f2SLuciano Coelho wl1271_debug(DEBUG_TESTMODE, "testmode cmd interrogate");
1467b3115f2SLuciano Coelho
1477b3115f2SLuciano Coelho if (!tb[WL1271_TM_ATTR_IE_ID])
1487b3115f2SLuciano Coelho return -EINVAL;
1497b3115f2SLuciano Coelho
1507b3115f2SLuciano Coelho ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
1517b3115f2SLuciano Coelho
1527b3115f2SLuciano Coelho mutex_lock(&wl->mutex);
1537b3115f2SLuciano Coelho
1544cc53383SIdo Yariv if (unlikely(wl->state != WLCORE_STATE_ON)) {
1557b3115f2SLuciano Coelho ret = -EINVAL;
1567b3115f2SLuciano Coelho goto out;
1577b3115f2SLuciano Coelho }
1587b3115f2SLuciano Coelho
159*3447eebeSMinghao Chi ret = pm_runtime_resume_and_get(wl->dev);
160*3447eebeSMinghao Chi if (ret < 0)
1617b3115f2SLuciano Coelho goto out;
1627b3115f2SLuciano Coelho
1637b3115f2SLuciano Coelho cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
1647b3115f2SLuciano Coelho if (!cmd) {
1657b3115f2SLuciano Coelho ret = -ENOMEM;
1667b3115f2SLuciano Coelho goto out_sleep;
1677b3115f2SLuciano Coelho }
1687b3115f2SLuciano Coelho
1694b674144SIgal Chernobelsky ret = wl1271_cmd_interrogate(wl, ie_id, cmd,
1704b674144SIgal Chernobelsky sizeof(struct acx_header), sizeof(*cmd));
1717b3115f2SLuciano Coelho if (ret < 0) {
1727b3115f2SLuciano Coelho wl1271_warning("testmode cmd interrogate failed: %d", ret);
1737b3115f2SLuciano Coelho goto out_free;
1747b3115f2SLuciano Coelho }
1757b3115f2SLuciano Coelho
1767b3115f2SLuciano Coelho skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, sizeof(*cmd));
1777b3115f2SLuciano Coelho if (!skb) {
1787b3115f2SLuciano Coelho ret = -ENOMEM;
1797b3115f2SLuciano Coelho goto out_free;
1807b3115f2SLuciano Coelho }
1817b3115f2SLuciano Coelho
182bf722d1dSYair Shapira if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(*cmd), cmd)) {
183bf722d1dSYair Shapira kfree_skb(skb);
184bf722d1dSYair Shapira ret = -EMSGSIZE;
185bf722d1dSYair Shapira goto out_free;
186bf722d1dSYair Shapira }
187bf722d1dSYair Shapira
1887b3115f2SLuciano Coelho ret = cfg80211_testmode_reply(skb);
1897b3115f2SLuciano Coelho if (ret < 0)
1907b3115f2SLuciano Coelho goto out_free;
1917b3115f2SLuciano Coelho
1927b3115f2SLuciano Coelho out_free:
1937b3115f2SLuciano Coelho kfree(cmd);
1947b3115f2SLuciano Coelho out_sleep:
1959b71578dSTony Lindgren pm_runtime_mark_last_busy(wl->dev);
1969b71578dSTony Lindgren pm_runtime_put_autosuspend(wl->dev);
1977b3115f2SLuciano Coelho out:
1987b3115f2SLuciano Coelho mutex_unlock(&wl->mutex);
1997b3115f2SLuciano Coelho
2007b3115f2SLuciano Coelho return ret;
2017b3115f2SLuciano Coelho }
2027b3115f2SLuciano Coelho
wl1271_tm_cmd_configure(struct wl1271 * wl,struct nlattr * tb[])2037b3115f2SLuciano Coelho static int wl1271_tm_cmd_configure(struct wl1271 *wl, struct nlattr *tb[])
2047b3115f2SLuciano Coelho {
2057b3115f2SLuciano Coelho int buf_len, ret;
2067b3115f2SLuciano Coelho void *buf;
2077b3115f2SLuciano Coelho u8 ie_id;
2087b3115f2SLuciano Coelho
2097b3115f2SLuciano Coelho wl1271_debug(DEBUG_TESTMODE, "testmode cmd configure");
2107b3115f2SLuciano Coelho
2117b3115f2SLuciano Coelho if (!tb[WL1271_TM_ATTR_DATA])
2127b3115f2SLuciano Coelho return -EINVAL;
2137b3115f2SLuciano Coelho if (!tb[WL1271_TM_ATTR_IE_ID])
2147b3115f2SLuciano Coelho return -EINVAL;
2157b3115f2SLuciano Coelho
2167b3115f2SLuciano Coelho ie_id = nla_get_u8(tb[WL1271_TM_ATTR_IE_ID]);
2177b3115f2SLuciano Coelho buf = nla_data(tb[WL1271_TM_ATTR_DATA]);
2187b3115f2SLuciano Coelho buf_len = nla_len(tb[WL1271_TM_ATTR_DATA]);
2197b3115f2SLuciano Coelho
2207b3115f2SLuciano Coelho if (buf_len > sizeof(struct wl1271_command))
2217b3115f2SLuciano Coelho return -EMSGSIZE;
2227b3115f2SLuciano Coelho
2237b3115f2SLuciano Coelho mutex_lock(&wl->mutex);
2247b3115f2SLuciano Coelho ret = wl1271_cmd_configure(wl, ie_id, buf, buf_len);
2257b3115f2SLuciano Coelho mutex_unlock(&wl->mutex);
2267b3115f2SLuciano Coelho
2277b3115f2SLuciano Coelho if (ret < 0) {
2287b3115f2SLuciano Coelho wl1271_warning("testmode cmd configure failed: %d", ret);
2297b3115f2SLuciano Coelho return ret;
2307b3115f2SLuciano Coelho }
2317b3115f2SLuciano Coelho
2327b3115f2SLuciano Coelho return 0;
2337b3115f2SLuciano Coelho }
2347b3115f2SLuciano Coelho
wl1271_tm_detect_fem(struct wl1271 * wl,struct nlattr * tb[])235ff324317SYair Shapira static int wl1271_tm_detect_fem(struct wl1271 *wl, struct nlattr *tb[])
236ff324317SYair Shapira {
237ff324317SYair Shapira /* return FEM type */
238ff324317SYair Shapira int ret, len;
239ff324317SYair Shapira struct sk_buff *skb;
240ff324317SYair Shapira
241ff324317SYair Shapira ret = wl1271_plt_start(wl, PLT_FEM_DETECT);
242ff324317SYair Shapira if (ret < 0)
243ff324317SYair Shapira goto out;
244ff324317SYair Shapira
245ff324317SYair Shapira mutex_lock(&wl->mutex);
246ff324317SYair Shapira
247ff324317SYair Shapira len = nla_total_size(sizeof(wl->fem_manuf));
248ff324317SYair Shapira skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, len);
249ff324317SYair Shapira if (!skb) {
250ff324317SYair Shapira ret = -ENOMEM;
251ff324317SYair Shapira goto out_mutex;
252ff324317SYair Shapira }
253ff324317SYair Shapira
254ff324317SYair Shapira if (nla_put(skb, WL1271_TM_ATTR_DATA, sizeof(wl->fem_manuf),
255ff324317SYair Shapira &wl->fem_manuf)) {
256ff324317SYair Shapira kfree_skb(skb);
257ff324317SYair Shapira ret = -EMSGSIZE;
258ff324317SYair Shapira goto out_mutex;
259ff324317SYair Shapira }
260ff324317SYair Shapira
261ff324317SYair Shapira ret = cfg80211_testmode_reply(skb);
262ff324317SYair Shapira
263ff324317SYair Shapira out_mutex:
264ff324317SYair Shapira mutex_unlock(&wl->mutex);
265ff324317SYair Shapira
266ff324317SYair Shapira /* We always stop plt after DETECT mode */
267ff324317SYair Shapira wl1271_plt_stop(wl);
268ff324317SYair Shapira out:
269ff324317SYair Shapira return ret;
270ff324317SYair Shapira }
271ff324317SYair Shapira
wl1271_tm_cmd_set_plt_mode(struct wl1271 * wl,struct nlattr * tb[])2727b3115f2SLuciano Coelho static int wl1271_tm_cmd_set_plt_mode(struct wl1271 *wl, struct nlattr *tb[])
2737b3115f2SLuciano Coelho {
2747b3115f2SLuciano Coelho u32 val;
2757b3115f2SLuciano Coelho int ret;
2767b3115f2SLuciano Coelho
2777b3115f2SLuciano Coelho wl1271_debug(DEBUG_TESTMODE, "testmode cmd set plt mode");
2787b3115f2SLuciano Coelho
2797b3115f2SLuciano Coelho if (!tb[WL1271_TM_ATTR_PLT_MODE])
2807b3115f2SLuciano Coelho return -EINVAL;
2817b3115f2SLuciano Coelho
2827b3115f2SLuciano Coelho val = nla_get_u32(tb[WL1271_TM_ATTR_PLT_MODE]);
2837b3115f2SLuciano Coelho
2847b3115f2SLuciano Coelho switch (val) {
2857019c80eSYair Shapira case PLT_OFF:
2867b3115f2SLuciano Coelho ret = wl1271_plt_stop(wl);
2877b3115f2SLuciano Coelho break;
2887019c80eSYair Shapira case PLT_ON:
289dd491ffbSYair Shapira case PLT_CHIP_AWAKE:
290dd491ffbSYair Shapira ret = wl1271_plt_start(wl, val);
291ff324317SYair Shapira break;
2927019c80eSYair Shapira case PLT_FEM_DETECT:
293ff324317SYair Shapira ret = wl1271_tm_detect_fem(wl, tb);
2947b3115f2SLuciano Coelho break;
2957b3115f2SLuciano Coelho default:
2967b3115f2SLuciano Coelho ret = -EINVAL;
2977b3115f2SLuciano Coelho break;
2987b3115f2SLuciano Coelho }
2997b3115f2SLuciano Coelho
3007b3115f2SLuciano Coelho return ret;
3017b3115f2SLuciano Coelho }
3027b3115f2SLuciano Coelho
wl12xx_tm_cmd_get_mac(struct wl1271 * wl,struct nlattr * tb[])3037b3115f2SLuciano Coelho static int wl12xx_tm_cmd_get_mac(struct wl1271 *wl, struct nlattr *tb[])
3047b3115f2SLuciano Coelho {
3057b3115f2SLuciano Coelho struct sk_buff *skb;
3067b3115f2SLuciano Coelho u8 mac_addr[ETH_ALEN];
3077b3115f2SLuciano Coelho int ret = 0;
3087b3115f2SLuciano Coelho
3097b3115f2SLuciano Coelho mutex_lock(&wl->mutex);
3107b3115f2SLuciano Coelho
3117b3115f2SLuciano Coelho if (!wl->plt) {
3127b3115f2SLuciano Coelho ret = -EINVAL;
3137b3115f2SLuciano Coelho goto out;
3147b3115f2SLuciano Coelho }
3157b3115f2SLuciano Coelho
3167b3115f2SLuciano Coelho if (wl->fuse_oui_addr == 0 && wl->fuse_nic_addr == 0) {
3177b3115f2SLuciano Coelho ret = -EOPNOTSUPP;
3187b3115f2SLuciano Coelho goto out;
3197b3115f2SLuciano Coelho }
3207b3115f2SLuciano Coelho
3217b3115f2SLuciano Coelho mac_addr[0] = (u8)(wl->fuse_oui_addr >> 16);
3227b3115f2SLuciano Coelho mac_addr[1] = (u8)(wl->fuse_oui_addr >> 8);
3237b3115f2SLuciano Coelho mac_addr[2] = (u8) wl->fuse_oui_addr;
3247b3115f2SLuciano Coelho mac_addr[3] = (u8)(wl->fuse_nic_addr >> 16);
3257b3115f2SLuciano Coelho mac_addr[4] = (u8)(wl->fuse_nic_addr >> 8);
3267b3115f2SLuciano Coelho mac_addr[5] = (u8) wl->fuse_nic_addr;
3277b3115f2SLuciano Coelho
3287b3115f2SLuciano Coelho skb = cfg80211_testmode_alloc_reply_skb(wl->hw->wiphy, ETH_ALEN);
3297b3115f2SLuciano Coelho if (!skb) {
3307b3115f2SLuciano Coelho ret = -ENOMEM;
3317b3115f2SLuciano Coelho goto out;
3327b3115f2SLuciano Coelho }
3337b3115f2SLuciano Coelho
334bf722d1dSYair Shapira if (nla_put(skb, WL1271_TM_ATTR_DATA, ETH_ALEN, mac_addr)) {
335bf722d1dSYair Shapira kfree_skb(skb);
336bf722d1dSYair Shapira ret = -EMSGSIZE;
337bf722d1dSYair Shapira goto out;
338bf722d1dSYair Shapira }
339bf722d1dSYair Shapira
3407b3115f2SLuciano Coelho ret = cfg80211_testmode_reply(skb);
3417b3115f2SLuciano Coelho if (ret < 0)
3427b3115f2SLuciano Coelho goto out;
3437b3115f2SLuciano Coelho
3447b3115f2SLuciano Coelho out:
3457b3115f2SLuciano Coelho mutex_unlock(&wl->mutex);
3467b3115f2SLuciano Coelho return ret;
3477b3115f2SLuciano Coelho }
3487b3115f2SLuciano Coelho
wl1271_tm_cmd(struct ieee80211_hw * hw,struct ieee80211_vif * vif,void * data,int len)34952981cd7SDavid Spinadel int wl1271_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
35052981cd7SDavid Spinadel void *data, int len)
3517b3115f2SLuciano Coelho {
3527b3115f2SLuciano Coelho struct wl1271 *wl = hw->priv;
3537b3115f2SLuciano Coelho struct nlattr *tb[WL1271_TM_ATTR_MAX + 1];
354dd491ffbSYair Shapira u32 nla_cmd;
3557b3115f2SLuciano Coelho int err;
3567b3115f2SLuciano Coelho
3578cb08174SJohannes Berg err = nla_parse_deprecated(tb, WL1271_TM_ATTR_MAX, data, len,
3588cb08174SJohannes Berg wl1271_tm_policy, NULL);
3597b3115f2SLuciano Coelho if (err)
3607b3115f2SLuciano Coelho return err;
3617b3115f2SLuciano Coelho
3627b3115f2SLuciano Coelho if (!tb[WL1271_TM_ATTR_CMD_ID])
3637b3115f2SLuciano Coelho return -EINVAL;
3647b3115f2SLuciano Coelho
365dd491ffbSYair Shapira nla_cmd = nla_get_u32(tb[WL1271_TM_ATTR_CMD_ID]);
366dd491ffbSYair Shapira
367dd491ffbSYair Shapira /* Only SET_PLT_MODE is allowed in case of mode PLT_CHIP_AWAKE */
368dd491ffbSYair Shapira if (wl->plt_mode == PLT_CHIP_AWAKE &&
369dd491ffbSYair Shapira nla_cmd != WL1271_TM_CMD_SET_PLT_MODE)
370dd491ffbSYair Shapira return -EOPNOTSUPP;
371dd491ffbSYair Shapira
372dd491ffbSYair Shapira switch (nla_cmd) {
3737b3115f2SLuciano Coelho case WL1271_TM_CMD_TEST:
3747b3115f2SLuciano Coelho return wl1271_tm_cmd_test(wl, tb);
3757b3115f2SLuciano Coelho case WL1271_TM_CMD_INTERROGATE:
3767b3115f2SLuciano Coelho return wl1271_tm_cmd_interrogate(wl, tb);
3777b3115f2SLuciano Coelho case WL1271_TM_CMD_CONFIGURE:
3787b3115f2SLuciano Coelho return wl1271_tm_cmd_configure(wl, tb);
3797b3115f2SLuciano Coelho case WL1271_TM_CMD_SET_PLT_MODE:
3807b3115f2SLuciano Coelho return wl1271_tm_cmd_set_plt_mode(wl, tb);
3817b3115f2SLuciano Coelho case WL1271_TM_CMD_GET_MAC:
3827b3115f2SLuciano Coelho return wl12xx_tm_cmd_get_mac(wl, tb);
3837b3115f2SLuciano Coelho default:
3847b3115f2SLuciano Coelho return -EOPNOTSUPP;
3857b3115f2SLuciano Coelho }
3867b3115f2SLuciano Coelho }
387