Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / drivers / net / wireless / ti / wlcore / vendor_cmd.c
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, MAX_WLCORE_VENDOR_ATTR, data, data_len,
42                                    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_get_sync(wl->dev);
57         if (ret < 0) {
58                 pm_runtime_put_noidle(wl->dev);
59                 goto out;
60         }
61
62         ret = wlcore_smart_config_start(wl,
63                         nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]));
64
65         pm_runtime_mark_last_busy(wl->dev);
66         pm_runtime_put_autosuspend(wl->dev);
67 out:
68         mutex_unlock(&wl->mutex);
69
70         return ret;
71 }
72
73 static int
74 wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy,
75                                     struct wireless_dev *wdev,
76                                     const void *data, int data_len)
77 {
78         struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
79         struct wl1271 *wl = hw->priv;
80         int ret;
81
82         wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop");
83
84         mutex_lock(&wl->mutex);
85
86         if (unlikely(wl->state != WLCORE_STATE_ON)) {
87                 ret = -EINVAL;
88                 goto out;
89         }
90
91         ret = pm_runtime_get_sync(wl->dev);
92         if (ret < 0) {
93                 pm_runtime_put_noidle(wl->dev);
94                 goto out;
95         }
96
97         ret = wlcore_smart_config_stop(wl);
98
99         pm_runtime_mark_last_busy(wl->dev);
100         pm_runtime_put_autosuspend(wl->dev);
101 out:
102         mutex_unlock(&wl->mutex);
103
104         return ret;
105 }
106
107 static int
108 wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy,
109                                              struct wireless_dev *wdev,
110                                              const void *data, int data_len)
111 {
112         struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
113         struct wl1271 *wl = hw->priv;
114         struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR];
115         int ret;
116
117         wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key");
118
119         if (!data)
120                 return -EINVAL;
121
122         ret = nla_parse_deprecated(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len,
123                                    wlcore_vendor_attr_policy, NULL);
124         if (ret)
125                 return ret;
126
127         if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] ||
128             !tb[WLCORE_VENDOR_ATTR_GROUP_KEY])
129                 return -EINVAL;
130
131         mutex_lock(&wl->mutex);
132
133         if (unlikely(wl->state != WLCORE_STATE_ON)) {
134                 ret = -EINVAL;
135                 goto out;
136         }
137
138         ret = pm_runtime_get_sync(wl->dev);
139         if (ret < 0) {
140                 pm_runtime_put_noidle(wl->dev);
141                 goto out;
142         }
143
144         ret = wlcore_smart_config_set_group_key(wl,
145                         nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]),
146                         nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]),
147                         nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]));
148
149         pm_runtime_mark_last_busy(wl->dev);
150         pm_runtime_put_autosuspend(wl->dev);
151 out:
152         mutex_unlock(&wl->mutex);
153
154         return ret;
155 }
156
157 static const struct wiphy_vendor_command wlcore_vendor_commands[] = {
158         {
159                 .info = {
160                         .vendor_id = TI_OUI,
161                         .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START,
162                 },
163                 .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
164                          WIPHY_VENDOR_CMD_NEED_RUNNING,
165                 .doit = wlcore_vendor_cmd_smart_config_start,
166                 .policy = wlcore_vendor_attr_policy,
167         },
168         {
169                 .info = {
170                         .vendor_id = TI_OUI,
171                         .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP,
172                 },
173                 .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
174                          WIPHY_VENDOR_CMD_NEED_RUNNING,
175                 .doit = wlcore_vendor_cmd_smart_config_stop,
176                 .policy = wlcore_vendor_attr_policy,
177         },
178         {
179                 .info = {
180                         .vendor_id = TI_OUI,
181                         .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY,
182                 },
183                 .flags = WIPHY_VENDOR_CMD_NEED_NETDEV |
184                          WIPHY_VENDOR_CMD_NEED_RUNNING,
185                 .doit = wlcore_vendor_cmd_smart_config_set_group_key,
186                 .policy = wlcore_vendor_attr_policy,
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
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 }