1/*
2 * This file is part of wlcore
3 *
4 * Copyright (C) 2014 Texas Instruments. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 */
10
11#include <net/mac80211.h>
12#include <net/netlink.h>
13
14#include "wlcore.h"
15#include "debug.h"
16#include "ps.h"
17#include "hw_ops.h"
18#include "vendor_cmd.h"
19
20static const
21struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = {
22	[WLCORE_VENDOR_ATTR_FREQ]		= { .type = NLA_U32 },
23	[WLCORE_VENDOR_ATTR_GROUP_ID]		= { .type = NLA_U32 },
24	[WLCORE_VENDOR_ATTR_GROUP_KEY]		= { .type = NLA_U32,
25						    .len = WLAN_MAX_KEY_LEN },
26};
27
28static int
29wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
30				     struct wireless_dev *wdev,
31				     const void *data, int data_len)
32{
33	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
34	struct wl1271 *wl = hw->priv;
35	struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
36	int ret;
37
38	wl1271_debug(DEBUG_CMD, "vendor cmd smart config start");
39
40	if (!data)
41		return -EINVAL;
42
43	ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
44			wlcore_vendor_attr_policy);
45	if (ret)
46		return ret;
47
48	if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID])
49		return -EINVAL;
50
51	mutex_lock(&wl->mutex);
52
53	if (unlikely(wl->state != WLCORE_STATE_ON)) {
54		ret = -EINVAL;
55		goto out;
56	}
57
58	ret = wl1271_ps_elp_wakeup(wl);
59	if (ret < 0)
60		goto out;
61
62	ret = wlcore_smart_config_start(wl,
63			nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
64
65	wl1271_ps_elp_sleep(wl);
66out:
67	mutex_unlock(&wl->mutex);
68
69	return 0;
70}
71
72static int
73wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
74				    struct wireless_dev *wdev,
75				    const void *data, int data_len)
76{
77	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
78	struct wl1271 *wl = hw->priv;
79	int ret;
80
81	wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
82
83	mutex_lock(&wl->mutex);
84
85	if (unlikely(wl->state != WLCORE_STATE_ON)) {
86		ret = -EINVAL;
87		goto out;
88	}
89
90	ret = wl1271_ps_elp_wakeup(wl);
91	if (ret < 0)
92		goto out;
93
94	ret = wlcore_smart_config_stop(wl);
95
96	wl1271_ps_elp_sleep(wl);
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(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
119			wlcore_vendor_attr_policy);
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 = wl1271_ps_elp_wakeup(wl);
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	wl1271_ps_elp_sleep(wl);
144out:
145	mutex_unlock(&wl->mutex);
146
147	return ret;
148}
149
150static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
151	{
152		.info = {
153			.vendor_id = TI_OUI,
154			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
155		},
156		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
157			 WIPHY_VENDOR_CMD_NEED_RUNNING,
158		.doit = wlcore_vendor_cmd_smart_config_start,
159	},
160	{
161		.info = {
162			.vendor_id = TI_OUI,
163			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
164		},
165		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
166			 WIPHY_VENDOR_CMD_NEED_RUNNING,
167		.doit = wlcore_vendor_cmd_smart_config_stop,
168	},
169	{
170		.info = {
171			.vendor_id = TI_OUI,
172			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
173		},
174		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
175			 WIPHY_VENDOR_CMD_NEED_RUNNING,
176		.doit = wlcore_vendor_cmd_smart_config_set_group_key,
177	},
178};
179
180static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
181	{
182		.vendor_id = TI_OUI,
183		.subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
184	},
185	{
186		.vendor_id = TI_OUI,
187		.subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
188	},
189};
190
191void wlcore_set_vendor_commands(struct wiphy *wiphy)
192{
193	wiphy->vendor_commands = wlcore_vendor_commands;
194	wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
195	wiphy->vendor_events = wlcore_vendor_events;
196	wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
197}
198