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 
20 static const
21 struct 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_BINARY,
25 						    .len = WLAN_MAX_KEY_LEN },
26 };
27 
28 static int
29 wlcore_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, NULL);
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);
66 out:
67 	mutex_unlock(&wl->mutex);
68 
69 	return 0;
70 }
71 
72 static int
73 wlcore_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);
97 out:
98 	mutex_unlock(&wl->mutex);
99 
100 	return ret;
101 }
102 
103 static int
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(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 = 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);
144 out:
145 	mutex_unlock(&wl->mutex);
146 
147 	return ret;
148 }
149 
150 static 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 
180 static 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 
191 void 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