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 <linux/pm_runtime.h>
12 
13 #include <net/mac80211.h>
14 #include <net/netlink.h>
15 
16 #include "wlcore.h"
17 #include "debug.h"
18 #include "hw_ops.h"
19 #include "vendor_cmd.h"
20 
21 static const
22 struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = {
23 	[WLCORE_VENDOR_ATTR_FREQ]		= { .type = NLA_U32 },
24 	[WLCORE_VENDOR_ATTR_GROUP_ID]		= { .type = NLA_U32 },
25 	[WLCORE_VENDOR_ATTR_GROUP_KEY]		= { .type = NLA_BINARY,
26 						    .len = WLAN_MAX_KEY_LEN },
27 };
28 
29 static int
wlcore_vendor_cmd_smart_config_start(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)30 wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy,
31 				     struct wireless_dev *wdev,
32 				     const void *data, int data_len)
33 {
34 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
35 	struct wl1271 *wl = hw->priv;
36 	struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
37 	int ret;
38 
39 	wl1271_debug(DEBUG_CMD, "vendor cmd smart config start");
40 
41 	if (!data)
42 		return -EINVAL;
43 
44 	ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
45 			wlcore_vendor_attr_policy, NULL);
46 	if (ret)
47 		return ret;
48 
49 	if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID])
50 		return -EINVAL;
51 
52 	mutex_lock(&wl->mutex);
53 
54 	if (unlikely(wl->state != WLCORE_STATE_ON)) {
55 		ret = -EINVAL;
56 		goto out;
57 	}
58 
59 	ret = pm_runtime_get_sync(wl->dev);
60 	if (ret < 0) {
61 		pm_runtime_put_noidle(wl->dev);
62 		goto out;
63 	}
64 
65 	ret = wlcore_smart_config_start(wl,
66 			nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
67 
68 	pm_runtime_mark_last_busy(wl->dev);
69 	pm_runtime_put_autosuspend(wl->dev);
70 out:
71 	mutex_unlock(&wl->mutex);
72 
73 	return 0;
74 }
75 
76 static int
wlcore_vendor_cmd_smart_config_stop(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)77 wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
78 				    struct wireless_dev *wdev,
79 				    const void *data, int data_len)
80 {
81 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
82 	struct wl1271 *wl = hw->priv;
83 	int ret;
84 
85 	wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
86 
87 	mutex_lock(&wl->mutex);
88 
89 	if (unlikely(wl->state != WLCORE_STATE_ON)) {
90 		ret = -EINVAL;
91 		goto out;
92 	}
93 
94 	ret = pm_runtime_get_sync(wl->dev);
95 	if (ret < 0) {
96 		pm_runtime_put_noidle(wl->dev);
97 		goto out;
98 	}
99 
100 	ret = wlcore_smart_config_stop(wl);
101 
102 	pm_runtime_mark_last_busy(wl->dev);
103 	pm_runtime_put_autosuspend(wl->dev);
104 out:
105 	mutex_unlock(&wl->mutex);
106 
107 	return ret;
108 }
109 
110 static int
wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)111 wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
112 					     struct wireless_dev *wdev,
113 					     const void *data, int data_len)
114 {
115 	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
116 	struct wl1271 *wl = hw->priv;
117 	struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
118 	int ret;
119 
120 	wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key");
121 
122 	if (!data)
123 		return -EINVAL;
124 
125 	ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
126 			wlcore_vendor_attr_policy, NULL);
127 	if (ret)
128 		return ret;
129 
130 	if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] ||
131 	    !tb[WLCORE_VENDOR_ATTR_GROUP_KEY])
132 		return -EINVAL;
133 
134 	mutex_lock(&wl->mutex);
135 
136 	if (unlikely(wl->state != WLCORE_STATE_ON)) {
137 		ret = -EINVAL;
138 		goto out;
139 	}
140 
141 	ret = pm_runtime_get_sync(wl->dev);
142 	if (ret < 0) {
143 		pm_runtime_put_noidle(wl->dev);
144 		goto out;
145 	}
146 
147 	ret = wlcore_smart_config_set_group_key(wl,
148 			nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
149 			nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
150 			nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
151 
152 	pm_runtime_mark_last_busy(wl->dev);
153 	pm_runtime_put_autosuspend(wl->dev);
154 out:
155 	mutex_unlock(&wl->mutex);
156 
157 	return ret;
158 }
159 
160 static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
161 	{
162 		.info = {
163 			.vendor_id = TI_OUI,
164 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
165 		},
166 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
167 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
168 		.doit = wlcore_vendor_cmd_smart_config_start,
169 	},
170 	{
171 		.info = {
172 			.vendor_id = TI_OUI,
173 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
174 		},
175 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
176 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
177 		.doit = wlcore_vendor_cmd_smart_config_stop,
178 	},
179 	{
180 		.info = {
181 			.vendor_id = TI_OUI,
182 			.subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
183 		},
184 		.flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
185 			 WIPHY_VENDOR_CMD_NEED_RUNNING,
186 		.doit = wlcore_vendor_cmd_smart_config_set_group_key,
187 	},
188 };
189 
190 static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = {
191 	{
192 		.vendor_id = TI_OUI,
193 		.subcmd = WLCORE_VENDOR_EVENT_SC_SYNC,
194 	},
195 	{
196 		.vendor_id = TI_OUI,
197 		.subcmd = WLCORE_VENDOR_EVENT_SC_DECODE,
198 	},
199 };
200 
wlcore_set_vendor_commands(struct wiphy * wiphy)201 void wlcore_set_vendor_commands(struct wiphy *wiphy)
202 {
203 	wiphy->vendor_commands = wlcore_vendor_commands;
204 	wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands);
205 	wiphy->vendor_events = wlcore_vendor_events;
206 	wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events);
207 }
208