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