1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * This file is part of wlcore
4 *
5 * Copyright (C) 2014 Texas Instruments. All rights reserved.
6 */
7
8 #include <linux/pm_runtime.h>
9
10 #include <net/mac80211.h>
11 #include <net/netlink.h>
12
13 #include "wlcore.h"
14 #include "debug.h"
15 #include "hw_ops.h"
16 #include "vendor_cmd.h"
17
18 static const
19 struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = {
20 [WLCORE_VENDOR_ATTR_FREQ] = { .type = NLA_U32 },
21 [WLCORE_VENDOR_ATTR_GROUP_ID] = { .type = NLA_U32 },
22 [WLCORE_VENDOR_ATTR_GROUP_KEY] = { .type = NLA_BINARY,
23 .len = WLAN_MAX_KEY_LEN },
24 };
25
26 static int
wlcore_vendor_cmd_smart_config_start(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)27 wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
28 struct wireless_dev *wdev,
29 const void *data, int data_len)
30 {
31 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
32 struct wl1271 *wl = hw->priv;
33 struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
34 int ret;
35
36 wl1271_debug(DEBUG_CMD, "vendor cmd smart config start");
37
38 if (!data)
39 return -EINVAL;
40
41 ret = nla_parse_deprecated(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
42 wlcore_vendor_attr_policy, NULL);
43 if (ret)
44 return ret;
45
46 if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID])
47 return -EINVAL;
48
49 mutex_lock(&wl->mutex);
50
51 if (unlikely(wl->state != WLCORE_STATE_ON)) {
52 ret = -EINVAL;
53 goto out;
54 }
55
56 ret = pm_runtime_get_sync(wl->dev);
57 if (ret < 0) {
58 pm_runtime_put_noidle(wl->dev);
59 goto out;
60 }
61
62 ret = wlcore_smart_config_start(wl,
63 nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
64
65 pm_runtime_mark_last_busy(wl->dev);
66 pm_runtime_put_autosuspend(wl->dev);
67 out:
68 mutex_unlock(&wl->mutex);
69
70 return ret;
71 }
72
73 static int
wlcore_vendor_cmd_smart_config_stop(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)74 wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
75 struct wireless_dev *wdev,
76 const void *data, int data_len)
77 {
78 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
79 struct wl1271 *wl = hw->priv;
80 int ret;
81
82 wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
83
84 mutex_lock(&wl->mutex);
85
86 if (unlikely(wl->state != WLCORE_STATE_ON)) {
87 ret = -EINVAL;
88 goto out;
89 }
90
91 ret = pm_runtime_get_sync(wl->dev);
92 if (ret < 0) {
93 pm_runtime_put_noidle(wl->dev);
94 goto out;
95 }
96
97 ret = wlcore_smart_config_stop(wl);
98
99 pm_runtime_mark_last_busy(wl->dev);
100 pm_runtime_put_autosuspend(wl->dev);
101 out:
102 mutex_unlock(&wl->mutex);
103
104 return ret;
105 }
106
107 static int
wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)108 wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
109 struct wireless_dev *wdev,
110 const void *data, int data_len)
111 {
112 struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
113 struct wl1271 *wl = hw->priv;
114 struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
115 int ret;
116
117 wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key");
118
119 if (!data)
120 return -EINVAL;
121
122 ret = nla_parse_deprecated(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
123 wlcore_vendor_attr_policy, NULL);
124 if (ret)
125 return ret;
126
127 if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] ||
128 !tb[WLCORE_VENDOR_ATTR_GROUP_KEY])
129 return -EINVAL;
130
131 mutex_lock(&wl->mutex);
132
133 if (unlikely(wl->state != WLCORE_STATE_ON)) {
134 ret = -EINVAL;
135 goto out;
136 }
137
138 ret = pm_runtime_get_sync(wl->dev);
139 if (ret < 0) {
140 pm_runtime_put_noidle(wl->dev);
141 goto out;
142 }
143
144 ret = wlcore_smart_config_set_group_key(wl,
145 nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
146 nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
147 nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
148
149 pm_runtime_mark_last_busy(wl->dev);
150 pm_runtime_put_autosuspend(wl->dev);
151 out:
152 mutex_unlock(&wl->mutex);
153
154 return ret;
155 }
156
157 static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
158 {
159 .info = {
160 .vendor_id = TI_OUI,
161 .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
162 },
163 .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
164 WIPHY_VENDOR_CMD_NEED_RUNNING,
165 .doit = wlcore_vendor_cmd_smart_config_start,
166 .policy = wlcore_vendor_attr_policy,
167 },
168 {
169 .info = {
170 .vendor_id = TI_OUI,
171 .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
172 },
173 .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
174 WIPHY_VENDOR_CMD_NEED_RUNNING,
175 .doit = wlcore_vendor_cmd_smart_config_stop,
176 .policy = wlcore_vendor_attr_policy,
177 },
178 {
179 .info = {
180 .vendor_id = TI_OUI,
181 .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
182 },
183 .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
184 WIPHY_VENDOR_CMD_NEED_RUNNING,
185 .doit = wlcore_vendor_cmd_smart_config_set_group_key,
186 .policy = wlcore_vendor_attr_policy,
187 },
188 };
189
190 static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
191 {
192 .vendor_id = TI_OUI,
193 .subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
194 },
195 {
196 .vendor_id = TI_OUI,
197 .subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
198 },
199 };
200
wlcore_set_vendor_commands(struct wiphy * wiphy)201 void wlcore_set_vendor_commands(struct wiphy *wiphy)
202 {
203 wiphy->vendor_commands = wlcore_vendor_commands;
204 wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
205 wiphy->vendor_events = wlcore_vendor_events;
206 wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
207 }
208