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
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
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
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 	},
167 	{
168 		.info = {
169 			.vendor_id = TI_OUI,
170 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
171 		},
172 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
173 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
174 		.doit = wlcore_vendor_cmd_smart_config_stop,
175 	},
176 	{
177 		.info = {
178 			.vendor_id = TI_OUI,
179 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
180 		},
181 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
182 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
183 		.doit = wlcore_vendor_cmd_smart_config_set_group_key,
184 	},
185 };
186 
187 static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
188 	{
189 		.vendor_id = TI_OUI,
190 		.subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
191 	},
192 	{
193 		.vendor_id = TI_OUI,
194 		.subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
195 	},
196 };
197 
198 void wlcore_set_vendor_commands(struct wiphy *wiphy)
199 {
200 	wiphy->vendor_commands = wlcore_vendor_commands;
201 	wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
202 	wiphy->vendor_events = wlcore_vendor_events;
203 	wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
204 }
205