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_resume_and_get(wl->dev);
57 	if (ret < 0)
58 		goto out;
59 
60 	ret = wlcore_smart_config_start(wl,
61 			nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
62 
63 	pm_runtime_mark_last_busy(wl->dev);
64 	pm_runtime_put_autosuspend(wl->dev);
65 out:
66 	mutex_unlock(&wl->mutex);
67 
68 	return ret;
69 }
70 
71 static int
wlcore_vendor_cmd_smart_config_stop(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)72 wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
73 				    struct wireless_dev *wdev,
74 				    const void *data, int data_len)
75 {
76 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
77 	struct wl1271 *wl = hw->priv;
78 	int ret;
79 
80 	wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
81 
82 	mutex_lock(&wl->mutex);
83 
84 	if (unlikely(wl->state != WLCORE_STATE_ON)) {
85 		ret = -EINVAL;
86 		goto out;
87 	}
88 
89 	ret = pm_runtime_resume_and_get(wl->dev);
90 	if (ret < 0)
91 		goto out;
92 
93 	ret = wlcore_smart_config_stop(wl);
94 
95 	pm_runtime_mark_last_busy(wl->dev);
96 	pm_runtime_put_autosuspend(wl->dev);
97 out:
98 	mutex_unlock(&wl->mutex);
99 
100 	return ret;
101 }
102 
103 static int
wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)104 wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
105 					     struct wireless_dev *wdev,
106 					     const void *data, int data_len)
107 {
108 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
109 	struct wl1271 *wl = hw->priv;
110 	struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
111 	int ret;
112 
113 	wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key");
114 
115 	if (!data)
116 		return -EINVAL;
117 
118 	ret = nla_parse_deprecated(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
119 				   wlcore_vendor_attr_policy, NULL);
120 	if (ret)
121 		return ret;
122 
123 	if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] ||
124 	    !tb[WLCORE_VENDOR_ATTR_GROUP_KEY])
125 		return -EINVAL;
126 
127 	mutex_lock(&wl->mutex);
128 
129 	if (unlikely(wl->state != WLCORE_STATE_ON)) {
130 		ret = -EINVAL;
131 		goto out;
132 	}
133 
134 	ret = pm_runtime_resume_and_get(wl->dev);
135 	if (ret < 0)
136 		goto out;
137 
138 	ret = wlcore_smart_config_set_group_key(wl,
139 			nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
140 			nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
141 			nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
142 
143 	pm_runtime_mark_last_busy(wl->dev);
144 	pm_runtime_put_autosuspend(wl->dev);
145 out:
146 	mutex_unlock(&wl->mutex);
147 
148 	return ret;
149 }
150 
151 static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
152 	{
153 		.info = {
154 			.vendor_id = TI_OUI,
155 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
156 		},
157 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
158 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
159 		.doit = wlcore_vendor_cmd_smart_config_start,
160 		.policy = wlcore_vendor_attr_policy,
161 	},
162 	{
163 		.info = {
164 			.vendor_id = TI_OUI,
165 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
166 		},
167 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
168 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
169 		.doit = wlcore_vendor_cmd_smart_config_stop,
170 		.policy = wlcore_vendor_attr_policy,
171 	},
172 	{
173 		.info = {
174 			.vendor_id = TI_OUI,
175 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
176 		},
177 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
178 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
179 		.doit = wlcore_vendor_cmd_smart_config_set_group_key,
180 		.policy = wlcore_vendor_attr_policy,
181 	},
182 };
183 
184 static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
185 	{
186 		.vendor_id = TI_OUI,
187 		.subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
188 	},
189 	{
190 		.vendor_id = TI_OUI,
191 		.subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
192 	},
193 };
194 
wlcore_set_vendor_commands(struct wiphy * wiphy)195 void wlcore_set_vendor_commands(struct wiphy *wiphy)
196 {
197 	wiphy->vendor_commands = wlcore_vendor_commands;
198 	wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
199 	wiphy->vendor_events = wlcore_vendor_events;
200 	wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
201 }
202