Merge tag 'for-5.2-rc2-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[sfrench/cifs-2.6.git] / drivers / net / wireless / ti / wlcore / vendor_cmd.c
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
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_deprecated(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 ret;
74 }
75
76 static int
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
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_deprecated(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
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 }