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 | |
18 | static const |
19 | struct 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 | |
26 | static int |
27 | wlcore_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, maxtype: MAX_WLCORE_VENDOR_ATTR, head: data, len: data_len, |
42 | policy: 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(dev: wl->dev); |
57 | if (ret < 0) |
58 | goto out; |
59 | |
60 | ret = wlcore_smart_config_start(wl, |
61 | group_bitmap: nla_get_u32(nla: tb[WLCORE_VENDOR_ATTR_GROUP_ID])); |
62 | |
63 | pm_runtime_mark_last_busy(dev: wl->dev); |
64 | pm_runtime_put_autosuspend(dev: wl->dev); |
65 | out: |
66 | mutex_unlock(lock: &wl->mutex); |
67 | |
68 | return ret; |
69 | } |
70 | |
71 | static int |
72 | wlcore_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(dev: wl->dev); |
90 | if (ret < 0) |
91 | goto out; |
92 | |
93 | ret = wlcore_smart_config_stop(wl); |
94 | |
95 | pm_runtime_mark_last_busy(dev: wl->dev); |
96 | pm_runtime_put_autosuspend(dev: wl->dev); |
97 | out: |
98 | mutex_unlock(lock: &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_deprecated(tb, maxtype: MAX_WLCORE_VENDOR_ATTR, head: data, len: data_len, |
119 | policy: 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(dev: wl->dev); |
135 | if (ret < 0) |
136 | goto out; |
137 | |
138 | ret = wlcore_smart_config_set_group_key(wl, |
139 | group_id: nla_get_u32(nla: tb[WLCORE_VENDOR_ATTR_GROUP_ID]), |
140 | key_len: nla_len(nla: tb[WLCORE_VENDOR_ATTR_GROUP_KEY]), |
141 | key: nla_data(nla: tb[WLCORE_VENDOR_ATTR_GROUP_KEY])); |
142 | |
143 | pm_runtime_mark_last_busy(dev: wl->dev); |
144 | pm_runtime_put_autosuspend(dev: wl->dev); |
145 | out: |
146 | mutex_unlock(lock: &wl->mutex); |
147 | |
148 | return ret; |
149 | } |
150 | |
151 | static 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 | |
184 | static 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 | |
195 | void 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 | |