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
18static const
19struct 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
26static int
27wlcore_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);
65out:
66	mutex_unlock(&wl->mutex);
67
68	return ret;
69}
70
71static int
72wlcore_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);
97out:
98	mutex_unlock(&wl->mutex);
99
100	return ret;
101}
102
103static int
104wlcore_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);
145out:
146	mutex_unlock(&wl->mutex);
147
148	return ret;
149}
150
151static 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
184static 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
195void 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