diff options
author | Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com> | 2012-12-20 16:13:19 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2013-01-04 16:10:53 -0500 |
commit | 2be7d22f062535de59babdb4b5e9de9ff31e817e (patch) | |
tree | c01269d1e929dd32bc880cf4c319faf2dbb7a496 /drivers | |
parent | c3ff0b2dff5b6c63f2deda8f934c0a21fb74850d (diff) |
wireless: add new wil6210 802.11ad 60GHz driver
This adds support for the 60 GHz 802.11ad Wilocity card
through a new driver, wil6210. Wilocity implemented the
firmware, QCA maintains the device driver.
Currently supported:
- STA: with security
- AP: limited to 1 connected STA, security disabled
- Monitor: due to a hardware/firmware limitation
either control or non-control frames are monitored
Using a STA and AP with this drive, one can assemble
a fully functional BSS. Throughput of 1.2Gbps is achieved
with iperf.
The wil6210 cards have on-board flash memory for the
firmware, the cards comes pre-flashed and no firmware
download is required.
For more details see:
http://wireless.kernel.org/en/users/Drivers/wil6210
Signed-off-by: Vladimir Kondratiev <qca_vkondrat@qca.qualcomm.com>
Signed-off-by: Luis R. Rodriguez <mcgrof@qca.qualcomm.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/wireless/ath/Kconfig | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/Kconfig | 29 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/Makefile | 13 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/cfg80211.c | 573 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/dbg_hexdump.h | 30 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/debugfs.c | 603 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/interrupt.c | 471 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/main.c | 407 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/netdev.c | 157 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/pcie_bus.c | 223 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.c | 871 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/txrx.h | 362 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wil6210.h | 363 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.c | 975 | ||||
-rw-r--r-- | drivers/net/wireless/ath/wil6210/wmi.h | 1116 |
16 files changed, 6195 insertions, 0 deletions
diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1a67a4f829fe..2c02b4e84094 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig | |||
@@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig" | |||
30 | source "drivers/net/wireless/ath/carl9170/Kconfig" | 30 | source "drivers/net/wireless/ath/carl9170/Kconfig" |
31 | source "drivers/net/wireless/ath/ath6kl/Kconfig" | 31 | source "drivers/net/wireless/ath/ath6kl/Kconfig" |
32 | source "drivers/net/wireless/ath/ar5523/Kconfig" | 32 | source "drivers/net/wireless/ath/ar5523/Kconfig" |
33 | source "drivers/net/wireless/ath/wil6210/Kconfig" | ||
33 | 34 | ||
34 | endif | 35 | endif |
diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 1e18621326dc..97b964ded2be 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile | |||
@@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW) += ath9k/ | |||
3 | obj-$(CONFIG_CARL9170) += carl9170/ | 3 | obj-$(CONFIG_CARL9170) += carl9170/ |
4 | obj-$(CONFIG_ATH6KL) += ath6kl/ | 4 | obj-$(CONFIG_ATH6KL) += ath6kl/ |
5 | obj-$(CONFIG_AR5523) += ar5523/ | 5 | obj-$(CONFIG_AR5523) += ar5523/ |
6 | obj-$(CONFIG_WIL6210) += wil6210/ | ||
6 | 7 | ||
7 | obj-$(CONFIG_ATH_COMMON) += ath.o | 8 | obj-$(CONFIG_ATH_COMMON) += ath.o |
8 | 9 | ||
diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig new file mode 100644 index 000000000000..bac3d98a0cfb --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Kconfig | |||
@@ -0,0 +1,29 @@ | |||
1 | config WIL6210 | ||
2 | tristate "Wilocity 60g WiFi card wil6210 support" | ||
3 | depends on CFG80211 | ||
4 | depends on PCI | ||
5 | default n | ||
6 | ---help--- | ||
7 | This module adds support for wireless adapter based on | ||
8 | wil6210 chip by Wilocity. It supports operation on the | ||
9 | 60 GHz band, covered by the IEEE802.11ad standard. | ||
10 | |||
11 | http://wireless.kernel.org/en/users/Drivers/wil6210 | ||
12 | |||
13 | If you choose to build it as a module, it will be called | ||
14 | wil6210 | ||
15 | |||
16 | config WIL6210_ISR_COR | ||
17 | bool "Use Clear-On-Read mode for ISR registers for wil6210" | ||
18 | depends on WIL6210 | ||
19 | default y | ||
20 | ---help--- | ||
21 | ISR registers on wil6210 chip may operate in either | ||
22 | COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode. | ||
23 | For production code, use COR (say y); is default since | ||
24 | it saves extra target transaction; | ||
25 | For ISR debug, use W1C (say n); is allows to monitor ISR | ||
26 | registers with debugfs. If COR were used, ISR would | ||
27 | self-clear when accessed for debug purposes, it makes | ||
28 | such monitoring impossible. | ||
29 | Say y unless you debug interrupts | ||
diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile new file mode 100644 index 000000000000..9396dc9fe3c5 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | obj-$(CONFIG_WIL6210) += wil6210.o | ||
2 | |||
3 | wil6210-objs := main.o | ||
4 | wil6210-objs += netdev.o | ||
5 | wil6210-objs += cfg80211.o | ||
6 | wil6210-objs += pcie_bus.o | ||
7 | wil6210-objs += debugfs.o | ||
8 | wil6210-objs += wmi.o | ||
9 | wil6210-objs += interrupt.o | ||
10 | wil6210-objs += txrx.o | ||
11 | |||
12 | subdir-ccflags-y += -Werror | ||
13 | subdir-ccflags-y += -D__CHECK_ENDIAN__ | ||
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c new file mode 100644 index 000000000000..116f4e807ae1 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c | |||
@@ -0,0 +1,573 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/etherdevice.h> | ||
21 | #include <linux/wireless.h> | ||
22 | #include <linux/ieee80211.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/version.h> | ||
25 | #include <net/cfg80211.h> | ||
26 | |||
27 | #include "wil6210.h" | ||
28 | #include "wmi.h" | ||
29 | |||
30 | #define CHAN60G(_channel, _flags) { \ | ||
31 | .band = IEEE80211_BAND_60GHZ, \ | ||
32 | .center_freq = 56160 + (2160 * (_channel)), \ | ||
33 | .hw_value = (_channel), \ | ||
34 | .flags = (_flags), \ | ||
35 | .max_antenna_gain = 0, \ | ||
36 | .max_power = 40, \ | ||
37 | } | ||
38 | |||
39 | static struct ieee80211_channel wil_60ghz_channels[] = { | ||
40 | CHAN60G(1, 0), | ||
41 | CHAN60G(2, 0), | ||
42 | CHAN60G(3, 0), | ||
43 | /* channel 4 not supported yet */ | ||
44 | }; | ||
45 | |||
46 | static struct ieee80211_supported_band wil_band_60ghz = { | ||
47 | .channels = wil_60ghz_channels, | ||
48 | .n_channels = ARRAY_SIZE(wil_60ghz_channels), | ||
49 | .ht_cap = { | ||
50 | .ht_supported = true, | ||
51 | .cap = 0, /* TODO */ | ||
52 | .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ | ||
53 | .ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ | ||
54 | .mcs = { | ||
55 | /* MCS 1..12 - SC PHY */ | ||
56 | .rx_mask = {0xfe, 0x1f}, /* 1..12 */ | ||
57 | .tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ | ||
58 | }, | ||
59 | }, | ||
60 | }; | ||
61 | |||
62 | static const struct ieee80211_txrx_stypes | ||
63 | wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { | ||
64 | [NL80211_IFTYPE_STATION] = { | ||
65 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
66 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
67 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
68 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
69 | }, | ||
70 | [NL80211_IFTYPE_AP] = { | ||
71 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
72 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
73 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
74 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
75 | }, | ||
76 | [NL80211_IFTYPE_P2P_CLIENT] = { | ||
77 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
78 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
79 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
80 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
81 | }, | ||
82 | [NL80211_IFTYPE_P2P_GO] = { | ||
83 | .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
84 | BIT(IEEE80211_STYPE_PROBE_RESP >> 4), | ||
85 | .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | | ||
86 | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | ||
87 | }, | ||
88 | }; | ||
89 | |||
90 | static const u32 wil_cipher_suites[] = { | ||
91 | WLAN_CIPHER_SUITE_GCMP, | ||
92 | }; | ||
93 | |||
94 | int wil_iftype_nl2wmi(enum nl80211_iftype type) | ||
95 | { | ||
96 | static const struct { | ||
97 | enum nl80211_iftype nl; | ||
98 | enum wmi_network_type wmi; | ||
99 | } __nl2wmi[] = { | ||
100 | {NL80211_IFTYPE_ADHOC, WMI_NETTYPE_ADHOC}, | ||
101 | {NL80211_IFTYPE_STATION, WMI_NETTYPE_INFRA}, | ||
102 | {NL80211_IFTYPE_AP, WMI_NETTYPE_AP}, | ||
103 | {NL80211_IFTYPE_P2P_CLIENT, WMI_NETTYPE_P2P}, | ||
104 | {NL80211_IFTYPE_P2P_GO, WMI_NETTYPE_P2P}, | ||
105 | {NL80211_IFTYPE_MONITOR, WMI_NETTYPE_ADHOC}, /* FIXME */ | ||
106 | }; | ||
107 | uint i; | ||
108 | |||
109 | for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { | ||
110 | if (__nl2wmi[i].nl == type) | ||
111 | return __nl2wmi[i].wmi; | ||
112 | } | ||
113 | |||
114 | return -EOPNOTSUPP; | ||
115 | } | ||
116 | |||
117 | static int wil_cfg80211_get_station(struct wiphy *wiphy, | ||
118 | struct net_device *ndev, | ||
119 | u8 *mac, struct station_info *sinfo) | ||
120 | { | ||
121 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
122 | int rc; | ||
123 | struct wmi_notify_req_cmd cmd = { | ||
124 | .cid = 0, | ||
125 | .interval_usec = 0, | ||
126 | }; | ||
127 | |||
128 | if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) | ||
129 | return -ENOENT; | ||
130 | |||
131 | /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ | ||
132 | rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), | ||
133 | WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); | ||
134 | if (rc) | ||
135 | return rc; | ||
136 | |||
137 | sinfo->generation = wil->sinfo_gen; | ||
138 | |||
139 | sinfo->filled |= STATION_INFO_TX_BITRATE; | ||
140 | sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; | ||
141 | sinfo->txrate.mcs = wil->stats.bf_mcs; | ||
142 | sinfo->filled |= STATION_INFO_RX_BITRATE; | ||
143 | sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; | ||
144 | sinfo->rxrate.mcs = wil->stats.last_mcs_rx; | ||
145 | |||
146 | if (test_bit(wil_status_fwconnected, &wil->status)) { | ||
147 | sinfo->filled |= STATION_INFO_SIGNAL; | ||
148 | sinfo->signal = 12; /* TODO: provide real value */ | ||
149 | } | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int wil_cfg80211_change_iface(struct wiphy *wiphy, | ||
155 | struct net_device *ndev, | ||
156 | enum nl80211_iftype type, u32 *flags, | ||
157 | struct vif_params *params) | ||
158 | { | ||
159 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
160 | struct wireless_dev *wdev = wil->wdev; | ||
161 | |||
162 | switch (type) { | ||
163 | case NL80211_IFTYPE_STATION: | ||
164 | case NL80211_IFTYPE_AP: | ||
165 | case NL80211_IFTYPE_P2P_CLIENT: | ||
166 | case NL80211_IFTYPE_P2P_GO: | ||
167 | break; | ||
168 | case NL80211_IFTYPE_MONITOR: | ||
169 | if (flags) | ||
170 | wil->monitor_flags = *flags; | ||
171 | else | ||
172 | wil->monitor_flags = 0; | ||
173 | |||
174 | break; | ||
175 | default: | ||
176 | return -EOPNOTSUPP; | ||
177 | } | ||
178 | |||
179 | wdev->iftype = type; | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | static int wil_cfg80211_scan(struct wiphy *wiphy, | ||
185 | struct cfg80211_scan_request *request) | ||
186 | { | ||
187 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
188 | struct wireless_dev *wdev = wil->wdev; | ||
189 | struct { | ||
190 | struct wmi_start_scan_cmd cmd; | ||
191 | u16 chnl[4]; | ||
192 | } __packed cmd; | ||
193 | uint i, n; | ||
194 | |||
195 | if (wil->scan_request) { | ||
196 | wil_err(wil, "Already scanning\n"); | ||
197 | return -EAGAIN; | ||
198 | } | ||
199 | |||
200 | /* check we are client side */ | ||
201 | switch (wdev->iftype) { | ||
202 | case NL80211_IFTYPE_STATION: | ||
203 | case NL80211_IFTYPE_P2P_CLIENT: | ||
204 | break; | ||
205 | default: | ||
206 | return -EOPNOTSUPP; | ||
207 | |||
208 | } | ||
209 | |||
210 | /* FW don't support scan after connection attempt */ | ||
211 | if (test_bit(wil_status_dontscan, &wil->status)) { | ||
212 | wil_err(wil, "Scan after connect attempt not supported\n"); | ||
213 | return -EBUSY; | ||
214 | } | ||
215 | |||
216 | wil->scan_request = request; | ||
217 | |||
218 | memset(&cmd, 0, sizeof(cmd)); | ||
219 | cmd.cmd.num_channels = 0; | ||
220 | n = min(request->n_channels, 4U); | ||
221 | for (i = 0; i < n; i++) { | ||
222 | int ch = request->channels[i]->hw_value; | ||
223 | if (ch == 0) { | ||
224 | wil_err(wil, | ||
225 | "Scan requested for unknown frequency %dMhz\n", | ||
226 | request->channels[i]->center_freq); | ||
227 | continue; | ||
228 | } | ||
229 | /* 0-based channel indexes */ | ||
230 | cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; | ||
231 | wil_dbg(wil, "Scan for ch %d : %d MHz\n", ch, | ||
232 | request->channels[i]->center_freq); | ||
233 | } | ||
234 | |||
235 | return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + | ||
236 | cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); | ||
237 | } | ||
238 | |||
239 | static int wil_cfg80211_connect(struct wiphy *wiphy, | ||
240 | struct net_device *ndev, | ||
241 | struct cfg80211_connect_params *sme) | ||
242 | { | ||
243 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
244 | struct cfg80211_bss *bss; | ||
245 | struct wmi_connect_cmd conn; | ||
246 | const u8 *ssid_eid; | ||
247 | const u8 *rsn_eid; | ||
248 | int ch; | ||
249 | int rc = 0; | ||
250 | |||
251 | bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, | ||
252 | sme->ssid, sme->ssid_len, | ||
253 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | ||
254 | if (!bss) { | ||
255 | wil_err(wil, "Unable to find BSS\n"); | ||
256 | return -ENOENT; | ||
257 | } | ||
258 | |||
259 | ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); | ||
260 | if (!ssid_eid) { | ||
261 | wil_err(wil, "No SSID\n"); | ||
262 | rc = -ENOENT; | ||
263 | goto out; | ||
264 | } | ||
265 | |||
266 | rsn_eid = sme->ie ? | ||
267 | cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : | ||
268 | NULL; | ||
269 | if (rsn_eid) { | ||
270 | if (sme->ie_len > WMI_MAX_IE_LEN) { | ||
271 | rc = -ERANGE; | ||
272 | wil_err(wil, "IE too large (%td bytes)\n", | ||
273 | sme->ie_len); | ||
274 | goto out; | ||
275 | } | ||
276 | /* | ||
277 | * For secure assoc, send: | ||
278 | * (1) WMI_DELETE_CIPHER_KEY_CMD | ||
279 | * (2) WMI_SET_APPIE_CMD | ||
280 | */ | ||
281 | rc = wmi_del_cipher_key(wil, 0, bss->bssid); | ||
282 | if (rc) { | ||
283 | wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); | ||
284 | goto out; | ||
285 | } | ||
286 | /* WMI_SET_APPIE_CMD */ | ||
287 | rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); | ||
288 | if (rc) { | ||
289 | wil_err(wil, "WMI_SET_APPIE_CMD failed\n"); | ||
290 | goto out; | ||
291 | } | ||
292 | } | ||
293 | |||
294 | /* WMI_CONNECT_CMD */ | ||
295 | memset(&conn, 0, sizeof(conn)); | ||
296 | switch (bss->capability & 0x03) { | ||
297 | case WLAN_CAPABILITY_DMG_TYPE_AP: | ||
298 | conn.network_type = WMI_NETTYPE_INFRA; | ||
299 | break; | ||
300 | case WLAN_CAPABILITY_DMG_TYPE_PBSS: | ||
301 | conn.network_type = WMI_NETTYPE_P2P; | ||
302 | break; | ||
303 | default: | ||
304 | wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", | ||
305 | bss->capability); | ||
306 | goto out; | ||
307 | } | ||
308 | if (rsn_eid) { | ||
309 | conn.dot11_auth_mode = WMI_AUTH11_SHARED; | ||
310 | conn.auth_mode = WMI_AUTH_WPA2_PSK; | ||
311 | conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; | ||
312 | conn.pairwise_crypto_len = 16; | ||
313 | } else { | ||
314 | conn.dot11_auth_mode = WMI_AUTH11_OPEN; | ||
315 | conn.auth_mode = WMI_AUTH_NONE; | ||
316 | } | ||
317 | |||
318 | conn.ssid_len = min_t(u8, ssid_eid[1], 32); | ||
319 | memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); | ||
320 | |||
321 | ch = bss->channel->hw_value; | ||
322 | if (ch == 0) { | ||
323 | wil_err(wil, "BSS at unknown frequency %dMhz\n", | ||
324 | bss->channel->center_freq); | ||
325 | rc = -EOPNOTSUPP; | ||
326 | goto out; | ||
327 | } | ||
328 | conn.channel = ch - 1; | ||
329 | |||
330 | memcpy(conn.bssid, bss->bssid, 6); | ||
331 | memcpy(conn.dst_mac, bss->bssid, 6); | ||
332 | /* | ||
333 | * FW don't support scan after connection attempt | ||
334 | */ | ||
335 | set_bit(wil_status_dontscan, &wil->status); | ||
336 | |||
337 | rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); | ||
338 | if (rc == 0) { | ||
339 | /* Connect can take lots of time */ | ||
340 | mod_timer(&wil->connect_timer, | ||
341 | jiffies + msecs_to_jiffies(2000)); | ||
342 | } | ||
343 | |||
344 | out: | ||
345 | cfg80211_put_bss(bss); | ||
346 | |||
347 | return rc; | ||
348 | } | ||
349 | |||
350 | static int wil_cfg80211_disconnect(struct wiphy *wiphy, | ||
351 | struct net_device *ndev, | ||
352 | u16 reason_code) | ||
353 | { | ||
354 | int rc; | ||
355 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
356 | |||
357 | rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); | ||
358 | |||
359 | return rc; | ||
360 | } | ||
361 | |||
362 | static int wil_cfg80211_set_channel(struct wiphy *wiphy, | ||
363 | struct cfg80211_chan_def *chandef) | ||
364 | { | ||
365 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
366 | struct wireless_dev *wdev = wil->wdev; | ||
367 | |||
368 | wdev->preset_chandef = *chandef; | ||
369 | |||
370 | return 0; | ||
371 | } | ||
372 | |||
373 | static int wil_cfg80211_add_key(struct wiphy *wiphy, | ||
374 | struct net_device *ndev, | ||
375 | u8 key_index, bool pairwise, | ||
376 | const u8 *mac_addr, | ||
377 | struct key_params *params) | ||
378 | { | ||
379 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
380 | |||
381 | /* group key is not used */ | ||
382 | if (!pairwise) | ||
383 | return 0; | ||
384 | |||
385 | return wmi_add_cipher_key(wil, key_index, mac_addr, | ||
386 | params->key_len, params->key); | ||
387 | } | ||
388 | |||
389 | static int wil_cfg80211_del_key(struct wiphy *wiphy, | ||
390 | struct net_device *ndev, | ||
391 | u8 key_index, bool pairwise, | ||
392 | const u8 *mac_addr) | ||
393 | { | ||
394 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
395 | |||
396 | /* group key is not used */ | ||
397 | if (!pairwise) | ||
398 | return 0; | ||
399 | |||
400 | return wmi_del_cipher_key(wil, key_index, mac_addr); | ||
401 | } | ||
402 | |||
403 | /* Need to be present or wiphy_new() will WARN */ | ||
404 | static int wil_cfg80211_set_default_key(struct wiphy *wiphy, | ||
405 | struct net_device *ndev, | ||
406 | u8 key_index, bool unicast, | ||
407 | bool multicast) | ||
408 | { | ||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static int wil_cfg80211_start_ap(struct wiphy *wiphy, | ||
413 | struct net_device *ndev, | ||
414 | struct cfg80211_ap_settings *info) | ||
415 | { | ||
416 | int rc = 0; | ||
417 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
418 | struct wireless_dev *wdev = ndev->ieee80211_ptr; | ||
419 | struct ieee80211_channel *channel = info->chandef.chan; | ||
420 | struct cfg80211_beacon_data *bcon = &info->beacon; | ||
421 | u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); | ||
422 | |||
423 | if (!channel) { | ||
424 | wil_err(wil, "AP: No channel???\n"); | ||
425 | return -EINVAL; | ||
426 | } | ||
427 | |||
428 | wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, | ||
429 | channel->center_freq, info->privacy ? "secure" : "open"); | ||
430 | print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, | ||
431 | info->ssid, info->ssid_len); | ||
432 | |||
433 | rc = wil_reset(wil); | ||
434 | if (rc) | ||
435 | return rc; | ||
436 | |||
437 | rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); | ||
438 | if (rc) | ||
439 | return rc; | ||
440 | |||
441 | rc = wmi_set_channel(wil, channel->hw_value); | ||
442 | if (rc) | ||
443 | return rc; | ||
444 | |||
445 | /* MAC address - pre-requisite for other commands */ | ||
446 | wmi_set_mac_address(wil, ndev->dev_addr); | ||
447 | |||
448 | /* IE's */ | ||
449 | /* bcon 'head IE's are not relevant for 60g band */ | ||
450 | wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, | ||
451 | bcon->beacon_ies); | ||
452 | wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, | ||
453 | bcon->proberesp_ies); | ||
454 | wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, | ||
455 | bcon->assocresp_ies); | ||
456 | |||
457 | wil->secure_pcp = info->privacy; | ||
458 | |||
459 | rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype); | ||
460 | if (rc) | ||
461 | return rc; | ||
462 | |||
463 | /* Rx VRING. After MAC and beacon */ | ||
464 | rc = wil_rx_init(wil); | ||
465 | |||
466 | netif_carrier_on(ndev); | ||
467 | |||
468 | return rc; | ||
469 | } | ||
470 | |||
471 | static int wil_cfg80211_stop_ap(struct wiphy *wiphy, | ||
472 | struct net_device *ndev) | ||
473 | { | ||
474 | int rc = 0; | ||
475 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
476 | struct wireless_dev *wdev = ndev->ieee80211_ptr; | ||
477 | u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); | ||
478 | |||
479 | /* To stop beaconing, set BI to 0 */ | ||
480 | rc = wmi_set_bcon(wil, 0, wmi_nettype); | ||
481 | |||
482 | return rc; | ||
483 | } | ||
484 | |||
485 | static struct cfg80211_ops wil_cfg80211_ops = { | ||
486 | .scan = wil_cfg80211_scan, | ||
487 | .connect = wil_cfg80211_connect, | ||
488 | .disconnect = wil_cfg80211_disconnect, | ||
489 | .change_virtual_intf = wil_cfg80211_change_iface, | ||
490 | .get_station = wil_cfg80211_get_station, | ||
491 | .set_monitor_channel = wil_cfg80211_set_channel, | ||
492 | .add_key = wil_cfg80211_add_key, | ||
493 | .del_key = wil_cfg80211_del_key, | ||
494 | .set_default_key = wil_cfg80211_set_default_key, | ||
495 | /* AP mode */ | ||
496 | .start_ap = wil_cfg80211_start_ap, | ||
497 | .stop_ap = wil_cfg80211_stop_ap, | ||
498 | }; | ||
499 | |||
500 | static void wil_wiphy_init(struct wiphy *wiphy) | ||
501 | { | ||
502 | /* TODO: set real value */ | ||
503 | wiphy->max_scan_ssids = 10; | ||
504 | wiphy->max_num_pmkids = 0 /* TODO: */; | ||
505 | wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | | ||
506 | BIT(NL80211_IFTYPE_AP) | | ||
507 | BIT(NL80211_IFTYPE_MONITOR); | ||
508 | /* TODO: enable P2P when integrated with supplicant: | ||
509 | * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | ||
510 | */ | ||
511 | wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | | ||
512 | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; | ||
513 | dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", | ||
514 | __func__, wiphy->flags); | ||
515 | wiphy->probe_resp_offload = | ||
516 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | | ||
517 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | | ||
518 | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; | ||
519 | |||
520 | wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; | ||
521 | |||
522 | /* TODO: figure this out */ | ||
523 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | ||
524 | |||
525 | wiphy->cipher_suites = wil_cipher_suites; | ||
526 | wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); | ||
527 | wiphy->mgmt_stypes = wil_mgmt_stypes; | ||
528 | } | ||
529 | |||
530 | struct wireless_dev *wil_cfg80211_init(struct device *dev) | ||
531 | { | ||
532 | int rc = 0; | ||
533 | struct wireless_dev *wdev; | ||
534 | |||
535 | wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); | ||
536 | if (!wdev) | ||
537 | return ERR_PTR(-ENOMEM); | ||
538 | |||
539 | wdev->wiphy = wiphy_new(&wil_cfg80211_ops, | ||
540 | sizeof(struct wil6210_priv)); | ||
541 | if (!wdev->wiphy) { | ||
542 | rc = -ENOMEM; | ||
543 | goto out; | ||
544 | } | ||
545 | |||
546 | set_wiphy_dev(wdev->wiphy, dev); | ||
547 | wil_wiphy_init(wdev->wiphy); | ||
548 | |||
549 | rc = wiphy_register(wdev->wiphy); | ||
550 | if (rc < 0) | ||
551 | goto out_failed_reg; | ||
552 | |||
553 | return wdev; | ||
554 | |||
555 | out_failed_reg: | ||
556 | wiphy_free(wdev->wiphy); | ||
557 | out: | ||
558 | kfree(wdev); | ||
559 | |||
560 | return ERR_PTR(rc); | ||
561 | } | ||
562 | |||
563 | void wil_wdev_free(struct wil6210_priv *wil) | ||
564 | { | ||
565 | struct wireless_dev *wdev = wil_to_wdev(wil); | ||
566 | |||
567 | if (!wdev) | ||
568 | return; | ||
569 | |||
570 | wiphy_unregister(wdev->wiphy); | ||
571 | wiphy_free(wdev->wiphy); | ||
572 | kfree(wdev); | ||
573 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h new file mode 100644 index 000000000000..6a315ba5aa7d --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h | |||
@@ -0,0 +1,30 @@ | |||
1 | #ifndef WIL_DBG_HEXDUMP_H_ | ||
2 | #define WIL_DBG_HEXDUMP_H_ | ||
3 | |||
4 | #if defined(CONFIG_DYNAMIC_DEBUG) | ||
5 | #define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ | ||
6 | groupsize, buf, len, ascii) \ | ||
7 | do { \ | ||
8 | DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, \ | ||
9 | __builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\ | ||
10 | if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT)) \ | ||
11 | print_hex_dump(KERN_DEBUG, prefix_str, \ | ||
12 | prefix_type, rowsize, groupsize, \ | ||
13 | buf, len, ascii); \ | ||
14 | } while (0) | ||
15 | |||
16 | #define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ | ||
17 | groupsize, buf, len, ascii) \ | ||
18 | wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize, \ | ||
19 | groupsize, buf, len, ascii) | ||
20 | |||
21 | #define print_hex_dump_bytes(prefix_str, prefix_type, buf, len) \ | ||
22 | wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true) | ||
23 | #else /* defined(CONFIG_DYNAMIC_DEBUG) */ | ||
24 | #define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize, \ | ||
25 | groupsize, buf, len, ascii) \ | ||
26 | print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize, \ | ||
27 | groupsize, buf, len, ascii) | ||
28 | #endif /* defined(CONFIG_DYNAMIC_DEBUG) */ | ||
29 | |||
30 | #endif /* WIL_DBG_HEXDUMP_H_ */ | ||
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c new file mode 100644 index 000000000000..65fc9683bfd8 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debugfs.c | |||
@@ -0,0 +1,603 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/debugfs.h> | ||
19 | #include <linux/seq_file.h> | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/rtnetlink.h> | ||
22 | |||
23 | #include "wil6210.h" | ||
24 | #include "txrx.h" | ||
25 | |||
26 | /* Nasty hack. Better have per device instances */ | ||
27 | static u32 mem_addr; | ||
28 | static u32 dbg_txdesc_index; | ||
29 | |||
30 | static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, | ||
31 | const char *name, struct vring *vring) | ||
32 | { | ||
33 | void __iomem *x = wmi_addr(wil, vring->hwtail); | ||
34 | |||
35 | seq_printf(s, "VRING %s = {\n", name); | ||
36 | seq_printf(s, " pa = 0x%016llx\n", (unsigned long long)vring->pa); | ||
37 | seq_printf(s, " va = 0x%p\n", vring->va); | ||
38 | seq_printf(s, " size = %d\n", vring->size); | ||
39 | seq_printf(s, " swtail = %d\n", vring->swtail); | ||
40 | seq_printf(s, " swhead = %d\n", vring->swhead); | ||
41 | seq_printf(s, " hwtail = [0x%08x] -> ", vring->hwtail); | ||
42 | if (x) | ||
43 | seq_printf(s, "0x%08x\n", ioread32(x)); | ||
44 | else | ||
45 | seq_printf(s, "???\n"); | ||
46 | |||
47 | if (vring->va && (vring->size < 1025)) { | ||
48 | uint i; | ||
49 | for (i = 0; i < vring->size; i++) { | ||
50 | volatile struct vring_tx_desc *d = &vring->va[i].tx; | ||
51 | if ((i % 64) == 0 && (i != 0)) | ||
52 | seq_printf(s, "\n"); | ||
53 | seq_printf(s, "%s", (d->dma.status & BIT(0)) ? | ||
54 | "S" : (vring->ctx[i] ? "H" : "h")); | ||
55 | } | ||
56 | seq_printf(s, "\n"); | ||
57 | } | ||
58 | seq_printf(s, "}\n"); | ||
59 | } | ||
60 | |||
61 | static int wil_vring_debugfs_show(struct seq_file *s, void *data) | ||
62 | { | ||
63 | uint i; | ||
64 | struct wil6210_priv *wil = s->private; | ||
65 | |||
66 | wil_print_vring(s, wil, "rx", &wil->vring_rx); | ||
67 | |||
68 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { | ||
69 | struct vring *vring = &(wil->vring_tx[i]); | ||
70 | if (vring->va) { | ||
71 | char name[10]; | ||
72 | snprintf(name, sizeof(name), "tx_%2d", i); | ||
73 | wil_print_vring(s, wil, name, vring); | ||
74 | } | ||
75 | } | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | |||
80 | static int wil_vring_seq_open(struct inode *inode, struct file *file) | ||
81 | { | ||
82 | return single_open(file, wil_vring_debugfs_show, inode->i_private); | ||
83 | } | ||
84 | |||
85 | static const struct file_operations fops_vring = { | ||
86 | .open = wil_vring_seq_open, | ||
87 | .release = single_release, | ||
88 | .read = seq_read, | ||
89 | .llseek = seq_lseek, | ||
90 | }; | ||
91 | |||
92 | static void wil_print_ring(struct seq_file *s, const char *prefix, | ||
93 | void __iomem *off) | ||
94 | { | ||
95 | struct wil6210_priv *wil = s->private; | ||
96 | struct wil6210_mbox_ring r; | ||
97 | int rsize; | ||
98 | uint i; | ||
99 | |||
100 | wil_memcpy_fromio_32(&r, off, sizeof(r)); | ||
101 | wil_mbox_ring_le2cpus(&r); | ||
102 | /* | ||
103 | * we just read memory block from NIC. This memory may be | ||
104 | * garbage. Check validity before using it. | ||
105 | */ | ||
106 | rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); | ||
107 | |||
108 | seq_printf(s, "ring %s = {\n", prefix); | ||
109 | seq_printf(s, " base = 0x%08x\n", r.base); | ||
110 | seq_printf(s, " size = 0x%04x bytes -> %d entries\n", r.size, rsize); | ||
111 | seq_printf(s, " tail = 0x%08x\n", r.tail); | ||
112 | seq_printf(s, " head = 0x%08x\n", r.head); | ||
113 | seq_printf(s, " entry size = %d\n", r.entry_size); | ||
114 | |||
115 | if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { | ||
116 | seq_printf(s, " ??? size is not multiple of %zd, garbage?\n", | ||
117 | sizeof(struct wil6210_mbox_ring_desc)); | ||
118 | goto out; | ||
119 | } | ||
120 | |||
121 | if (!wmi_addr(wil, r.base) || | ||
122 | !wmi_addr(wil, r.tail) || | ||
123 | !wmi_addr(wil, r.head)) { | ||
124 | seq_printf(s, " ??? pointers are garbage?\n"); | ||
125 | goto out; | ||
126 | } | ||
127 | |||
128 | for (i = 0; i < rsize; i++) { | ||
129 | struct wil6210_mbox_ring_desc d; | ||
130 | struct wil6210_mbox_hdr hdr; | ||
131 | size_t delta = i * sizeof(d); | ||
132 | void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; | ||
133 | |||
134 | wil_memcpy_fromio_32(&d, x, sizeof(d)); | ||
135 | |||
136 | seq_printf(s, " [%2x] %s %s%s 0x%08x", i, | ||
137 | d.sync ? "F" : "E", | ||
138 | (r.tail - r.base == delta) ? "t" : " ", | ||
139 | (r.head - r.base == delta) ? "h" : " ", | ||
140 | le32_to_cpu(d.addr)); | ||
141 | if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { | ||
142 | u16 len = le16_to_cpu(hdr.len); | ||
143 | seq_printf(s, " -> %04x %04x %04x %02x\n", | ||
144 | le16_to_cpu(hdr.seq), len, | ||
145 | le16_to_cpu(hdr.type), hdr.flags); | ||
146 | if (len <= MAX_MBOXITEM_SIZE) { | ||
147 | int n = 0; | ||
148 | unsigned char printbuf[16 * 3 + 2]; | ||
149 | unsigned char databuf[MAX_MBOXITEM_SIZE]; | ||
150 | void __iomem *src = wmi_buffer(wil, d.addr) + | ||
151 | sizeof(struct wil6210_mbox_hdr); | ||
152 | /* | ||
153 | * No need to check @src for validity - | ||
154 | * we already validated @d.addr while | ||
155 | * reading header | ||
156 | */ | ||
157 | wil_memcpy_fromio_32(databuf, src, len); | ||
158 | while (n < len) { | ||
159 | int l = min(len - n, 16); | ||
160 | hex_dump_to_buffer(databuf + n, l, | ||
161 | 16, 1, printbuf, | ||
162 | sizeof(printbuf), | ||
163 | false); | ||
164 | seq_printf(s, " : %s\n", printbuf); | ||
165 | n += l; | ||
166 | } | ||
167 | } | ||
168 | } else { | ||
169 | seq_printf(s, "\n"); | ||
170 | } | ||
171 | } | ||
172 | out: | ||
173 | seq_printf(s, "}\n"); | ||
174 | } | ||
175 | |||
176 | static int wil_mbox_debugfs_show(struct seq_file *s, void *data) | ||
177 | { | ||
178 | struct wil6210_priv *wil = s->private; | ||
179 | |||
180 | wil_print_ring(s, "tx", wil->csr + HOST_MBOX + | ||
181 | offsetof(struct wil6210_mbox_ctl, tx)); | ||
182 | wil_print_ring(s, "rx", wil->csr + HOST_MBOX + | ||
183 | offsetof(struct wil6210_mbox_ctl, rx)); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static int wil_mbox_seq_open(struct inode *inode, struct file *file) | ||
189 | { | ||
190 | return single_open(file, wil_mbox_debugfs_show, inode->i_private); | ||
191 | } | ||
192 | |||
193 | static const struct file_operations fops_mbox = { | ||
194 | .open = wil_mbox_seq_open, | ||
195 | .release = single_release, | ||
196 | .read = seq_read, | ||
197 | .llseek = seq_lseek, | ||
198 | }; | ||
199 | |||
200 | static int wil_debugfs_iomem_x32_set(void *data, u64 val) | ||
201 | { | ||
202 | iowrite32(val, (void __iomem *)data); | ||
203 | wmb(); /* make sure write propagated to HW */ | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | static int wil_debugfs_iomem_x32_get(void *data, u64 *val) | ||
209 | { | ||
210 | *val = ioread32((void __iomem *)data); | ||
211 | |||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, | ||
216 | wil_debugfs_iomem_x32_set, "0x%08llx\n"); | ||
217 | |||
218 | static struct dentry *wil_debugfs_create_iomem_x32(const char *name, | ||
219 | mode_t mode, | ||
220 | struct dentry *parent, | ||
221 | void __iomem *value) | ||
222 | { | ||
223 | return debugfs_create_file(name, mode, parent, (void * __force)value, | ||
224 | &fops_iomem_x32); | ||
225 | } | ||
226 | |||
227 | static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, | ||
228 | const char *name, | ||
229 | struct dentry *parent, u32 off) | ||
230 | { | ||
231 | struct dentry *d = debugfs_create_dir(name, parent); | ||
232 | |||
233 | if (IS_ERR_OR_NULL(d)) | ||
234 | return -ENODEV; | ||
235 | |||
236 | wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, | ||
237 | wil->csr + off); | ||
238 | wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, | ||
239 | wil->csr + off + 4); | ||
240 | wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, | ||
241 | wil->csr + off + 8); | ||
242 | wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, | ||
243 | wil->csr + off + 12); | ||
244 | wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, | ||
245 | wil->csr + off + 16); | ||
246 | wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, | ||
247 | wil->csr + off + 20); | ||
248 | wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, | ||
249 | wil->csr + off + 24); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, | ||
255 | struct dentry *parent) | ||
256 | { | ||
257 | struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); | ||
258 | |||
259 | if (IS_ERR_OR_NULL(d)) | ||
260 | return -ENODEV; | ||
261 | |||
262 | wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + | ||
263 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); | ||
264 | wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + | ||
265 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); | ||
266 | wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + | ||
267 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); | ||
268 | |||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, | ||
273 | struct dentry *parent) | ||
274 | { | ||
275 | struct dentry *d = debugfs_create_dir("ITR_CNT", parent); | ||
276 | |||
277 | if (IS_ERR_OR_NULL(d)) | ||
278 | return -ENODEV; | ||
279 | |||
280 | wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + | ||
281 | HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); | ||
282 | wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + | ||
283 | HOSTADDR(RGF_DMA_ITR_CNT_DATA)); | ||
284 | wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + | ||
285 | HOSTADDR(RGF_DMA_ITR_CNT_CRL)); | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static int wil_memread_debugfs_show(struct seq_file *s, void *data) | ||
291 | { | ||
292 | struct wil6210_priv *wil = s->private; | ||
293 | void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr)); | ||
294 | |||
295 | if (a) | ||
296 | seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); | ||
297 | else | ||
298 | seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | static int wil_memread_seq_open(struct inode *inode, struct file *file) | ||
304 | { | ||
305 | return single_open(file, wil_memread_debugfs_show, inode->i_private); | ||
306 | } | ||
307 | |||
308 | static const struct file_operations fops_memread = { | ||
309 | .open = wil_memread_seq_open, | ||
310 | .release = single_release, | ||
311 | .read = seq_read, | ||
312 | .llseek = seq_lseek, | ||
313 | }; | ||
314 | |||
315 | static int wil_default_open(struct inode *inode, struct file *file) | ||
316 | { | ||
317 | if (inode->i_private) | ||
318 | file->private_data = inode->i_private; | ||
319 | |||
320 | return 0; | ||
321 | } | ||
322 | |||
323 | static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, | ||
324 | size_t count, loff_t *ppos) | ||
325 | { | ||
326 | enum { max_count = 4096 }; | ||
327 | struct debugfs_blob_wrapper *blob = file->private_data; | ||
328 | loff_t pos = *ppos; | ||
329 | size_t available = blob->size; | ||
330 | void *buf; | ||
331 | size_t ret; | ||
332 | |||
333 | if (pos < 0) | ||
334 | return -EINVAL; | ||
335 | |||
336 | if (pos >= available || !count) | ||
337 | return 0; | ||
338 | |||
339 | if (count > available - pos) | ||
340 | count = available - pos; | ||
341 | if (count > max_count) | ||
342 | count = max_count; | ||
343 | |||
344 | buf = kmalloc(count, GFP_KERNEL); | ||
345 | if (!buf) | ||
346 | return -ENOMEM; | ||
347 | |||
348 | wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + | ||
349 | pos, count); | ||
350 | |||
351 | ret = copy_to_user(user_buf, buf, count); | ||
352 | kfree(buf); | ||
353 | if (ret == count) | ||
354 | return -EFAULT; | ||
355 | |||
356 | count -= ret; | ||
357 | *ppos = pos + count; | ||
358 | |||
359 | return count; | ||
360 | } | ||
361 | |||
362 | static const struct file_operations fops_ioblob = { | ||
363 | .read = wil_read_file_ioblob, | ||
364 | .open = wil_default_open, | ||
365 | .llseek = default_llseek, | ||
366 | }; | ||
367 | |||
368 | static | ||
369 | struct dentry *wil_debugfs_create_ioblob(const char *name, | ||
370 | mode_t mode, | ||
371 | struct dentry *parent, | ||
372 | struct debugfs_blob_wrapper *blob) | ||
373 | { | ||
374 | return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); | ||
375 | } | ||
376 | /*---reset---*/ | ||
377 | static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, | ||
378 | size_t len, loff_t *ppos) | ||
379 | { | ||
380 | struct wil6210_priv *wil = file->private_data; | ||
381 | struct net_device *ndev = wil_to_ndev(wil); | ||
382 | |||
383 | /** | ||
384 | * BUG: | ||
385 | * this code does NOT sync device state with the rest of system | ||
386 | * use with care, debug only!!! | ||
387 | */ | ||
388 | rtnl_lock(); | ||
389 | dev_close(ndev); | ||
390 | ndev->flags &= ~IFF_UP; | ||
391 | rtnl_unlock(); | ||
392 | wil_reset(wil); | ||
393 | |||
394 | return len; | ||
395 | } | ||
396 | |||
397 | static const struct file_operations fops_reset = { | ||
398 | .write = wil_write_file_reset, | ||
399 | .open = wil_default_open, | ||
400 | }; | ||
401 | /*---------Tx descriptor------------*/ | ||
402 | |||
403 | static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) | ||
404 | { | ||
405 | struct wil6210_priv *wil = s->private; | ||
406 | struct vring *vring = &(wil->vring_tx[0]); | ||
407 | |||
408 | if (!vring->va) { | ||
409 | seq_printf(s, "No Tx VRING\n"); | ||
410 | return 0; | ||
411 | } | ||
412 | |||
413 | if (dbg_txdesc_index < vring->size) { | ||
414 | volatile struct vring_tx_desc *d = | ||
415 | &(vring->va[dbg_txdesc_index].tx); | ||
416 | volatile u32 *u = (volatile u32 *)d; | ||
417 | struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; | ||
418 | |||
419 | seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); | ||
420 | seq_printf(s, " MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", | ||
421 | u[0], u[1], u[2], u[3]); | ||
422 | seq_printf(s, " DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", | ||
423 | u[4], u[5], u[6], u[7]); | ||
424 | seq_printf(s, " SKB = %p\n", skb); | ||
425 | |||
426 | if (skb) { | ||
427 | unsigned char printbuf[16 * 3 + 2]; | ||
428 | int i = 0; | ||
429 | int len = skb_headlen(skb); | ||
430 | void *p = skb->data; | ||
431 | |||
432 | seq_printf(s, " len = %d\n", len); | ||
433 | |||
434 | while (i < len) { | ||
435 | int l = min(len - i, 16); | ||
436 | hex_dump_to_buffer(p + i, l, 16, 1, printbuf, | ||
437 | sizeof(printbuf), false); | ||
438 | seq_printf(s, " : %s\n", printbuf); | ||
439 | i += l; | ||
440 | } | ||
441 | } | ||
442 | seq_printf(s, "}\n"); | ||
443 | } else { | ||
444 | seq_printf(s, "TxDesc index (%d) >= size (%d)\n", | ||
445 | dbg_txdesc_index, vring->size); | ||
446 | } | ||
447 | |||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static int wil_txdesc_seq_open(struct inode *inode, struct file *file) | ||
452 | { | ||
453 | return single_open(file, wil_txdesc_debugfs_show, inode->i_private); | ||
454 | } | ||
455 | |||
456 | static const struct file_operations fops_txdesc = { | ||
457 | .open = wil_txdesc_seq_open, | ||
458 | .release = single_release, | ||
459 | .read = seq_read, | ||
460 | .llseek = seq_lseek, | ||
461 | }; | ||
462 | |||
463 | /*---------beamforming------------*/ | ||
464 | static int wil_bf_debugfs_show(struct seq_file *s, void *data) | ||
465 | { | ||
466 | struct wil6210_priv *wil = s->private; | ||
467 | seq_printf(s, | ||
468 | "TSF : 0x%016llx\n" | ||
469 | "TxMCS : %d\n" | ||
470 | "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", | ||
471 | wil->stats.tsf, wil->stats.bf_mcs, | ||
472 | wil->stats.my_rx_sector, wil->stats.my_tx_sector, | ||
473 | wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | static int wil_bf_seq_open(struct inode *inode, struct file *file) | ||
478 | { | ||
479 | return single_open(file, wil_bf_debugfs_show, inode->i_private); | ||
480 | } | ||
481 | |||
482 | static const struct file_operations fops_bf = { | ||
483 | .open = wil_bf_seq_open, | ||
484 | .release = single_release, | ||
485 | .read = seq_read, | ||
486 | .llseek = seq_lseek, | ||
487 | }; | ||
488 | /*---------SSID------------*/ | ||
489 | static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, | ||
490 | size_t count, loff_t *ppos) | ||
491 | { | ||
492 | struct wil6210_priv *wil = file->private_data; | ||
493 | struct wireless_dev *wdev = wil_to_wdev(wil); | ||
494 | |||
495 | return simple_read_from_buffer(user_buf, count, ppos, | ||
496 | wdev->ssid, wdev->ssid_len); | ||
497 | } | ||
498 | |||
499 | static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, | ||
500 | size_t count, loff_t *ppos) | ||
501 | { | ||
502 | struct wil6210_priv *wil = file->private_data; | ||
503 | struct wireless_dev *wdev = wil_to_wdev(wil); | ||
504 | struct net_device *ndev = wil_to_ndev(wil); | ||
505 | |||
506 | if (*ppos != 0) { | ||
507 | wil_err(wil, "Unable to set SSID substring from [%d]\n", | ||
508 | (int)*ppos); | ||
509 | return -EINVAL; | ||
510 | } | ||
511 | |||
512 | if (count > sizeof(wdev->ssid)) { | ||
513 | wil_err(wil, "SSID too long, len = %d\n", (int)count); | ||
514 | return -EINVAL; | ||
515 | } | ||
516 | if (netif_running(ndev)) { | ||
517 | wil_err(wil, "Unable to change SSID on running interface\n"); | ||
518 | return -EINVAL; | ||
519 | } | ||
520 | |||
521 | wdev->ssid_len = count; | ||
522 | return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, | ||
523 | buf, count); | ||
524 | } | ||
525 | |||
526 | static const struct file_operations fops_ssid = { | ||
527 | .read = wil_read_file_ssid, | ||
528 | .write = wil_write_file_ssid, | ||
529 | .open = wil_default_open, | ||
530 | }; | ||
531 | |||
532 | /*----------------*/ | ||
533 | int wil6210_debugfs_init(struct wil6210_priv *wil) | ||
534 | { | ||
535 | struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, | ||
536 | wil_to_wiphy(wil)->debugfsdir); | ||
537 | |||
538 | if (IS_ERR_OR_NULL(dbg)) | ||
539 | return -ENODEV; | ||
540 | |||
541 | debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); | ||
542 | debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); | ||
543 | debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); | ||
544 | debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, | ||
545 | &dbg_txdesc_index); | ||
546 | debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); | ||
547 | debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); | ||
548 | debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, | ||
549 | &wil->secure_pcp); | ||
550 | |||
551 | wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, | ||
552 | HOSTADDR(RGF_USER_USER_ICR)); | ||
553 | wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, | ||
554 | HOSTADDR(RGF_DMA_EP_TX_ICR)); | ||
555 | wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, | ||
556 | HOSTADDR(RGF_DMA_EP_RX_ICR)); | ||
557 | wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, | ||
558 | HOSTADDR(RGF_DMA_EP_MISC_ICR)); | ||
559 | wil6210_debugfs_create_pseudo_ISR(wil, dbg); | ||
560 | wil6210_debugfs_create_ITR_CNT(wil, dbg); | ||
561 | |||
562 | debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); | ||
563 | debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); | ||
564 | |||
565 | debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); | ||
566 | |||
567 | wil->rgf_blob.data = (void * __force)wil->csr + 0; | ||
568 | wil->rgf_blob.size = 0xa000; | ||
569 | wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); | ||
570 | |||
571 | wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; | ||
572 | wil->fw_code_blob.size = 0x40000; | ||
573 | wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, | ||
574 | &wil->fw_code_blob); | ||
575 | |||
576 | wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; | ||
577 | wil->fw_data_blob.size = 0x8000; | ||
578 | wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, | ||
579 | &wil->fw_data_blob); | ||
580 | |||
581 | wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; | ||
582 | wil->fw_peri_blob.size = 0x18000; | ||
583 | wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, | ||
584 | &wil->fw_peri_blob); | ||
585 | |||
586 | wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; | ||
587 | wil->uc_code_blob.size = 0x10000; | ||
588 | wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, | ||
589 | &wil->uc_code_blob); | ||
590 | |||
591 | wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; | ||
592 | wil->uc_data_blob.size = 0x4000; | ||
593 | wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, | ||
594 | &wil->uc_data_blob); | ||
595 | |||
596 | return 0; | ||
597 | } | ||
598 | |||
599 | void wil6210_debugfs_remove(struct wil6210_priv *wil) | ||
600 | { | ||
601 | debugfs_remove_recursive(wil->debug); | ||
602 | wil->debug = NULL; | ||
603 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c new file mode 100644 index 000000000000..38049da71049 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/interrupt.c | |||
@@ -0,0 +1,471 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/interrupt.h> | ||
18 | |||
19 | #include "wil6210.h" | ||
20 | |||
21 | /** | ||
22 | * Theory of operation: | ||
23 | * | ||
24 | * There is ISR pseudo-cause register, | ||
25 | * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE | ||
26 | * Its bits represents OR'ed bits from 3 real ISR registers: | ||
27 | * TX, RX, and MISC. | ||
28 | * | ||
29 | * Registers may be configured to either "write 1 to clear" or | ||
30 | * "clear on read" mode | ||
31 | * | ||
32 | * When handling interrupt, one have to mask/unmask interrupts for the | ||
33 | * real ISR registers, or hardware may malfunction. | ||
34 | * | ||
35 | */ | ||
36 | |||
37 | #define WIL6210_IRQ_DISABLE (0xFFFFFFFFUL) | ||
38 | #define WIL6210_IMC_RX BIT_DMA_EP_RX_ICR_RX_DONE | ||
39 | #define WIL6210_IMC_TX (BIT_DMA_EP_TX_ICR_TX_DONE | \ | ||
40 | BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) | ||
41 | #define WIL6210_IMC_MISC (ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) | ||
42 | |||
43 | #define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ | ||
44 | BIT_DMA_PSEUDO_CAUSE_TX | \ | ||
45 | BIT_DMA_PSEUDO_CAUSE_MISC)) | ||
46 | |||
47 | #if defined(CONFIG_WIL6210_ISR_COR) | ||
48 | /* configure to Clear-On-Read mode */ | ||
49 | #define WIL_ICR_ICC_VALUE (0xFFFFFFFFUL) | ||
50 | |||
51 | static inline void wil_icr_clear(u32 x, void __iomem *addr) | ||
52 | { | ||
53 | |||
54 | } | ||
55 | #else /* defined(CONFIG_WIL6210_ISR_COR) */ | ||
56 | /* configure to Write-1-to-Clear mode */ | ||
57 | #define WIL_ICR_ICC_VALUE (0UL) | ||
58 | |||
59 | static inline void wil_icr_clear(u32 x, void __iomem *addr) | ||
60 | { | ||
61 | iowrite32(x, addr); | ||
62 | } | ||
63 | #endif /* defined(CONFIG_WIL6210_ISR_COR) */ | ||
64 | |||
65 | static inline u32 wil_ioread32_and_clear(void __iomem *addr) | ||
66 | { | ||
67 | u32 x = ioread32(addr); | ||
68 | |||
69 | wil_icr_clear(x, addr); | ||
70 | |||
71 | return x; | ||
72 | } | ||
73 | |||
74 | static void wil6210_mask_irq_tx(struct wil6210_priv *wil) | ||
75 | { | ||
76 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
77 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
78 | offsetof(struct RGF_ICR, IMS)); | ||
79 | } | ||
80 | |||
81 | static void wil6210_mask_irq_rx(struct wil6210_priv *wil) | ||
82 | { | ||
83 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
84 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
85 | offsetof(struct RGF_ICR, IMS)); | ||
86 | } | ||
87 | |||
88 | static void wil6210_mask_irq_misc(struct wil6210_priv *wil) | ||
89 | { | ||
90 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
91 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
92 | offsetof(struct RGF_ICR, IMS)); | ||
93 | } | ||
94 | |||
95 | static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) | ||
96 | { | ||
97 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
98 | |||
99 | iowrite32(WIL6210_IRQ_DISABLE, wil->csr + | ||
100 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); | ||
101 | |||
102 | clear_bit(wil_status_irqen, &wil->status); | ||
103 | } | ||
104 | |||
105 | static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) | ||
106 | { | ||
107 | iowrite32(WIL6210_IMC_TX, wil->csr + | ||
108 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
109 | offsetof(struct RGF_ICR, IMC)); | ||
110 | } | ||
111 | |||
112 | static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) | ||
113 | { | ||
114 | iowrite32(WIL6210_IMC_RX, wil->csr + | ||
115 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
116 | offsetof(struct RGF_ICR, IMC)); | ||
117 | } | ||
118 | |||
119 | static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) | ||
120 | { | ||
121 | iowrite32(WIL6210_IMC_MISC, wil->csr + | ||
122 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
123 | offsetof(struct RGF_ICR, IMC)); | ||
124 | } | ||
125 | |||
126 | static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) | ||
127 | { | ||
128 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
129 | |||
130 | set_bit(wil_status_irqen, &wil->status); | ||
131 | |||
132 | iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + | ||
133 | HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); | ||
134 | } | ||
135 | |||
136 | void wil6210_disable_irq(struct wil6210_priv *wil) | ||
137 | { | ||
138 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
139 | |||
140 | wil6210_mask_irq_tx(wil); | ||
141 | wil6210_mask_irq_rx(wil); | ||
142 | wil6210_mask_irq_misc(wil); | ||
143 | wil6210_mask_irq_pseudo(wil); | ||
144 | } | ||
145 | |||
146 | void wil6210_enable_irq(struct wil6210_priv *wil) | ||
147 | { | ||
148 | wil_dbg_IRQ(wil, "%s()\n", __func__); | ||
149 | |||
150 | iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
151 | offsetof(struct RGF_ICR, ICC)); | ||
152 | iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
153 | offsetof(struct RGF_ICR, ICC)); | ||
154 | iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
155 | offsetof(struct RGF_ICR, ICC)); | ||
156 | |||
157 | wil6210_unmask_irq_pseudo(wil); | ||
158 | wil6210_unmask_irq_tx(wil); | ||
159 | wil6210_unmask_irq_rx(wil); | ||
160 | wil6210_unmask_irq_misc(wil); | ||
161 | } | ||
162 | |||
163 | static irqreturn_t wil6210_irq_rx(int irq, void *cookie) | ||
164 | { | ||
165 | struct wil6210_priv *wil = cookie; | ||
166 | u32 isr = wil_ioread32_and_clear(wil->csr + | ||
167 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
168 | offsetof(struct RGF_ICR, ICR)); | ||
169 | |||
170 | wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); | ||
171 | |||
172 | if (!isr) { | ||
173 | wil_err(wil, "spurious IRQ: RX\n"); | ||
174 | return IRQ_NONE; | ||
175 | } | ||
176 | |||
177 | wil6210_mask_irq_rx(wil); | ||
178 | |||
179 | if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { | ||
180 | wil_dbg_IRQ(wil, "RX done\n"); | ||
181 | isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; | ||
182 | wil_rx_handle(wil); | ||
183 | } | ||
184 | |||
185 | if (isr) | ||
186 | wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); | ||
187 | |||
188 | wil6210_unmask_irq_rx(wil); | ||
189 | |||
190 | return IRQ_HANDLED; | ||
191 | } | ||
192 | |||
193 | static irqreturn_t wil6210_irq_tx(int irq, void *cookie) | ||
194 | { | ||
195 | struct wil6210_priv *wil = cookie; | ||
196 | u32 isr = wil_ioread32_and_clear(wil->csr + | ||
197 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
198 | offsetof(struct RGF_ICR, ICR)); | ||
199 | |||
200 | wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); | ||
201 | |||
202 | if (!isr) { | ||
203 | wil_err(wil, "spurious IRQ: TX\n"); | ||
204 | return IRQ_NONE; | ||
205 | } | ||
206 | |||
207 | wil6210_mask_irq_tx(wil); | ||
208 | |||
209 | if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { | ||
210 | uint i; | ||
211 | wil_dbg_IRQ(wil, "TX done\n"); | ||
212 | isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; | ||
213 | for (i = 0; i < 24; i++) { | ||
214 | u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); | ||
215 | if (isr & mask) { | ||
216 | isr &= ~mask; | ||
217 | wil_dbg_IRQ(wil, "TX done(%i)\n", i); | ||
218 | wil_tx_complete(wil, i); | ||
219 | } | ||
220 | } | ||
221 | } | ||
222 | |||
223 | if (isr) | ||
224 | wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); | ||
225 | |||
226 | wil6210_unmask_irq_tx(wil); | ||
227 | |||
228 | return IRQ_HANDLED; | ||
229 | } | ||
230 | |||
231 | static irqreturn_t wil6210_irq_misc(int irq, void *cookie) | ||
232 | { | ||
233 | struct wil6210_priv *wil = cookie; | ||
234 | u32 isr = wil_ioread32_and_clear(wil->csr + | ||
235 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
236 | offsetof(struct RGF_ICR, ICR)); | ||
237 | |||
238 | wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); | ||
239 | |||
240 | if (!isr) { | ||
241 | wil_err(wil, "spurious IRQ: MISC\n"); | ||
242 | return IRQ_NONE; | ||
243 | } | ||
244 | |||
245 | wil6210_mask_irq_misc(wil); | ||
246 | |||
247 | if (isr & ISR_MISC_FW_READY) { | ||
248 | wil_dbg_IRQ(wil, "IRQ: FW ready\n"); | ||
249 | /** | ||
250 | * Actual FW ready indicated by the | ||
251 | * WMI_FW_READY_EVENTID | ||
252 | */ | ||
253 | isr &= ~ISR_MISC_FW_READY; | ||
254 | } | ||
255 | |||
256 | wil->isr_misc = isr; | ||
257 | |||
258 | if (isr) { | ||
259 | return IRQ_WAKE_THREAD; | ||
260 | } else { | ||
261 | wil6210_unmask_irq_misc(wil); | ||
262 | return IRQ_HANDLED; | ||
263 | } | ||
264 | } | ||
265 | |||
266 | static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) | ||
267 | { | ||
268 | struct wil6210_priv *wil = cookie; | ||
269 | u32 isr = wil->isr_misc; | ||
270 | |||
271 | wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); | ||
272 | |||
273 | if (isr & ISR_MISC_MBOX_EVT) { | ||
274 | wil_dbg_IRQ(wil, "MBOX event\n"); | ||
275 | wmi_recv_cmd(wil); | ||
276 | isr &= ~ISR_MISC_MBOX_EVT; | ||
277 | } | ||
278 | |||
279 | if (isr) | ||
280 | wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); | ||
281 | |||
282 | wil->isr_misc = 0; | ||
283 | |||
284 | wil6210_unmask_irq_misc(wil); | ||
285 | |||
286 | return IRQ_HANDLED; | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * thread IRQ handler | ||
291 | */ | ||
292 | static irqreturn_t wil6210_thread_irq(int irq, void *cookie) | ||
293 | { | ||
294 | struct wil6210_priv *wil = cookie; | ||
295 | |||
296 | wil_dbg_IRQ(wil, "Thread IRQ\n"); | ||
297 | /* Discover real IRQ cause */ | ||
298 | if (wil->isr_misc) | ||
299 | wil6210_irq_misc_thread(irq, cookie); | ||
300 | |||
301 | wil6210_unmask_irq_pseudo(wil); | ||
302 | |||
303 | return IRQ_HANDLED; | ||
304 | } | ||
305 | |||
306 | /* DEBUG | ||
307 | * There is subtle bug in hardware that causes IRQ to raise when it should be | ||
308 | * masked. It is quite rare and hard to debug. | ||
309 | * | ||
310 | * Catch irq issue if it happens and print all I can. | ||
311 | */ | ||
312 | static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) | ||
313 | { | ||
314 | if (!test_bit(wil_status_irqen, &wil->status)) { | ||
315 | u32 icm_rx = wil_ioread32_and_clear(wil->csr + | ||
316 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
317 | offsetof(struct RGF_ICR, ICM)); | ||
318 | u32 icr_rx = wil_ioread32_and_clear(wil->csr + | ||
319 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
320 | offsetof(struct RGF_ICR, ICR)); | ||
321 | u32 imv_rx = ioread32(wil->csr + | ||
322 | HOSTADDR(RGF_DMA_EP_RX_ICR) + | ||
323 | offsetof(struct RGF_ICR, IMV)); | ||
324 | u32 icm_tx = wil_ioread32_and_clear(wil->csr + | ||
325 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
326 | offsetof(struct RGF_ICR, ICM)); | ||
327 | u32 icr_tx = wil_ioread32_and_clear(wil->csr + | ||
328 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
329 | offsetof(struct RGF_ICR, ICR)); | ||
330 | u32 imv_tx = ioread32(wil->csr + | ||
331 | HOSTADDR(RGF_DMA_EP_TX_ICR) + | ||
332 | offsetof(struct RGF_ICR, IMV)); | ||
333 | u32 icm_misc = wil_ioread32_and_clear(wil->csr + | ||
334 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
335 | offsetof(struct RGF_ICR, ICM)); | ||
336 | u32 icr_misc = wil_ioread32_and_clear(wil->csr + | ||
337 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
338 | offsetof(struct RGF_ICR, ICR)); | ||
339 | u32 imv_misc = ioread32(wil->csr + | ||
340 | HOSTADDR(RGF_DMA_EP_MISC_ICR) + | ||
341 | offsetof(struct RGF_ICR, IMV)); | ||
342 | wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" | ||
343 | "Rx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" | ||
344 | "Tx icm:icr:imv 0x%08x 0x%08x 0x%08x\n" | ||
345 | "Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", | ||
346 | pseudo_cause, | ||
347 | icm_rx, icr_rx, imv_rx, | ||
348 | icm_tx, icr_tx, imv_tx, | ||
349 | icm_misc, icr_misc, imv_misc); | ||
350 | |||
351 | return -EINVAL; | ||
352 | } | ||
353 | |||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | static irqreturn_t wil6210_hardirq(int irq, void *cookie) | ||
358 | { | ||
359 | irqreturn_t rc = IRQ_HANDLED; | ||
360 | struct wil6210_priv *wil = cookie; | ||
361 | u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); | ||
362 | |||
363 | /** | ||
364 | * pseudo_cause is Clear-On-Read, no need to ACK | ||
365 | */ | ||
366 | if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) | ||
367 | return IRQ_NONE; | ||
368 | |||
369 | /* FIXME: IRQ mask debug */ | ||
370 | if (wil6210_debug_irq_mask(wil, pseudo_cause)) | ||
371 | return IRQ_NONE; | ||
372 | |||
373 | wil6210_mask_irq_pseudo(wil); | ||
374 | |||
375 | /* Discover real IRQ cause | ||
376 | * There are 2 possible phases for every IRQ: | ||
377 | * - hard IRQ handler called right here | ||
378 | * - threaded handler called later | ||
379 | * | ||
380 | * Hard IRQ handler reads and clears ISR. | ||
381 | * | ||
382 | * If threaded handler requested, hard IRQ handler | ||
383 | * returns IRQ_WAKE_THREAD and saves ISR register value | ||
384 | * for the threaded handler use. | ||
385 | * | ||
386 | * voting for wake thread - need at least 1 vote | ||
387 | */ | ||
388 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && | ||
389 | (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) | ||
390 | rc = IRQ_WAKE_THREAD; | ||
391 | |||
392 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && | ||
393 | (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) | ||
394 | rc = IRQ_WAKE_THREAD; | ||
395 | |||
396 | if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && | ||
397 | (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) | ||
398 | rc = IRQ_WAKE_THREAD; | ||
399 | |||
400 | /* if thread is requested, it will unmask IRQ */ | ||
401 | if (rc != IRQ_WAKE_THREAD) | ||
402 | wil6210_unmask_irq_pseudo(wil); | ||
403 | |||
404 | wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); | ||
405 | |||
406 | return rc; | ||
407 | } | ||
408 | |||
409 | static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) | ||
410 | { | ||
411 | int rc; | ||
412 | /* | ||
413 | * IRQ's are in the following order: | ||
414 | * - Tx | ||
415 | * - Rx | ||
416 | * - Misc | ||
417 | */ | ||
418 | |||
419 | rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, | ||
420 | WIL_NAME"_tx", wil); | ||
421 | if (rc) | ||
422 | return rc; | ||
423 | |||
424 | rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, | ||
425 | WIL_NAME"_rx", wil); | ||
426 | if (rc) | ||
427 | goto free0; | ||
428 | |||
429 | rc = request_threaded_irq(irq + 2, wil6210_irq_misc, | ||
430 | wil6210_irq_misc_thread, | ||
431 | IRQF_SHARED, WIL_NAME"_misc", wil); | ||
432 | if (rc) | ||
433 | goto free1; | ||
434 | |||
435 | return 0; | ||
436 | /* error branch */ | ||
437 | free1: | ||
438 | free_irq(irq + 1, wil); | ||
439 | free0: | ||
440 | free_irq(irq, wil); | ||
441 | |||
442 | return rc; | ||
443 | } | ||
444 | |||
445 | int wil6210_init_irq(struct wil6210_priv *wil, int irq) | ||
446 | { | ||
447 | int rc; | ||
448 | if (wil->n_msi == 3) | ||
449 | rc = wil6210_request_3msi(wil, irq); | ||
450 | else | ||
451 | rc = request_threaded_irq(irq, wil6210_hardirq, | ||
452 | wil6210_thread_irq, | ||
453 | wil->n_msi ? 0 : IRQF_SHARED, | ||
454 | WIL_NAME, wil); | ||
455 | if (rc) | ||
456 | return rc; | ||
457 | |||
458 | wil6210_enable_irq(wil); | ||
459 | |||
460 | return 0; | ||
461 | } | ||
462 | |||
463 | void wil6210_fini_irq(struct wil6210_priv *wil, int irq) | ||
464 | { | ||
465 | wil6210_disable_irq(wil); | ||
466 | free_irq(irq, wil); | ||
467 | if (wil->n_msi == 3) { | ||
468 | free_irq(irq + 1, wil); | ||
469 | free_irq(irq + 2, wil); | ||
470 | } | ||
471 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c new file mode 100644 index 000000000000..95fcd361322b --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/main.c | |||
@@ -0,0 +1,407 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/ieee80211.h> | ||
21 | #include <linux/wireless.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | #include <linux/if_arp.h> | ||
25 | |||
26 | #include "wil6210.h" | ||
27 | |||
28 | /* | ||
29 | * Due to a hardware issue, | ||
30 | * one has to read/write to/from NIC in 32-bit chunks; | ||
31 | * regular memcpy_fromio and siblings will | ||
32 | * not work on 64-bit platform - it uses 64-bit transactions | ||
33 | * | ||
34 | * Force 32-bit transactions to enable NIC on 64-bit platforms | ||
35 | * | ||
36 | * To avoid byte swap on big endian host, __raw_{read|write}l | ||
37 | * should be used - {read|write}l would swap bytes to provide | ||
38 | * little endian on PCI value in host endianness. | ||
39 | */ | ||
40 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | ||
41 | size_t count) | ||
42 | { | ||
43 | u32 *d = dst; | ||
44 | const volatile u32 __iomem *s = src; | ||
45 | |||
46 | /* size_t is unsigned, if (count%4 != 0) it will wrap */ | ||
47 | for (count += 4; count > 4; count -= 4) | ||
48 | *d++ = __raw_readl(s++); | ||
49 | } | ||
50 | |||
51 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | ||
52 | size_t count) | ||
53 | { | ||
54 | volatile u32 __iomem *d = dst; | ||
55 | const u32 *s = src; | ||
56 | |||
57 | for (count += 4; count > 4; count -= 4) | ||
58 | __raw_writel(*s++, d++); | ||
59 | } | ||
60 | |||
61 | static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | ||
62 | { | ||
63 | uint i; | ||
64 | struct net_device *ndev = wil_to_ndev(wil); | ||
65 | struct wireless_dev *wdev = wil->wdev; | ||
66 | |||
67 | wil_dbg(wil, "%s()\n", __func__); | ||
68 | |||
69 | wil_link_off(wil); | ||
70 | clear_bit(wil_status_fwconnected, &wil->status); | ||
71 | |||
72 | switch (wdev->sme_state) { | ||
73 | case CFG80211_SME_CONNECTED: | ||
74 | cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
75 | NULL, 0, GFP_KERNEL); | ||
76 | break; | ||
77 | case CFG80211_SME_CONNECTING: | ||
78 | cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, | ||
79 | WLAN_STATUS_UNSPECIFIED_FAILURE, | ||
80 | GFP_KERNEL); | ||
81 | break; | ||
82 | default: | ||
83 | ; | ||
84 | } | ||
85 | |||
86 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) | ||
87 | wil_vring_fini_tx(wil, i); | ||
88 | } | ||
89 | |||
90 | static void wil_disconnect_worker(struct work_struct *work) | ||
91 | { | ||
92 | struct wil6210_priv *wil = container_of(work, | ||
93 | struct wil6210_priv, disconnect_worker); | ||
94 | |||
95 | _wil6210_disconnect(wil, NULL); | ||
96 | } | ||
97 | |||
98 | static void wil_connect_timer_fn(ulong x) | ||
99 | { | ||
100 | struct wil6210_priv *wil = (void *)x; | ||
101 | |||
102 | wil_dbg(wil, "Connect timeout\n"); | ||
103 | |||
104 | /* reschedule to thread context - disconnect won't | ||
105 | * run from atomic context | ||
106 | */ | ||
107 | schedule_work(&wil->disconnect_worker); | ||
108 | } | ||
109 | |||
110 | int wil_priv_init(struct wil6210_priv *wil) | ||
111 | { | ||
112 | wil_dbg(wil, "%s()\n", __func__); | ||
113 | |||
114 | mutex_init(&wil->mutex); | ||
115 | mutex_init(&wil->wmi_mutex); | ||
116 | |||
117 | init_completion(&wil->wmi_ready); | ||
118 | |||
119 | wil->pending_connect_cid = -1; | ||
120 | setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); | ||
121 | |||
122 | INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); | ||
123 | INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); | ||
124 | INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); | ||
125 | |||
126 | INIT_LIST_HEAD(&wil->pending_wmi_ev); | ||
127 | spin_lock_init(&wil->wmi_ev_lock); | ||
128 | |||
129 | wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); | ||
130 | if (!wil->wmi_wq) | ||
131 | return -EAGAIN; | ||
132 | |||
133 | wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); | ||
134 | if (!wil->wmi_wq_conn) { | ||
135 | destroy_workqueue(wil->wmi_wq); | ||
136 | return -EAGAIN; | ||
137 | } | ||
138 | |||
139 | /* make shadow copy of registers that should not change on run time */ | ||
140 | wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, | ||
141 | sizeof(struct wil6210_mbox_ctl)); | ||
142 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); | ||
143 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) | ||
149 | { | ||
150 | del_timer_sync(&wil->connect_timer); | ||
151 | _wil6210_disconnect(wil, bssid); | ||
152 | } | ||
153 | |||
154 | void wil_priv_deinit(struct wil6210_priv *wil) | ||
155 | { | ||
156 | cancel_work_sync(&wil->disconnect_worker); | ||
157 | wil6210_disconnect(wil, NULL); | ||
158 | wmi_event_flush(wil); | ||
159 | destroy_workqueue(wil->wmi_wq_conn); | ||
160 | destroy_workqueue(wil->wmi_wq); | ||
161 | } | ||
162 | |||
163 | static void wil_target_reset(struct wil6210_priv *wil) | ||
164 | { | ||
165 | wil_dbg(wil, "Resetting...\n"); | ||
166 | |||
167 | /* register write */ | ||
168 | #define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) | ||
169 | /* register set = read, OR, write */ | ||
170 | #define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ | ||
171 | wil->csr + HOSTADDR(a)) | ||
172 | |||
173 | /* hpal_perst_from_pad_src_n_mask */ | ||
174 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); | ||
175 | /* car_perst_rst_src_n_mask */ | ||
176 | S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); | ||
177 | |||
178 | W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ | ||
179 | W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ | ||
180 | |||
181 | msleep(100); | ||
182 | |||
183 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); | ||
184 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); | ||
185 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); | ||
186 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); | ||
187 | |||
188 | msleep(100); | ||
189 | |||
190 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); | ||
191 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); | ||
192 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); | ||
193 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | ||
194 | |||
195 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); | ||
196 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); | ||
197 | W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); | ||
198 | |||
199 | msleep(2000); | ||
200 | |||
201 | W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */ | ||
202 | |||
203 | msleep(2000); | ||
204 | |||
205 | wil_dbg(wil, "Reset completed\n"); | ||
206 | |||
207 | #undef W | ||
208 | #undef S | ||
209 | } | ||
210 | |||
211 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) | ||
212 | { | ||
213 | le32_to_cpus(&r->base); | ||
214 | le16_to_cpus(&r->entry_size); | ||
215 | le16_to_cpus(&r->size); | ||
216 | le32_to_cpus(&r->tail); | ||
217 | le32_to_cpus(&r->head); | ||
218 | } | ||
219 | |||
220 | static int wil_wait_for_fw_ready(struct wil6210_priv *wil) | ||
221 | { | ||
222 | ulong to = msecs_to_jiffies(1000); | ||
223 | ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); | ||
224 | if (0 == left) { | ||
225 | wil_err(wil, "Firmware not ready\n"); | ||
226 | return -ETIME; | ||
227 | } else { | ||
228 | wil_dbg(wil, "FW ready after %d ms\n", | ||
229 | jiffies_to_msecs(to-left)); | ||
230 | } | ||
231 | return 0; | ||
232 | } | ||
233 | |||
234 | /* | ||
235 | * We reset all the structures, and we reset the UMAC. | ||
236 | * After calling this routine, you're expected to reload | ||
237 | * the firmware. | ||
238 | */ | ||
239 | int wil_reset(struct wil6210_priv *wil) | ||
240 | { | ||
241 | int rc; | ||
242 | |||
243 | cancel_work_sync(&wil->disconnect_worker); | ||
244 | wil6210_disconnect(wil, NULL); | ||
245 | |||
246 | wmi_event_flush(wil); | ||
247 | |||
248 | flush_workqueue(wil->wmi_wq); | ||
249 | flush_workqueue(wil->wmi_wq_conn); | ||
250 | |||
251 | wil6210_disable_irq(wil); | ||
252 | wil->status = 0; | ||
253 | |||
254 | /* TODO: put MAC in reset */ | ||
255 | wil_target_reset(wil); | ||
256 | |||
257 | /* init after reset */ | ||
258 | wil->pending_connect_cid = -1; | ||
259 | INIT_COMPLETION(wil->wmi_ready); | ||
260 | |||
261 | /* make shadow copy of registers that should not change on run time */ | ||
262 | wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, | ||
263 | sizeof(struct wil6210_mbox_ctl)); | ||
264 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); | ||
265 | wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); | ||
266 | |||
267 | /* TODO: release MAC reset */ | ||
268 | wil6210_enable_irq(wil); | ||
269 | |||
270 | /* we just started MAC, wait for FW ready */ | ||
271 | rc = wil_wait_for_fw_ready(wil); | ||
272 | |||
273 | return rc; | ||
274 | } | ||
275 | |||
276 | |||
277 | void wil_link_on(struct wil6210_priv *wil) | ||
278 | { | ||
279 | struct net_device *ndev = wil_to_ndev(wil); | ||
280 | |||
281 | wil_dbg(wil, "%s()\n", __func__); | ||
282 | |||
283 | netif_carrier_on(ndev); | ||
284 | netif_tx_wake_all_queues(ndev); | ||
285 | } | ||
286 | |||
287 | void wil_link_off(struct wil6210_priv *wil) | ||
288 | { | ||
289 | struct net_device *ndev = wil_to_ndev(wil); | ||
290 | |||
291 | wil_dbg(wil, "%s()\n", __func__); | ||
292 | |||
293 | netif_tx_stop_all_queues(ndev); | ||
294 | netif_carrier_off(ndev); | ||
295 | } | ||
296 | |||
297 | static int __wil_up(struct wil6210_priv *wil) | ||
298 | { | ||
299 | struct net_device *ndev = wil_to_ndev(wil); | ||
300 | struct wireless_dev *wdev = wil->wdev; | ||
301 | struct ieee80211_channel *channel = wdev->preset_chandef.chan; | ||
302 | int rc; | ||
303 | int bi; | ||
304 | u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); | ||
305 | |||
306 | rc = wil_reset(wil); | ||
307 | if (rc) | ||
308 | return rc; | ||
309 | |||
310 | /* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ | ||
311 | wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); | ||
312 | switch (wdev->iftype) { | ||
313 | case NL80211_IFTYPE_STATION: | ||
314 | wil_dbg(wil, "type: STATION\n"); | ||
315 | bi = 0; | ||
316 | ndev->type = ARPHRD_ETHER; | ||
317 | break; | ||
318 | case NL80211_IFTYPE_AP: | ||
319 | wil_dbg(wil, "type: AP\n"); | ||
320 | bi = 100; | ||
321 | ndev->type = ARPHRD_ETHER; | ||
322 | break; | ||
323 | case NL80211_IFTYPE_P2P_CLIENT: | ||
324 | wil_dbg(wil, "type: P2P_CLIENT\n"); | ||
325 | bi = 0; | ||
326 | ndev->type = ARPHRD_ETHER; | ||
327 | break; | ||
328 | case NL80211_IFTYPE_P2P_GO: | ||
329 | wil_dbg(wil, "type: P2P_GO\n"); | ||
330 | bi = 100; | ||
331 | ndev->type = ARPHRD_ETHER; | ||
332 | break; | ||
333 | case NL80211_IFTYPE_MONITOR: | ||
334 | wil_dbg(wil, "type: Monitor\n"); | ||
335 | bi = 0; | ||
336 | ndev->type = ARPHRD_IEEE80211_RADIOTAP; | ||
337 | /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ | ||
338 | break; | ||
339 | default: | ||
340 | return -EOPNOTSUPP; | ||
341 | } | ||
342 | |||
343 | /* Apply profile in the following order: */ | ||
344 | /* SSID and channel for the AP */ | ||
345 | switch (wdev->iftype) { | ||
346 | case NL80211_IFTYPE_AP: | ||
347 | case NL80211_IFTYPE_P2P_GO: | ||
348 | if (wdev->ssid_len == 0) { | ||
349 | wil_err(wil, "SSID not set\n"); | ||
350 | return -EINVAL; | ||
351 | } | ||
352 | wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); | ||
353 | if (channel) | ||
354 | wmi_set_channel(wil, channel->hw_value); | ||
355 | break; | ||
356 | default: | ||
357 | ; | ||
358 | } | ||
359 | |||
360 | /* MAC address - pre-requisite for other commands */ | ||
361 | wmi_set_mac_address(wil, ndev->dev_addr); | ||
362 | |||
363 | /* Set up beaconing if required. */ | ||
364 | rc = wmi_set_bcon(wil, bi, wmi_nettype); | ||
365 | if (rc) | ||
366 | return rc; | ||
367 | |||
368 | /* Rx VRING. After MAC and beacon */ | ||
369 | wil_rx_init(wil); | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | int wil_up(struct wil6210_priv *wil) | ||
375 | { | ||
376 | int rc; | ||
377 | |||
378 | mutex_lock(&wil->mutex); | ||
379 | rc = __wil_up(wil); | ||
380 | mutex_unlock(&wil->mutex); | ||
381 | |||
382 | return rc; | ||
383 | } | ||
384 | |||
385 | static int __wil_down(struct wil6210_priv *wil) | ||
386 | { | ||
387 | if (wil->scan_request) { | ||
388 | cfg80211_scan_done(wil->scan_request, true); | ||
389 | wil->scan_request = NULL; | ||
390 | } | ||
391 | |||
392 | wil6210_disconnect(wil, NULL); | ||
393 | wil_rx_fini(wil); | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | int wil_down(struct wil6210_priv *wil) | ||
399 | { | ||
400 | int rc; | ||
401 | |||
402 | mutex_lock(&wil->mutex); | ||
403 | rc = __wil_down(wil); | ||
404 | mutex_unlock(&wil->mutex); | ||
405 | |||
406 | return rc; | ||
407 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c new file mode 100644 index 000000000000..3068b5cb53a7 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/netdev.c | |||
@@ -0,0 +1,157 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/etherdevice.h> | ||
20 | #include <linux/slab.h> | ||
21 | |||
22 | #include "wil6210.h" | ||
23 | |||
24 | static int wil_open(struct net_device *ndev) | ||
25 | { | ||
26 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
27 | |||
28 | return wil_up(wil); | ||
29 | } | ||
30 | |||
31 | static int wil_stop(struct net_device *ndev) | ||
32 | { | ||
33 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
34 | |||
35 | return wil_down(wil); | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * AC to queue mapping | ||
40 | * | ||
41 | * AC_VO -> queue 3 | ||
42 | * AC_VI -> queue 2 | ||
43 | * AC_BE -> queue 1 | ||
44 | * AC_BK -> queue 0 | ||
45 | */ | ||
46 | static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) | ||
47 | { | ||
48 | static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; | ||
49 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
50 | u16 rc; | ||
51 | |||
52 | skb->priority = cfg80211_classify8021d(skb); | ||
53 | |||
54 | rc = wil_1d_to_queue[skb->priority]; | ||
55 | |||
56 | wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, | ||
57 | (int)rc); | ||
58 | |||
59 | return rc; | ||
60 | } | ||
61 | |||
62 | static const struct net_device_ops wil_netdev_ops = { | ||
63 | .ndo_open = wil_open, | ||
64 | .ndo_stop = wil_stop, | ||
65 | .ndo_start_xmit = wil_start_xmit, | ||
66 | .ndo_select_queue = wil_select_queue, | ||
67 | .ndo_set_mac_address = eth_mac_addr, | ||
68 | .ndo_validate_addr = eth_validate_addr, | ||
69 | }; | ||
70 | |||
71 | void *wil_if_alloc(struct device *dev, void __iomem *csr) | ||
72 | { | ||
73 | struct net_device *ndev; | ||
74 | struct wireless_dev *wdev; | ||
75 | struct wil6210_priv *wil; | ||
76 | struct ieee80211_channel *ch; | ||
77 | int rc = 0; | ||
78 | |||
79 | wdev = wil_cfg80211_init(dev); | ||
80 | if (IS_ERR(wdev)) { | ||
81 | dev_err(dev, "wil_cfg80211_init failed\n"); | ||
82 | return wdev; | ||
83 | } | ||
84 | |||
85 | wil = wdev_to_wil(wdev); | ||
86 | wil->csr = csr; | ||
87 | wil->wdev = wdev; | ||
88 | |||
89 | rc = wil_priv_init(wil); | ||
90 | if (rc) { | ||
91 | dev_err(dev, "wil_priv_init failed\n"); | ||
92 | goto out_wdev; | ||
93 | } | ||
94 | |||
95 | wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ | ||
96 | /* default monitor channel */ | ||
97 | ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; | ||
98 | cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); | ||
99 | |||
100 | ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); | ||
101 | if (!ndev) { | ||
102 | dev_err(dev, "alloc_netdev_mqs failed\n"); | ||
103 | rc = -ENOMEM; | ||
104 | goto out_priv; | ||
105 | } | ||
106 | |||
107 | ndev->netdev_ops = &wil_netdev_ops; | ||
108 | ndev->ieee80211_ptr = wdev; | ||
109 | SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); | ||
110 | wdev->netdev = ndev; | ||
111 | |||
112 | wil_link_off(wil); | ||
113 | |||
114 | return wil; | ||
115 | |||
116 | out_priv: | ||
117 | wil_priv_deinit(wil); | ||
118 | |||
119 | out_wdev: | ||
120 | wil_wdev_free(wil); | ||
121 | |||
122 | return ERR_PTR(rc); | ||
123 | } | ||
124 | |||
125 | void wil_if_free(struct wil6210_priv *wil) | ||
126 | { | ||
127 | struct net_device *ndev = wil_to_ndev(wil); | ||
128 | if (!ndev) | ||
129 | return; | ||
130 | |||
131 | free_netdev(ndev); | ||
132 | wil_priv_deinit(wil); | ||
133 | wil_wdev_free(wil); | ||
134 | } | ||
135 | |||
136 | int wil_if_add(struct wil6210_priv *wil) | ||
137 | { | ||
138 | struct net_device *ndev = wil_to_ndev(wil); | ||
139 | int rc; | ||
140 | |||
141 | rc = register_netdev(ndev); | ||
142 | if (rc < 0) { | ||
143 | dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); | ||
144 | return rc; | ||
145 | } | ||
146 | |||
147 | wil_link_off(wil); | ||
148 | |||
149 | return 0; | ||
150 | } | ||
151 | |||
152 | void wil_if_remove(struct wil6210_priv *wil) | ||
153 | { | ||
154 | struct net_device *ndev = wil_to_ndev(wil); | ||
155 | |||
156 | unregister_netdev(ndev); | ||
157 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c new file mode 100644 index 000000000000..0fc83edd6bad --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/slab.h> | ||
20 | #include <linux/netdevice.h> | ||
21 | #include <linux/debugfs.h> | ||
22 | #include <linux/pci.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | |||
25 | #include "wil6210.h" | ||
26 | |||
27 | static int use_msi = 1; | ||
28 | module_param(use_msi, int, S_IRUGO); | ||
29 | MODULE_PARM_DESC(use_msi, | ||
30 | " Use MSI interrupt: " | ||
31 | "0 - don't, 1 - (default) - single, or 3"); | ||
32 | |||
33 | /* Bus ops */ | ||
34 | static int wil_if_pcie_enable(struct wil6210_priv *wil) | ||
35 | { | ||
36 | struct pci_dev *pdev = wil->pdev; | ||
37 | int rc; | ||
38 | |||
39 | pci_set_master(pdev); | ||
40 | |||
41 | /* | ||
42 | * how many MSI interrupts to request? | ||
43 | */ | ||
44 | switch (use_msi) { | ||
45 | case 3: | ||
46 | case 1: | ||
47 | case 0: | ||
48 | break; | ||
49 | default: | ||
50 | wil_err(wil, "Invalid use_msi=%d, default to 1\n", | ||
51 | use_msi); | ||
52 | use_msi = 1; | ||
53 | } | ||
54 | wil->n_msi = use_msi; | ||
55 | if (wil->n_msi) { | ||
56 | wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); | ||
57 | rc = pci_enable_msi_block(pdev, wil->n_msi); | ||
58 | if (rc && (wil->n_msi == 3)) { | ||
59 | wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); | ||
60 | wil->n_msi = 1; | ||
61 | rc = pci_enable_msi_block(pdev, wil->n_msi); | ||
62 | } | ||
63 | if (rc) { | ||
64 | wil_err(wil, "pci_enable_msi failed, use INTx\n"); | ||
65 | wil->n_msi = 0; | ||
66 | } | ||
67 | } else { | ||
68 | wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); | ||
69 | } | ||
70 | |||
71 | rc = wil6210_init_irq(wil, pdev->irq); | ||
72 | if (rc) | ||
73 | goto stop_master; | ||
74 | |||
75 | /* need reset here to obtain MAC */ | ||
76 | rc = wil_reset(wil); | ||
77 | if (rc) | ||
78 | goto release_irq; | ||
79 | |||
80 | return 0; | ||
81 | |||
82 | release_irq: | ||
83 | wil6210_fini_irq(wil, pdev->irq); | ||
84 | /* safe to call if no MSI */ | ||
85 | pci_disable_msi(pdev); | ||
86 | stop_master: | ||
87 | pci_clear_master(pdev); | ||
88 | return rc; | ||
89 | } | ||
90 | |||
91 | static int wil_if_pcie_disable(struct wil6210_priv *wil) | ||
92 | { | ||
93 | struct pci_dev *pdev = wil->pdev; | ||
94 | |||
95 | pci_clear_master(pdev); | ||
96 | /* disable and release IRQ */ | ||
97 | wil6210_fini_irq(wil, pdev->irq); | ||
98 | /* safe to call if no MSI */ | ||
99 | pci_disable_msi(pdev); | ||
100 | /* TODO: disable HW */ | ||
101 | |||
102 | return 0; | ||
103 | } | ||
104 | |||
105 | static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) | ||
106 | { | ||
107 | struct wil6210_priv *wil; | ||
108 | struct device *dev = &pdev->dev; | ||
109 | void __iomem *csr; | ||
110 | int rc; | ||
111 | |||
112 | /* check HW */ | ||
113 | dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", | ||
114 | (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); | ||
115 | |||
116 | if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { | ||
117 | dev_err(&pdev->dev, "Not " WIL_NAME "? " | ||
118 | "BAR0 size is %lu while expecting %lu\n", | ||
119 | (ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); | ||
120 | return -ENODEV; | ||
121 | } | ||
122 | |||
123 | rc = pci_enable_device(pdev); | ||
124 | if (rc) { | ||
125 | dev_err(&pdev->dev, "pci_enable_device failed\n"); | ||
126 | return -ENODEV; | ||
127 | } | ||
128 | /* rollback to err_disable_pdev */ | ||
129 | |||
130 | rc = pci_request_region(pdev, 0, WIL_NAME); | ||
131 | if (rc) { | ||
132 | dev_err(&pdev->dev, "pci_request_region failed\n"); | ||
133 | goto err_disable_pdev; | ||
134 | } | ||
135 | /* rollback to err_release_reg */ | ||
136 | |||
137 | csr = pci_ioremap_bar(pdev, 0); | ||
138 | if (!csr) { | ||
139 | dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); | ||
140 | rc = -ENODEV; | ||
141 | goto err_release_reg; | ||
142 | } | ||
143 | /* rollback to err_iounmap */ | ||
144 | dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); | ||
145 | |||
146 | wil = wil_if_alloc(dev, csr); | ||
147 | if (IS_ERR(wil)) { | ||
148 | rc = (int)PTR_ERR(wil); | ||
149 | dev_err(dev, "wil_if_alloc failed: %d\n", rc); | ||
150 | goto err_iounmap; | ||
151 | } | ||
152 | /* rollback to if_free */ | ||
153 | |||
154 | pci_set_drvdata(pdev, wil); | ||
155 | wil->pdev = pdev; | ||
156 | |||
157 | /* FW should raise IRQ when ready */ | ||
158 | rc = wil_if_pcie_enable(wil); | ||
159 | if (rc) { | ||
160 | wil_err(wil, "Enable device failed\n"); | ||
161 | goto if_free; | ||
162 | } | ||
163 | /* rollback to bus_disable */ | ||
164 | |||
165 | rc = wil_if_add(wil); | ||
166 | if (rc) { | ||
167 | wil_err(wil, "wil_if_add failed: %d\n", rc); | ||
168 | goto bus_disable; | ||
169 | } | ||
170 | |||
171 | wil6210_debugfs_init(wil); | ||
172 | |||
173 | /* check FW is alive */ | ||
174 | wmi_echo(wil); | ||
175 | |||
176 | return 0; | ||
177 | |||
178 | bus_disable: | ||
179 | wil_if_pcie_disable(wil); | ||
180 | if_free: | ||
181 | wil_if_free(wil); | ||
182 | err_iounmap: | ||
183 | pci_iounmap(pdev, csr); | ||
184 | err_release_reg: | ||
185 | pci_release_region(pdev, 0); | ||
186 | err_disable_pdev: | ||
187 | pci_disable_device(pdev); | ||
188 | |||
189 | return rc; | ||
190 | } | ||
191 | |||
192 | static void wil_pcie_remove(struct pci_dev *pdev) | ||
193 | { | ||
194 | struct wil6210_priv *wil = pci_get_drvdata(pdev); | ||
195 | |||
196 | wil6210_debugfs_remove(wil); | ||
197 | wil_if_pcie_disable(wil); | ||
198 | wil_if_remove(wil); | ||
199 | wil_if_free(wil); | ||
200 | pci_iounmap(pdev, wil->csr); | ||
201 | pci_release_region(pdev, 0); | ||
202 | pci_disable_device(pdev); | ||
203 | pci_set_drvdata(pdev, NULL); | ||
204 | } | ||
205 | |||
206 | static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { | ||
207 | { PCI_DEVICE(0x1ae9, 0x0301) }, | ||
208 | { /* end: all zeroes */ }, | ||
209 | }; | ||
210 | MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); | ||
211 | |||
212 | static struct pci_driver wil6210_driver = { | ||
213 | .probe = wil_pcie_probe, | ||
214 | .remove = wil_pcie_remove, | ||
215 | .id_table = wil6210_pcie_ids, | ||
216 | .name = WIL_NAME, | ||
217 | }; | ||
218 | |||
219 | module_pci_driver(wil6210_driver); | ||
220 | |||
221 | MODULE_LICENSE("Dual BSD/GPL"); | ||
222 | MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>"); | ||
223 | MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); | ||
diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c new file mode 100644 index 000000000000..f29c294413cf --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.c | |||
@@ -0,0 +1,871 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/netdevice.h> | ||
19 | #include <linux/etherdevice.h> | ||
20 | #include <linux/hardirq.h> | ||
21 | #include <net/ieee80211_radiotap.h> | ||
22 | #include <linux/if_arp.h> | ||
23 | #include <linux/moduleparam.h> | ||
24 | |||
25 | #include "wil6210.h" | ||
26 | #include "wmi.h" | ||
27 | #include "txrx.h" | ||
28 | |||
29 | static bool rtap_include_phy_info; | ||
30 | module_param(rtap_include_phy_info, bool, S_IRUGO); | ||
31 | MODULE_PARM_DESC(rtap_include_phy_info, | ||
32 | " Include PHY info in the radiotap header, default - no"); | ||
33 | |||
34 | static inline int wil_vring_is_empty(struct vring *vring) | ||
35 | { | ||
36 | return vring->swhead == vring->swtail; | ||
37 | } | ||
38 | |||
39 | static inline u32 wil_vring_next_tail(struct vring *vring) | ||
40 | { | ||
41 | return (vring->swtail + 1) % vring->size; | ||
42 | } | ||
43 | |||
44 | static inline void wil_vring_advance_head(struct vring *vring, int n) | ||
45 | { | ||
46 | vring->swhead = (vring->swhead + n) % vring->size; | ||
47 | } | ||
48 | |||
49 | static inline int wil_vring_is_full(struct vring *vring) | ||
50 | { | ||
51 | return wil_vring_next_tail(vring) == vring->swhead; | ||
52 | } | ||
53 | /* | ||
54 | * Available space in Tx Vring | ||
55 | */ | ||
56 | static inline int wil_vring_avail_tx(struct vring *vring) | ||
57 | { | ||
58 | u32 swhead = vring->swhead; | ||
59 | u32 swtail = vring->swtail; | ||
60 | int used = (vring->size + swhead - swtail) % vring->size; | ||
61 | |||
62 | return vring->size - used - 1; | ||
63 | } | ||
64 | |||
65 | static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) | ||
66 | { | ||
67 | struct device *dev = wil_to_dev(wil); | ||
68 | size_t sz = vring->size * sizeof(vring->va[0]); | ||
69 | uint i; | ||
70 | |||
71 | BUILD_BUG_ON(sizeof(vring->va[0]) != 32); | ||
72 | |||
73 | vring->swhead = 0; | ||
74 | vring->swtail = 0; | ||
75 | vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); | ||
76 | if (!vring->ctx) { | ||
77 | wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", | ||
78 | vring->size); | ||
79 | vring->va = NULL; | ||
80 | return -ENOMEM; | ||
81 | } | ||
82 | /* | ||
83 | * vring->va should be aligned on its size rounded up to power of 2 | ||
84 | * This is granted by the dma_alloc_coherent | ||
85 | */ | ||
86 | vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); | ||
87 | if (!vring->va) { | ||
88 | wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", | ||
89 | vring->size); | ||
90 | kfree(vring->ctx); | ||
91 | vring->ctx = NULL; | ||
92 | return -ENOMEM; | ||
93 | } | ||
94 | /* initially, all descriptors are SW owned | ||
95 | * For Tx and Rx, ownership bit is at the same location, thus | ||
96 | * we can use any | ||
97 | */ | ||
98 | for (i = 0; i < vring->size; i++) { | ||
99 | volatile struct vring_tx_desc *d = &(vring->va[i].tx); | ||
100 | d->dma.status = TX_DMA_STATUS_DU; | ||
101 | } | ||
102 | |||
103 | wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, | ||
104 | vring->va, (unsigned long long)vring->pa, vring->ctx); | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, | ||
110 | int tx) | ||
111 | { | ||
112 | struct device *dev = wil_to_dev(wil); | ||
113 | size_t sz = vring->size * sizeof(vring->va[0]); | ||
114 | |||
115 | while (!wil_vring_is_empty(vring)) { | ||
116 | if (tx) { | ||
117 | volatile struct vring_tx_desc *d = | ||
118 | &vring->va[vring->swtail].tx; | ||
119 | dma_addr_t pa = d->dma.addr_low | | ||
120 | ((u64)d->dma.addr_high << 32); | ||
121 | struct sk_buff *skb = vring->ctx[vring->swtail]; | ||
122 | if (skb) { | ||
123 | dma_unmap_single(dev, pa, d->dma.length, | ||
124 | DMA_TO_DEVICE); | ||
125 | dev_kfree_skb_any(skb); | ||
126 | vring->ctx[vring->swtail] = NULL; | ||
127 | } else { | ||
128 | dma_unmap_page(dev, pa, d->dma.length, | ||
129 | DMA_TO_DEVICE); | ||
130 | } | ||
131 | vring->swtail = wil_vring_next_tail(vring); | ||
132 | } else { /* rx */ | ||
133 | volatile struct vring_rx_desc *d = | ||
134 | &vring->va[vring->swtail].rx; | ||
135 | dma_addr_t pa = d->dma.addr_low | | ||
136 | ((u64)d->dma.addr_high << 32); | ||
137 | struct sk_buff *skb = vring->ctx[vring->swhead]; | ||
138 | dma_unmap_single(dev, pa, d->dma.length, | ||
139 | DMA_FROM_DEVICE); | ||
140 | kfree_skb(skb); | ||
141 | wil_vring_advance_head(vring, 1); | ||
142 | } | ||
143 | } | ||
144 | dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); | ||
145 | kfree(vring->ctx); | ||
146 | vring->pa = 0; | ||
147 | vring->va = NULL; | ||
148 | vring->ctx = NULL; | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * Allocate one skb for Rx VRING | ||
153 | * | ||
154 | * Safe to call from IRQ | ||
155 | */ | ||
156 | static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, | ||
157 | u32 i, int headroom) | ||
158 | { | ||
159 | struct device *dev = wil_to_dev(wil); | ||
160 | unsigned int sz = RX_BUF_LEN; | ||
161 | volatile struct vring_rx_desc *d = &(vring->va[i].rx); | ||
162 | dma_addr_t pa; | ||
163 | |||
164 | /* TODO align */ | ||
165 | struct sk_buff *skb = dev_alloc_skb(sz + headroom); | ||
166 | if (unlikely(!skb)) | ||
167 | return -ENOMEM; | ||
168 | |||
169 | skb_reserve(skb, headroom); | ||
170 | skb_put(skb, sz); | ||
171 | |||
172 | pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); | ||
173 | if (unlikely(dma_mapping_error(dev, pa))) { | ||
174 | kfree_skb(skb); | ||
175 | return -ENOMEM; | ||
176 | } | ||
177 | |||
178 | d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; | ||
179 | d->dma.addr_low = lower_32_bits(pa); | ||
180 | d->dma.addr_high = (u16)upper_32_bits(pa); | ||
181 | /* ip_length don't care */ | ||
182 | /* b11 don't care */ | ||
183 | /* error don't care */ | ||
184 | d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ | ||
185 | d->dma.length = sz; | ||
186 | vring->ctx[i] = skb; | ||
187 | |||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * Adds radiotap header | ||
193 | * | ||
194 | * Any error indicated as "Bad FCS" | ||
195 | * | ||
196 | * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: | ||
197 | * - Rx descriptor: 32 bytes | ||
198 | * - Phy info | ||
199 | */ | ||
200 | static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, | ||
201 | struct sk_buff *skb, | ||
202 | volatile struct vring_rx_desc *d) | ||
203 | { | ||
204 | struct wireless_dev *wdev = wil->wdev; | ||
205 | struct wil6210_rtap { | ||
206 | struct ieee80211_radiotap_header rthdr; | ||
207 | /* fields should be in the order of bits in rthdr.it_present */ | ||
208 | /* flags */ | ||
209 | u8 flags; | ||
210 | /* channel */ | ||
211 | __le16 chnl_freq __aligned(2); | ||
212 | __le16 chnl_flags; | ||
213 | /* MCS */ | ||
214 | u8 mcs_present; | ||
215 | u8 mcs_flags; | ||
216 | u8 mcs_index; | ||
217 | } __packed; | ||
218 | struct wil6210_rtap_vendor { | ||
219 | struct wil6210_rtap rtap; | ||
220 | /* vendor */ | ||
221 | u8 vendor_oui[3] __aligned(2); | ||
222 | u8 vendor_ns; | ||
223 | __le16 vendor_skip; | ||
224 | u8 vendor_data[0]; | ||
225 | } __packed; | ||
226 | struct wil6210_rtap_vendor *rtap_vendor; | ||
227 | int rtap_len = sizeof(struct wil6210_rtap); | ||
228 | int phy_length = 0; /* phy info header size, bytes */ | ||
229 | static char phy_data[128]; | ||
230 | struct ieee80211_channel *ch = wdev->preset_chandef.chan; | ||
231 | |||
232 | if (rtap_include_phy_info) { | ||
233 | rtap_len = sizeof(*rtap_vendor) + sizeof(*d); | ||
234 | /* calculate additional length */ | ||
235 | if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { | ||
236 | /** | ||
237 | * PHY info starts from 8-byte boundary | ||
238 | * there are 8-byte lines, last line may be partially | ||
239 | * written (HW bug), thus FW configures for last line | ||
240 | * to be excessive. Driver skips this last line. | ||
241 | */ | ||
242 | int len = min_t(int, 8 + sizeof(phy_data), | ||
243 | wil_rxdesc_phy_length(d)); | ||
244 | if (len > 8) { | ||
245 | void *p = skb_tail_pointer(skb); | ||
246 | void *pa = PTR_ALIGN(p, 8); | ||
247 | if (skb_tailroom(skb) >= len + (pa - p)) { | ||
248 | phy_length = len - 8; | ||
249 | memcpy(phy_data, pa, phy_length); | ||
250 | } | ||
251 | } | ||
252 | } | ||
253 | rtap_len += phy_length; | ||
254 | } | ||
255 | |||
256 | if (skb_headroom(skb) < rtap_len && | ||
257 | pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { | ||
258 | wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); | ||
259 | return; | ||
260 | } | ||
261 | |||
262 | rtap_vendor = (void *)skb_push(skb, rtap_len); | ||
263 | memset(rtap_vendor, 0, rtap_len); | ||
264 | |||
265 | rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; | ||
266 | rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); | ||
267 | rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( | ||
268 | (1 << IEEE80211_RADIOTAP_FLAGS) | | ||
269 | (1 << IEEE80211_RADIOTAP_CHANNEL) | | ||
270 | (1 << IEEE80211_RADIOTAP_MCS)); | ||
271 | if (d->dma.status & RX_DMA_STATUS_ERROR) | ||
272 | rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; | ||
273 | |||
274 | rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); | ||
275 | rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); | ||
276 | |||
277 | rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; | ||
278 | rtap_vendor->rtap.mcs_flags = 0; | ||
279 | rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d); | ||
280 | |||
281 | if (rtap_include_phy_info) { | ||
282 | rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << | ||
283 | IEEE80211_RADIOTAP_VENDOR_NAMESPACE); | ||
284 | /* OUI for Wilocity 04:ce:14 */ | ||
285 | rtap_vendor->vendor_oui[0] = 0x04; | ||
286 | rtap_vendor->vendor_oui[1] = 0xce; | ||
287 | rtap_vendor->vendor_oui[2] = 0x14; | ||
288 | rtap_vendor->vendor_ns = 1; | ||
289 | /* Rx descriptor + PHY data */ | ||
290 | rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + | ||
291 | phy_length); | ||
292 | memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d)); | ||
293 | memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, | ||
294 | phy_length); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | /* | ||
299 | * Fast swap in place between 2 registers | ||
300 | */ | ||
301 | static void wil_swap_u16(u16 *a, u16 *b) | ||
302 | { | ||
303 | *a ^= *b; | ||
304 | *b ^= *a; | ||
305 | *a ^= *b; | ||
306 | } | ||
307 | |||
308 | static void wil_swap_ethaddr(void *data) | ||
309 | { | ||
310 | struct ethhdr *eth = data; | ||
311 | u16 *s = (u16 *)eth->h_source; | ||
312 | u16 *d = (u16 *)eth->h_dest; | ||
313 | |||
314 | wil_swap_u16(s++, d++); | ||
315 | wil_swap_u16(s++, d++); | ||
316 | wil_swap_u16(s, d); | ||
317 | } | ||
318 | |||
319 | /** | ||
320 | * reap 1 frame from @swhead | ||
321 | * | ||
322 | * Safe to call from IRQ | ||
323 | */ | ||
324 | static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, | ||
325 | struct vring *vring) | ||
326 | { | ||
327 | struct device *dev = wil_to_dev(wil); | ||
328 | struct net_device *ndev = wil_to_ndev(wil); | ||
329 | volatile struct vring_rx_desc *d; | ||
330 | struct sk_buff *skb; | ||
331 | dma_addr_t pa; | ||
332 | unsigned int sz = RX_BUF_LEN; | ||
333 | u8 ftype; | ||
334 | u8 ds_bits; | ||
335 | |||
336 | if (wil_vring_is_empty(vring)) | ||
337 | return NULL; | ||
338 | |||
339 | d = &(vring->va[vring->swhead].rx); | ||
340 | if (!(d->dma.status & RX_DMA_STATUS_DU)) { | ||
341 | /* it is not error, we just reached end of Rx done area */ | ||
342 | return NULL; | ||
343 | } | ||
344 | |||
345 | pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); | ||
346 | skb = vring->ctx[vring->swhead]; | ||
347 | dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); | ||
348 | skb_trim(skb, d->dma.length); | ||
349 | |||
350 | wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); | ||
351 | |||
352 | /* use radiotap header only if required */ | ||
353 | if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) | ||
354 | wil_rx_add_radiotap_header(wil, skb, d); | ||
355 | |||
356 | wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); | ||
357 | wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, | ||
358 | (const void *)d, sizeof(*d), false); | ||
359 | |||
360 | wil_vring_advance_head(vring, 1); | ||
361 | |||
362 | /* no extra checks if in sniffer mode */ | ||
363 | if (ndev->type != ARPHRD_ETHER) | ||
364 | return skb; | ||
365 | /* | ||
366 | * Non-data frames may be delivered through Rx DMA channel (ex: BAR) | ||
367 | * Driver should recognize it by frame type, that is found | ||
368 | * in Rx descriptor. If type is not data, it is 802.11 frame as is | ||
369 | */ | ||
370 | ftype = wil_rxdesc_ftype(d) << 2; | ||
371 | if (ftype != IEEE80211_FTYPE_DATA) { | ||
372 | wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); | ||
373 | /* TODO: process it */ | ||
374 | kfree_skb(skb); | ||
375 | return NULL; | ||
376 | } | ||
377 | |||
378 | if (skb->len < ETH_HLEN) { | ||
379 | wil_err(wil, "Short frame, len = %d\n", skb->len); | ||
380 | /* TODO: process it (i.e. BAR) */ | ||
381 | kfree_skb(skb); | ||
382 | return NULL; | ||
383 | } | ||
384 | |||
385 | ds_bits = wil_rxdesc_ds_bits(d); | ||
386 | if (ds_bits == 1) { | ||
387 | /* | ||
388 | * HW bug - in ToDS mode, i.e. Rx on AP side, | ||
389 | * addresses get swapped | ||
390 | */ | ||
391 | wil_swap_ethaddr(skb->data); | ||
392 | } | ||
393 | |||
394 | return skb; | ||
395 | } | ||
396 | |||
397 | /** | ||
398 | * allocate and fill up to @count buffers in rx ring | ||
399 | * buffers posted at @swtail | ||
400 | */ | ||
401 | static int wil_rx_refill(struct wil6210_priv *wil, int count) | ||
402 | { | ||
403 | struct net_device *ndev = wil_to_ndev(wil); | ||
404 | struct vring *v = &wil->vring_rx; | ||
405 | u32 next_tail; | ||
406 | int rc = 0; | ||
407 | int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? | ||
408 | WIL6210_RTAP_SIZE : 0; | ||
409 | |||
410 | for (; next_tail = wil_vring_next_tail(v), | ||
411 | (next_tail != v->swhead) && (count-- > 0); | ||
412 | v->swtail = next_tail) { | ||
413 | rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); | ||
414 | if (rc) { | ||
415 | wil_err(wil, "Error %d in wil_rx_refill[%d]\n", | ||
416 | rc, v->swtail); | ||
417 | break; | ||
418 | } | ||
419 | } | ||
420 | iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); | ||
421 | |||
422 | return rc; | ||
423 | } | ||
424 | |||
425 | /* | ||
426 | * Pass Rx packet to the netif. Update statistics. | ||
427 | */ | ||
428 | static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) | ||
429 | { | ||
430 | int rc; | ||
431 | unsigned int len = skb->len; | ||
432 | |||
433 | if (in_interrupt()) | ||
434 | rc = netif_rx(skb); | ||
435 | else | ||
436 | rc = netif_rx_ni(skb); | ||
437 | |||
438 | if (likely(rc == NET_RX_SUCCESS)) { | ||
439 | ndev->stats.rx_packets++; | ||
440 | ndev->stats.rx_bytes += len; | ||
441 | |||
442 | } else { | ||
443 | ndev->stats.rx_dropped++; | ||
444 | } | ||
445 | } | ||
446 | |||
447 | /** | ||
448 | * Proceed all completed skb's from Rx VRING | ||
449 | * | ||
450 | * Safe to call from IRQ | ||
451 | */ | ||
452 | void wil_rx_handle(struct wil6210_priv *wil) | ||
453 | { | ||
454 | struct net_device *ndev = wil_to_ndev(wil); | ||
455 | struct vring *v = &wil->vring_rx; | ||
456 | struct sk_buff *skb; | ||
457 | |||
458 | if (!v->va) { | ||
459 | wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); | ||
460 | return; | ||
461 | } | ||
462 | wil_dbg_TXRX(wil, "%s()\n", __func__); | ||
463 | while (NULL != (skb = wil_vring_reap_rx(wil, v))) { | ||
464 | wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, | ||
465 | skb->data, skb_headlen(skb), false); | ||
466 | |||
467 | skb_orphan(skb); | ||
468 | |||
469 | if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { | ||
470 | skb->dev = ndev; | ||
471 | skb_reset_mac_header(skb); | ||
472 | skb->ip_summed = CHECKSUM_UNNECESSARY; | ||
473 | skb->pkt_type = PACKET_OTHERHOST; | ||
474 | skb->protocol = htons(ETH_P_802_2); | ||
475 | |||
476 | } else { | ||
477 | skb->protocol = eth_type_trans(skb, ndev); | ||
478 | } | ||
479 | |||
480 | wil_netif_rx_any(skb, ndev); | ||
481 | } | ||
482 | wil_rx_refill(wil, v->size); | ||
483 | } | ||
484 | |||
485 | int wil_rx_init(struct wil6210_priv *wil) | ||
486 | { | ||
487 | struct net_device *ndev = wil_to_ndev(wil); | ||
488 | struct wireless_dev *wdev = wil->wdev; | ||
489 | struct vring *vring = &wil->vring_rx; | ||
490 | int rc; | ||
491 | struct wmi_cfg_rx_chain_cmd cmd = { | ||
492 | .action = WMI_RX_CHAIN_ADD, | ||
493 | .rx_sw_ring = { | ||
494 | .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), | ||
495 | }, | ||
496 | .mid = 0, /* TODO - what is it? */ | ||
497 | .decap_trans_type = WMI_DECAP_TYPE_802_3, | ||
498 | }; | ||
499 | struct { | ||
500 | struct wil6210_mbox_hdr_wmi wmi; | ||
501 | struct wmi_cfg_rx_chain_done_event evt; | ||
502 | } __packed evt; | ||
503 | |||
504 | vring->size = WIL6210_RX_RING_SIZE; | ||
505 | rc = wil_vring_alloc(wil, vring); | ||
506 | if (rc) | ||
507 | return rc; | ||
508 | |||
509 | cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); | ||
510 | cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); | ||
511 | if (wdev->iftype == NL80211_IFTYPE_MONITOR) { | ||
512 | struct ieee80211_channel *ch = wdev->preset_chandef.chan; | ||
513 | |||
514 | cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); | ||
515 | if (ch) | ||
516 | cmd.sniffer_cfg.channel = ch->hw_value - 1; | ||
517 | cmd.sniffer_cfg.phy_info_mode = | ||
518 | cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); | ||
519 | cmd.sniffer_cfg.phy_support = | ||
520 | cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) | ||
521 | ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); | ||
522 | } | ||
523 | /* typical time for secure PCP is 840ms */ | ||
524 | rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), | ||
525 | WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); | ||
526 | if (rc) | ||
527 | goto err_free; | ||
528 | |||
529 | vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); | ||
530 | |||
531 | wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", | ||
532 | le32_to_cpu(evt.evt.status), vring->hwtail); | ||
533 | |||
534 | rc = wil_rx_refill(wil, vring->size); | ||
535 | if (rc) | ||
536 | goto err_free; | ||
537 | |||
538 | return 0; | ||
539 | err_free: | ||
540 | wil_vring_free(wil, vring, 0); | ||
541 | |||
542 | return rc; | ||
543 | } | ||
544 | |||
545 | void wil_rx_fini(struct wil6210_priv *wil) | ||
546 | { | ||
547 | struct vring *vring = &wil->vring_rx; | ||
548 | |||
549 | if (vring->va) { | ||
550 | int rc; | ||
551 | struct wmi_cfg_rx_chain_cmd cmd = { | ||
552 | .action = cpu_to_le32(WMI_RX_CHAIN_DEL), | ||
553 | .rx_sw_ring = { | ||
554 | .max_mpdu_size = cpu_to_le16(RX_BUF_LEN), | ||
555 | }, | ||
556 | }; | ||
557 | struct { | ||
558 | struct wil6210_mbox_hdr_wmi wmi; | ||
559 | struct wmi_cfg_rx_chain_done_event cfg; | ||
560 | } __packed wmi_rx_cfg_reply; | ||
561 | |||
562 | rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), | ||
563 | WMI_CFG_RX_CHAIN_DONE_EVENTID, | ||
564 | &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), | ||
565 | 100); | ||
566 | wil_vring_free(wil, vring, 0); | ||
567 | } | ||
568 | } | ||
569 | |||
570 | int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, | ||
571 | int cid, int tid) | ||
572 | { | ||
573 | int rc; | ||
574 | struct wmi_vring_cfg_cmd cmd = { | ||
575 | .action = cpu_to_le32(WMI_VRING_CMD_ADD), | ||
576 | .vring_cfg = { | ||
577 | .tx_sw_ring = { | ||
578 | .max_mpdu_size = cpu_to_le16(TX_BUF_LEN), | ||
579 | }, | ||
580 | .ringid = id, | ||
581 | .cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), | ||
582 | .encap_trans_type = WMI_VRING_ENC_TYPE_802_3, | ||
583 | .mac_ctrl = 0, | ||
584 | .to_resolution = 0, | ||
585 | .agg_max_wsize = 16, | ||
586 | .schd_params = { | ||
587 | .priority = cpu_to_le16(0), | ||
588 | .timeslot_us = cpu_to_le16(0xfff), | ||
589 | }, | ||
590 | }, | ||
591 | }; | ||
592 | struct { | ||
593 | struct wil6210_mbox_hdr_wmi wmi; | ||
594 | struct wmi_vring_cfg_done_event cmd; | ||
595 | } __packed reply; | ||
596 | struct vring *vring = &wil->vring_tx[id]; | ||
597 | |||
598 | if (vring->va) { | ||
599 | wil_err(wil, "Tx ring [%d] already allocated\n", id); | ||
600 | rc = -EINVAL; | ||
601 | goto out; | ||
602 | } | ||
603 | |||
604 | vring->size = size; | ||
605 | rc = wil_vring_alloc(wil, vring); | ||
606 | if (rc) | ||
607 | goto out; | ||
608 | |||
609 | cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); | ||
610 | cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size); | ||
611 | |||
612 | rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), | ||
613 | WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); | ||
614 | if (rc) | ||
615 | goto out_free; | ||
616 | |||
617 | if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { | ||
618 | wil_err(wil, "Tx config failed, status 0x%02x\n", | ||
619 | reply.cmd.status); | ||
620 | goto out_free; | ||
621 | } | ||
622 | vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); | ||
623 | |||
624 | return 0; | ||
625 | out_free: | ||
626 | wil_vring_free(wil, vring, 1); | ||
627 | out: | ||
628 | |||
629 | return rc; | ||
630 | } | ||
631 | |||
632 | void wil_vring_fini_tx(struct wil6210_priv *wil, int id) | ||
633 | { | ||
634 | struct vring *vring = &wil->vring_tx[id]; | ||
635 | |||
636 | if (!vring->va) | ||
637 | return; | ||
638 | |||
639 | wil_vring_free(wil, vring, 1); | ||
640 | } | ||
641 | |||
642 | static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, | ||
643 | struct sk_buff *skb) | ||
644 | { | ||
645 | struct vring *v = &wil->vring_tx[0]; | ||
646 | |||
647 | if (v->va) | ||
648 | return v; | ||
649 | |||
650 | return NULL; | ||
651 | } | ||
652 | |||
653 | static int wil_tx_desc_map(volatile struct vring_tx_desc *d, | ||
654 | dma_addr_t pa, u32 len) | ||
655 | { | ||
656 | d->dma.addr_low = lower_32_bits(pa); | ||
657 | d->dma.addr_high = (u16)upper_32_bits(pa); | ||
658 | d->dma.ip_length = 0; | ||
659 | /* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ | ||
660 | d->dma.b11 = 0/*14 | BIT(7)*/; | ||
661 | d->dma.error = 0; | ||
662 | d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ | ||
663 | d->dma.length = len; | ||
664 | d->dma.d0 = 0; | ||
665 | d->mac.d[0] = 0; | ||
666 | d->mac.d[1] = 0; | ||
667 | d->mac.d[2] = 0; | ||
668 | d->mac.ucode_cmd = 0; | ||
669 | /* use dst index 0 */ | ||
670 | d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | | ||
671 | (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); | ||
672 | /* translation type: 0 - bypass; 1 - 802.3; 2 - native wifi */ | ||
673 | d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | | ||
674 | (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); | ||
675 | |||
676 | return 0; | ||
677 | } | ||
678 | |||
679 | static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, | ||
680 | struct sk_buff *skb) | ||
681 | { | ||
682 | struct device *dev = wil_to_dev(wil); | ||
683 | volatile struct vring_tx_desc *d; | ||
684 | u32 swhead = vring->swhead; | ||
685 | int avail = wil_vring_avail_tx(vring); | ||
686 | int nr_frags = skb_shinfo(skb)->nr_frags; | ||
687 | uint f; | ||
688 | int vring_index = vring - wil->vring_tx; | ||
689 | uint i = swhead; | ||
690 | dma_addr_t pa; | ||
691 | |||
692 | wil_dbg_TXRX(wil, "%s()\n", __func__); | ||
693 | |||
694 | if (avail < vring->size/8) | ||
695 | netif_tx_stop_all_queues(wil_to_ndev(wil)); | ||
696 | if (avail < 1 + nr_frags) { | ||
697 | wil_err(wil, "Tx ring full. No space for %d fragments\n", | ||
698 | 1 + nr_frags); | ||
699 | return -ENOMEM; | ||
700 | } | ||
701 | d = &(vring->va[i].tx); | ||
702 | |||
703 | /* FIXME FW can accept only unicast frames for the peer */ | ||
704 | memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); | ||
705 | |||
706 | pa = dma_map_single(dev, skb->data, | ||
707 | skb_headlen(skb), DMA_TO_DEVICE); | ||
708 | |||
709 | wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), | ||
710 | skb->data, (unsigned long long)pa); | ||
711 | wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, | ||
712 | skb->data, skb_headlen(skb), false); | ||
713 | |||
714 | if (unlikely(dma_mapping_error(dev, pa))) | ||
715 | return -EINVAL; | ||
716 | /* 1-st segment */ | ||
717 | wil_tx_desc_map(d, pa, skb_headlen(skb)); | ||
718 | d->mac.d[2] |= ((nr_frags + 1) << | ||
719 | MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); | ||
720 | /* middle segments */ | ||
721 | for (f = 0; f < nr_frags; f++) { | ||
722 | const struct skb_frag_struct *frag = | ||
723 | &skb_shinfo(skb)->frags[f]; | ||
724 | int len = skb_frag_size(frag); | ||
725 | i = (swhead + f + 1) % vring->size; | ||
726 | d = &(vring->va[i].tx); | ||
727 | pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), | ||
728 | DMA_TO_DEVICE); | ||
729 | if (unlikely(dma_mapping_error(dev, pa))) | ||
730 | goto dma_error; | ||
731 | wil_tx_desc_map(d, pa, len); | ||
732 | vring->ctx[i] = NULL; | ||
733 | } | ||
734 | /* for the last seg only */ | ||
735 | d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); | ||
736 | d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ | ||
737 | d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); | ||
738 | d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); | ||
739 | |||
740 | wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, | ||
741 | (const void *)d, sizeof(*d), false); | ||
742 | |||
743 | /* advance swhead */ | ||
744 | wil_vring_advance_head(vring, nr_frags + 1); | ||
745 | wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); | ||
746 | iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); | ||
747 | /* hold reference to skb | ||
748 | * to prevent skb release before accounting | ||
749 | * in case of immediate "tx done" | ||
750 | */ | ||
751 | vring->ctx[i] = skb_get(skb); | ||
752 | |||
753 | return 0; | ||
754 | dma_error: | ||
755 | /* unmap what we have mapped */ | ||
756 | /* Note: increment @f to operate with positive index */ | ||
757 | for (f++; f > 0; f--) { | ||
758 | i = (swhead + f) % vring->size; | ||
759 | d = &(vring->va[i].tx); | ||
760 | d->dma.status = TX_DMA_STATUS_DU; | ||
761 | pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); | ||
762 | if (vring->ctx[i]) | ||
763 | dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
764 | else | ||
765 | dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
766 | } | ||
767 | |||
768 | return -EINVAL; | ||
769 | } | ||
770 | |||
771 | |||
772 | netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) | ||
773 | { | ||
774 | struct wil6210_priv *wil = ndev_to_wil(ndev); | ||
775 | struct vring *vring; | ||
776 | int rc; | ||
777 | |||
778 | wil_dbg_TXRX(wil, "%s()\n", __func__); | ||
779 | if (!test_bit(wil_status_fwready, &wil->status)) { | ||
780 | wil_err(wil, "FW not ready\n"); | ||
781 | goto drop; | ||
782 | } | ||
783 | if (!test_bit(wil_status_fwconnected, &wil->status)) { | ||
784 | wil_err(wil, "FW not connected\n"); | ||
785 | goto drop; | ||
786 | } | ||
787 | if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { | ||
788 | wil_err(wil, "Xmit in monitor mode not supported\n"); | ||
789 | goto drop; | ||
790 | } | ||
791 | if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { | ||
792 | rc = wmi_tx_eapol(wil, skb); | ||
793 | } else { | ||
794 | /* find vring */ | ||
795 | vring = wil_find_tx_vring(wil, skb); | ||
796 | if (!vring) { | ||
797 | wil_err(wil, "No Tx VRING available\n"); | ||
798 | goto drop; | ||
799 | } | ||
800 | /* set up vring entry */ | ||
801 | rc = wil_tx_vring(wil, vring, skb); | ||
802 | } | ||
803 | switch (rc) { | ||
804 | case 0: | ||
805 | ndev->stats.tx_packets++; | ||
806 | ndev->stats.tx_bytes += skb->len; | ||
807 | dev_kfree_skb_any(skb); | ||
808 | return NETDEV_TX_OK; | ||
809 | case -ENOMEM: | ||
810 | return NETDEV_TX_BUSY; | ||
811 | default: | ||
812 | ; /* goto drop; */ | ||
813 | break; | ||
814 | } | ||
815 | drop: | ||
816 | netif_tx_stop_all_queues(ndev); | ||
817 | ndev->stats.tx_dropped++; | ||
818 | dev_kfree_skb_any(skb); | ||
819 | |||
820 | return NET_XMIT_DROP; | ||
821 | } | ||
822 | |||
823 | /** | ||
824 | * Clean up transmitted skb's from the Tx VRING | ||
825 | * | ||
826 | * Safe to call from IRQ | ||
827 | */ | ||
828 | void wil_tx_complete(struct wil6210_priv *wil, int ringid) | ||
829 | { | ||
830 | struct device *dev = wil_to_dev(wil); | ||
831 | struct vring *vring = &wil->vring_tx[ringid]; | ||
832 | |||
833 | if (!vring->va) { | ||
834 | wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); | ||
835 | return; | ||
836 | } | ||
837 | |||
838 | wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); | ||
839 | |||
840 | while (!wil_vring_is_empty(vring)) { | ||
841 | volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; | ||
842 | dma_addr_t pa; | ||
843 | struct sk_buff *skb; | ||
844 | if (!(d->dma.status & TX_DMA_STATUS_DU)) | ||
845 | break; | ||
846 | |||
847 | wil_dbg_TXRX(wil, | ||
848 | "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", | ||
849 | vring->swtail, d->dma.length, d->dma.status, | ||
850 | d->dma.error); | ||
851 | wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, | ||
852 | (const void *)d, sizeof(*d), false); | ||
853 | |||
854 | pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); | ||
855 | skb = vring->ctx[vring->swtail]; | ||
856 | if (skb) { | ||
857 | dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
858 | dev_kfree_skb_any(skb); | ||
859 | vring->ctx[vring->swtail] = NULL; | ||
860 | } else { | ||
861 | dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); | ||
862 | } | ||
863 | d->dma.addr_low = 0; | ||
864 | d->dma.addr_high = 0; | ||
865 | d->dma.length = 0; | ||
866 | d->dma.status = TX_DMA_STATUS_DU; | ||
867 | vring->swtail = wil_vring_next_tail(vring); | ||
868 | } | ||
869 | if (wil_vring_avail_tx(vring) > vring->size/4) | ||
870 | netif_tx_wake_all_queues(wil_to_ndev(wil)); | ||
871 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h new file mode 100644 index 000000000000..45a61f597c5c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.h | |||
@@ -0,0 +1,362 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #ifndef WIL6210_TXRX_H | ||
18 | #define WIL6210_TXRX_H | ||
19 | |||
20 | #define BUF_SW_OWNED (1) | ||
21 | #define BUF_HW_OWNED (0) | ||
22 | |||
23 | /* size of max. Rx packet */ | ||
24 | #define RX_BUF_LEN (2048) | ||
25 | #define TX_BUF_LEN (2048) | ||
26 | /* how many bytes to reserve for rtap header? */ | ||
27 | #define WIL6210_RTAP_SIZE (128) | ||
28 | |||
29 | /* Tx/Rx path */ | ||
30 | /* | ||
31 | * Tx descriptor - MAC part | ||
32 | * [dword 0] | ||
33 | * bit 0.. 9 : lifetime_expiry_value:10 | ||
34 | * bit 10 : interrup_en:1 | ||
35 | * bit 11 : status_en:1 | ||
36 | * bit 12..13 : txss_override:2 | ||
37 | * bit 14 : timestamp_insertion:1 | ||
38 | * bit 15 : duration_preserve:1 | ||
39 | * bit 16..21 : reserved0:6 | ||
40 | * bit 22..26 : mcs_index:5 | ||
41 | * bit 27 : mcs_en:1 | ||
42 | * bit 28..29 : reserved1:2 | ||
43 | * bit 30 : reserved2:1 | ||
44 | * bit 31 : sn_preserved:1 | ||
45 | * [dword 1] | ||
46 | * bit 0.. 3 : pkt_mode:4 | ||
47 | * bit 4 : pkt_mode_en:1 | ||
48 | * bit 5.. 7 : reserved0:3 | ||
49 | * bit 8..13 : reserved1:6 | ||
50 | * bit 14 : reserved2:1 | ||
51 | * bit 15 : ack_policy_en:1 | ||
52 | * bit 16..19 : dst_index:4 | ||
53 | * bit 20 : dst_index_en:1 | ||
54 | * bit 21..22 : ack_policy:2 | ||
55 | * bit 23 : lifetime_en:1 | ||
56 | * bit 24..30 : max_retry:7 | ||
57 | * bit 31 : max_retry_en:1 | ||
58 | * [dword 2] | ||
59 | * bit 0.. 7 : num_of_descriptors:8 | ||
60 | * bit 8..17 : reserved:10 | ||
61 | * bit 18..19 : l2_translation_type:2 | ||
62 | * bit 20 : snap_hdr_insertion_en:1 | ||
63 | * bit 21 : vlan_removal_en:1 | ||
64 | * bit 22..31 : reserved0:10 | ||
65 | * [dword 3] | ||
66 | * bit 0.. 31: ucode_cmd:32 | ||
67 | */ | ||
68 | struct vring_tx_mac { | ||
69 | u32 d[3]; | ||
70 | u32 ucode_cmd; | ||
71 | } __packed; | ||
72 | |||
73 | /* TX MAC Dword 0 */ | ||
74 | #define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0 | ||
75 | #define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10 | ||
76 | #define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF | ||
77 | |||
78 | #define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10 | ||
79 | #define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1 | ||
80 | #define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400 | ||
81 | |||
82 | #define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11 | ||
83 | #define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1 | ||
84 | #define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800 | ||
85 | |||
86 | #define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12 | ||
87 | #define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2 | ||
88 | #define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000 | ||
89 | |||
90 | #define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14 | ||
91 | #define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1 | ||
92 | #define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000 | ||
93 | |||
94 | #define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15 | ||
95 | #define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1 | ||
96 | #define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000 | ||
97 | |||
98 | #define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22 | ||
99 | #define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5 | ||
100 | #define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000 | ||
101 | |||
102 | #define MAC_CFG_DESC_TX_0_MCS_EN_POS 27 | ||
103 | #define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1 | ||
104 | #define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000 | ||
105 | |||
106 | #define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31 | ||
107 | #define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1 | ||
108 | #define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000 | ||
109 | |||
110 | /* TX MAC Dword 1 */ | ||
111 | #define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0 | ||
112 | #define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4 | ||
113 | #define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF | ||
114 | |||
115 | #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4 | ||
116 | #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 | ||
117 | #define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 | ||
118 | |||
119 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 | ||
120 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 | ||
121 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 | ||
122 | |||
123 | #define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16 | ||
124 | #define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4 | ||
125 | #define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000 | ||
126 | |||
127 | #define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20 | ||
128 | #define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1 | ||
129 | #define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000 | ||
130 | |||
131 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21 | ||
132 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2 | ||
133 | #define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000 | ||
134 | |||
135 | #define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23 | ||
136 | #define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1 | ||
137 | #define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000 | ||
138 | |||
139 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24 | ||
140 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7 | ||
141 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000 | ||
142 | |||
143 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31 | ||
144 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1 | ||
145 | #define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000 | ||
146 | |||
147 | /* TX MAC Dword 2 */ | ||
148 | #define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0 | ||
149 | #define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8 | ||
150 | #define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF | ||
151 | |||
152 | #define MAC_CFG_DESC_TX_2_RESERVED_POS 8 | ||
153 | #define MAC_CFG_DESC_TX_2_RESERVED_LEN 10 | ||
154 | #define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00 | ||
155 | |||
156 | #define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18 | ||
157 | #define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2 | ||
158 | #define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000 | ||
159 | |||
160 | #define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20 | ||
161 | #define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1 | ||
162 | #define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 | ||
163 | |||
164 | #define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21 | ||
165 | #define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1 | ||
166 | #define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000 | ||
167 | |||
168 | /* TX MAC Dword 3 */ | ||
169 | #define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0 | ||
170 | #define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32 | ||
171 | #define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF | ||
172 | |||
173 | /* TX DMA Dword 0 */ | ||
174 | #define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0 | ||
175 | #define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8 | ||
176 | #define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF | ||
177 | |||
178 | #define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8 | ||
179 | #define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 | ||
180 | #define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 | ||
181 | |||
182 | #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 | ||
183 | #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 | ||
184 | #define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 | ||
185 | |||
186 | #define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11 | ||
187 | #define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2 | ||
188 | #define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800 | ||
189 | |||
190 | #define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13 | ||
191 | #define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1 | ||
192 | #define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000 | ||
193 | |||
194 | #define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14 | ||
195 | #define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1 | ||
196 | #define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000 | ||
197 | |||
198 | #define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15 | ||
199 | #define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1 | ||
200 | #define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 | ||
201 | |||
202 | #define DMA_CFG_DESC_TX_0_QID_POS 16 | ||
203 | #define DMA_CFG_DESC_TX_0_QID_LEN 5 | ||
204 | #define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000 | ||
205 | |||
206 | #define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21 | ||
207 | #define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1 | ||
208 | #define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 | ||
209 | |||
210 | #define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 | ||
211 | #define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 | ||
212 | #define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 | ||
213 | |||
214 | |||
215 | #define TX_DMA_STATUS_DU BIT(0) | ||
216 | |||
217 | struct vring_tx_dma { | ||
218 | u32 d0; | ||
219 | u32 addr_low; | ||
220 | u16 addr_high; | ||
221 | u8 ip_length; | ||
222 | u8 b11; /* 0..6: mac_length; 7:ip_version */ | ||
223 | u8 error; /* 0..2: err; 3..7: reserved; */ | ||
224 | u8 status; /* 0: used; 1..7; reserved */ | ||
225 | u16 length; | ||
226 | } __packed; | ||
227 | |||
228 | /* | ||
229 | * Rx descriptor - MAC part | ||
230 | * [dword 0] | ||
231 | * bit 0.. 3 : tid:4 The QoS (b3-0) TID Field | ||
232 | * bit 4.. 6 : connection_id:3 :The Source index that was found during | ||
233 | * Parsing the TA. This field is used to define the source of the packet | ||
234 | * bit 7 : reserved:1 | ||
235 | * bit 8.. 9 : mac_id:2 : The MAC virtual Ring number (always zero) | ||
236 | * bit 10..11 : frame_type:2 : The FC Control (b3-2) - MPDU Type | ||
237 | * (management, data, control and extension) | ||
238 | * bit 12..15 : frame_subtype:4 : The FC Control (b7-4) - Frame Subtype | ||
239 | * bit 16..27 : seq_number:12 The received Sequence number field | ||
240 | * bit 28..31 : extended:4 extended subtype | ||
241 | * [dword 1] | ||
242 | * bit 0.. 3 : reserved | ||
243 | * bit 4.. 5 : key_id:2 | ||
244 | * bit 6 : decrypt_bypass:1 | ||
245 | * bit 7 : security:1 | ||
246 | * bit 8.. 9 : ds_bits:2 | ||
247 | * bit 10 : a_msdu_present:1 from qos header | ||
248 | * bit 11 : a_msdu_type:1 from qos header | ||
249 | * bit 12 : a_mpdu:1 part of AMPDU aggregation | ||
250 | * bit 13 : broadcast:1 | ||
251 | * bit 14 : mutlicast:1 | ||
252 | * bit 15 : reserved:1 | ||
253 | * bit 16..20 : rx_mac_qid:5 The Queue Identifier that the packet | ||
254 | * is received from | ||
255 | * bit 21..24 : mcs:4 | ||
256 | * bit 25..28 : mic_icr:4 | ||
257 | * bit 29..31 : reserved:3 | ||
258 | * [dword 2] | ||
259 | * bit 0.. 2 : time_slot:3 The timeslot that the MPDU is received | ||
260 | * bit 3 : fc_protocol_ver:1 The FC Control (b0) - Protocol Version | ||
261 | * bit 4 : fc_order:1 The FC Control (b15) -Order | ||
262 | * bit 5.. 7 : qos_ack_policy:3 The QoS (b6-5) ack policy Field | ||
263 | * bit 8 : esop:1 The QoS (b4) ESOP field | ||
264 | * bit 9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG field | ||
265 | * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved field | ||
266 | * bit 15 : qos_ac_constraint:1 | ||
267 | * bit 16..31 : pn_15_0:16 low 2 bytes of PN | ||
268 | * [dword 3] | ||
269 | * bit 0..31 : pn_47_16:32 high 4 bytes of PN | ||
270 | */ | ||
271 | struct vring_rx_mac { | ||
272 | u32 d0; | ||
273 | u32 d1; | ||
274 | u16 w4; | ||
275 | u16 pn_15_0; | ||
276 | u32 pn_47_16; | ||
277 | } __packed; | ||
278 | |||
279 | /* | ||
280 | * Rx descriptor - DMA part | ||
281 | * [dword 0] | ||
282 | * bit 0.. 7 : l4_length:8 layer 4 length | ||
283 | * bit 8.. 9 : reserved:2 | ||
284 | * bit 10 : cmd_dma_it:1 | ||
285 | * bit 11..15 : reserved:5 | ||
286 | * bit 16..29 : phy_info_length:14 | ||
287 | * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field | ||
288 | * [dword 1] | ||
289 | * bit 0..31 : addr_low:32 The payload buffer low address | ||
290 | * [dword 2] | ||
291 | * bit 0..15 : addr_high:16 The payload buffer high address | ||
292 | * bit 16..23 : ip_length:8 | ||
293 | * bit 24..30 : mac_length:7 | ||
294 | * bit 31 : ip_version:1 | ||
295 | * [dword 3] | ||
296 | * [byte 12] error | ||
297 | * [byte 13] status | ||
298 | * bit 0 : du:1 | ||
299 | * bit 1 : eop:1 | ||
300 | * bit 2 : error:1 | ||
301 | * bit 3 : mi:1 | ||
302 | * bit 4 : l3_identified:1 | ||
303 | * bit 5 : l4_identified:1 | ||
304 | * bit 6 : phy_info_included:1 | ||
305 | * bit 7 : reserved:1 | ||
306 | * [word 7] length | ||
307 | * | ||
308 | */ | ||
309 | |||
310 | #define RX_DMA_D0_CMD_DMA_IT BIT(10) | ||
311 | |||
312 | #define RX_DMA_STATUS_DU BIT(0) | ||
313 | #define RX_DMA_STATUS_ERROR BIT(2) | ||
314 | #define RX_DMA_STATUS_PHY_INFO BIT(6) | ||
315 | |||
316 | struct vring_rx_dma { | ||
317 | u32 d0; | ||
318 | u32 addr_low; | ||
319 | u16 addr_high; | ||
320 | u8 ip_length; | ||
321 | u8 b11; | ||
322 | u8 error; | ||
323 | u8 status; | ||
324 | u16 length; | ||
325 | } __packed; | ||
326 | |||
327 | struct vring_tx_desc { | ||
328 | struct vring_tx_mac mac; | ||
329 | struct vring_tx_dma dma; | ||
330 | } __packed; | ||
331 | |||
332 | struct vring_rx_desc { | ||
333 | struct vring_rx_mac mac; | ||
334 | struct vring_rx_dma dma; | ||
335 | } __packed; | ||
336 | |||
337 | union vring_desc { | ||
338 | struct vring_tx_desc tx; | ||
339 | struct vring_rx_desc rx; | ||
340 | } __packed; | ||
341 | |||
342 | static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d) | ||
343 | { | ||
344 | return WIL_GET_BITS(d->dma.d0, 16, 29); | ||
345 | } | ||
346 | |||
347 | static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d) | ||
348 | { | ||
349 | return WIL_GET_BITS(d->mac.d1, 21, 24); | ||
350 | } | ||
351 | |||
352 | static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d) | ||
353 | { | ||
354 | return WIL_GET_BITS(d->mac.d1, 8, 9); | ||
355 | } | ||
356 | |||
357 | static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d) | ||
358 | { | ||
359 | return WIL_GET_BITS(d->mac.d0, 10, 11); | ||
360 | } | ||
361 | |||
362 | #endif /* WIL6210_TXRX_H */ | ||
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h new file mode 100644 index 000000000000..9bcfffa4006c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210.h | |||
@@ -0,0 +1,363 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #ifndef __WIL6210_H__ | ||
18 | #define __WIL6210_H__ | ||
19 | |||
20 | #include <linux/netdevice.h> | ||
21 | #include <linux/wireless.h> | ||
22 | #include <net/cfg80211.h> | ||
23 | |||
24 | #include "dbg_hexdump.h" | ||
25 | |||
26 | #define WIL_NAME "wil6210" | ||
27 | |||
28 | /** | ||
29 | * extract bits [@b0:@b1] (inclusive) from the value @x | ||
30 | * it should be @b0 <= @b1, or result is incorrect | ||
31 | */ | ||
32 | static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) | ||
33 | { | ||
34 | return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); | ||
35 | } | ||
36 | |||
37 | #define WIL6210_MEM_SIZE (2*1024*1024UL) | ||
38 | |||
39 | #define WIL6210_TX_QUEUES (4) | ||
40 | |||
41 | #define WIL6210_RX_RING_SIZE (128) | ||
42 | #define WIL6210_TX_RING_SIZE (128) | ||
43 | #define WIL6210_MAX_TX_RINGS (24) | ||
44 | |||
45 | /* Hardware definitions begin */ | ||
46 | |||
47 | /* | ||
48 | * Mapping | ||
49 | * RGF File | Host addr | FW addr | ||
50 | * | | | ||
51 | * user_rgf | 0x000000 | 0x880000 | ||
52 | * dma_rgf | 0x001000 | 0x881000 | ||
53 | * pcie_rgf | 0x002000 | 0x882000 | ||
54 | * | | | ||
55 | */ | ||
56 | |||
57 | /* Where various structures placed in host address space */ | ||
58 | #define WIL6210_FW_HOST_OFF (0x880000UL) | ||
59 | |||
60 | #define HOSTADDR(fwaddr) (fwaddr - WIL6210_FW_HOST_OFF) | ||
61 | |||
62 | /* | ||
63 | * Interrupt control registers block | ||
64 | * | ||
65 | * each interrupt controlled by the same bit in all registers | ||
66 | */ | ||
67 | struct RGF_ICR { | ||
68 | u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ | ||
69 | u32 ICR; /* Cause, W1C/COR depending on ICC */ | ||
70 | u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ | ||
71 | u32 ICS; /* Cause Set, WO */ | ||
72 | u32 IMV; /* Mask, RW+S/C */ | ||
73 | u32 IMS; /* Mask Set, write 1 to set */ | ||
74 | u32 IMC; /* Mask Clear, write 1 to clear */ | ||
75 | } __packed; | ||
76 | |||
77 | /* registers - FW addresses */ | ||
78 | #define RGF_USER_USER_SCRATCH_PAD (0x8802bc) | ||
79 | #define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ | ||
80 | #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) | ||
81 | #define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) | ||
82 | #define RGF_USER_MAC_CPU_0 (0x8801fc) | ||
83 | #define RGF_USER_USER_CPU_0 (0x8801e0) | ||
84 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_0 (0x880b04) | ||
85 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_1 (0x880b08) | ||
86 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_2 (0x880b0c) | ||
87 | #define RGF_USER_CLKS_CTL_SW_RST_VEC_3 (0x880b10) | ||
88 | |||
89 | #define RGF_DMA_PSEUDO_CAUSE (0x881c68) | ||
90 | #define RGF_DMA_PSEUDO_CAUSE_MASK_SW (0x881c6c) | ||
91 | #define RGF_DMA_PSEUDO_CAUSE_MASK_FW (0x881c70) | ||
92 | #define BIT_DMA_PSEUDO_CAUSE_RX BIT(0) | ||
93 | #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) | ||
94 | #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) | ||
95 | |||
96 | #define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ | ||
97 | #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) | ||
98 | #define BIT_DMA_EP_TX_ICR_TX_DONE_N(n) BIT(n+1) /* n = [0..23] */ | ||
99 | #define RGF_DMA_EP_RX_ICR (0x881bd0) /* struct RGF_ICR */ | ||
100 | #define BIT_DMA_EP_RX_ICR_RX_DONE BIT(0) | ||
101 | #define RGF_DMA_EP_MISC_ICR (0x881bec) /* struct RGF_ICR */ | ||
102 | #define BIT_DMA_EP_MISC_ICR_RX_HTRSH BIT(0) | ||
103 | #define BIT_DMA_EP_MISC_ICR_TX_NO_ACT BIT(1) | ||
104 | #define BIT_DMA_EP_MISC_ICR_FW_INT0 BIT(28) | ||
105 | #define BIT_DMA_EP_MISC_ICR_FW_INT1 BIT(29) | ||
106 | |||
107 | /* Interrupt moderation control */ | ||
108 | #define RGF_DMA_ITR_CNT_TRSH (0x881c5c) | ||
109 | #define RGF_DMA_ITR_CNT_DATA (0x881c60) | ||
110 | #define RGF_DMA_ITR_CNT_CRL (0x881C64) | ||
111 | #define BIT_DMA_ITR_CNT_CRL_EN BIT(0) | ||
112 | #define BIT_DMA_ITR_CNT_CRL_EXT_TICK BIT(1) | ||
113 | #define BIT_DMA_ITR_CNT_CRL_FOREVER BIT(2) | ||
114 | #define BIT_DMA_ITR_CNT_CRL_CLR BIT(3) | ||
115 | #define BIT_DMA_ITR_CNT_CRL_REACH_TRSH BIT(4) | ||
116 | |||
117 | /* popular locations */ | ||
118 | #define HOST_MBOX HOSTADDR(RGF_USER_USER_SCRATCH_PAD) | ||
119 | #define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ | ||
120 | offsetof(struct RGF_ICR, ICS)) | ||
121 | #define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 | ||
122 | |||
123 | /* ISR register bits */ | ||
124 | #define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 | ||
125 | #define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 | ||
126 | |||
127 | /* Hardware definitions end */ | ||
128 | |||
129 | struct wil6210_mbox_ring { | ||
130 | u32 base; | ||
131 | u16 entry_size; /* max. size of mbox entry, incl. all headers */ | ||
132 | u16 size; | ||
133 | u32 tail; | ||
134 | u32 head; | ||
135 | } __packed; | ||
136 | |||
137 | struct wil6210_mbox_ring_desc { | ||
138 | __le32 sync; | ||
139 | __le32 addr; | ||
140 | } __packed; | ||
141 | |||
142 | /* at HOST_OFF_WIL6210_MBOX_CTL */ | ||
143 | struct wil6210_mbox_ctl { | ||
144 | struct wil6210_mbox_ring tx; | ||
145 | struct wil6210_mbox_ring rx; | ||
146 | } __packed; | ||
147 | |||
148 | struct wil6210_mbox_hdr { | ||
149 | __le16 seq; | ||
150 | __le16 len; /* payload, bytes after this header */ | ||
151 | __le16 type; | ||
152 | u8 flags; | ||
153 | u8 reserved; | ||
154 | } __packed; | ||
155 | |||
156 | #define WIL_MBOX_HDR_TYPE_WMI (0) | ||
157 | |||
158 | /* max. value for wil6210_mbox_hdr.len */ | ||
159 | #define MAX_MBOXITEM_SIZE (240) | ||
160 | |||
161 | struct wil6210_mbox_hdr_wmi { | ||
162 | u8 reserved0[2]; | ||
163 | __le16 id; | ||
164 | __le16 info1; /* bits [0..3] - device_id, rest - unused */ | ||
165 | u8 reserved1[2]; | ||
166 | } __packed; | ||
167 | |||
168 | struct pending_wmi_event { | ||
169 | struct list_head list; | ||
170 | struct { | ||
171 | struct wil6210_mbox_hdr hdr; | ||
172 | struct wil6210_mbox_hdr_wmi wmi; | ||
173 | u8 data[0]; | ||
174 | } __packed event; | ||
175 | }; | ||
176 | |||
177 | union vring_desc; | ||
178 | |||
179 | struct vring { | ||
180 | dma_addr_t pa; | ||
181 | volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */ | ||
182 | u16 size; /* number of vring_desc elements */ | ||
183 | u32 swtail; | ||
184 | u32 swhead; | ||
185 | u32 hwtail; /* write here to inform hw */ | ||
186 | void **ctx; /* void *ctx[size] - software context */ | ||
187 | }; | ||
188 | |||
189 | enum { /* for wil6210_priv.status */ | ||
190 | wil_status_fwready = 0, | ||
191 | wil_status_fwconnected, | ||
192 | wil_status_dontscan, | ||
193 | wil_status_irqen, /* FIXME: interrupts enabled - for debug */ | ||
194 | }; | ||
195 | |||
196 | struct pci_dev; | ||
197 | |||
198 | struct wil6210_stats { | ||
199 | u64 tsf; | ||
200 | u32 snr; | ||
201 | u16 last_mcs_rx; | ||
202 | u16 bf_mcs; /* last BF, used for Tx */ | ||
203 | u16 my_rx_sector; | ||
204 | u16 my_tx_sector; | ||
205 | u16 peer_rx_sector; | ||
206 | u16 peer_tx_sector; | ||
207 | }; | ||
208 | |||
209 | struct wil6210_priv { | ||
210 | struct pci_dev *pdev; | ||
211 | int n_msi; | ||
212 | struct wireless_dev *wdev; | ||
213 | void __iomem *csr; | ||
214 | ulong status; | ||
215 | /* profile */ | ||
216 | u32 monitor_flags; | ||
217 | u32 secure_pcp; /* create secure PCP? */ | ||
218 | int sinfo_gen; | ||
219 | /* cached ISR registers */ | ||
220 | u32 isr_misc; | ||
221 | /* mailbox related */ | ||
222 | struct mutex wmi_mutex; | ||
223 | struct wil6210_mbox_ctl mbox_ctl; | ||
224 | struct completion wmi_ready; | ||
225 | u16 wmi_seq; | ||
226 | u16 reply_id; /**< wait for this WMI event */ | ||
227 | void *reply_buf; | ||
228 | u16 reply_size; | ||
229 | struct workqueue_struct *wmi_wq; /* for deferred calls */ | ||
230 | struct work_struct wmi_event_worker; | ||
231 | struct workqueue_struct *wmi_wq_conn; /* for connect worker */ | ||
232 | struct work_struct wmi_connect_worker; | ||
233 | struct work_struct disconnect_worker; | ||
234 | struct timer_list connect_timer; | ||
235 | int pending_connect_cid; | ||
236 | struct list_head pending_wmi_ev; | ||
237 | /* | ||
238 | * protect pending_wmi_ev | ||
239 | * - fill in IRQ from wil6210_irq_misc, | ||
240 | * - consumed in thread by wmi_event_worker | ||
241 | */ | ||
242 | spinlock_t wmi_ev_lock; | ||
243 | /* DMA related */ | ||
244 | struct vring vring_rx; | ||
245 | struct vring vring_tx[WIL6210_MAX_TX_RINGS]; | ||
246 | u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; | ||
247 | /* scan */ | ||
248 | struct cfg80211_scan_request *scan_request; | ||
249 | |||
250 | struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ | ||
251 | /* statistics */ | ||
252 | struct wil6210_stats stats; | ||
253 | /* debugfs */ | ||
254 | struct dentry *debug; | ||
255 | struct debugfs_blob_wrapper fw_code_blob; | ||
256 | struct debugfs_blob_wrapper fw_data_blob; | ||
257 | struct debugfs_blob_wrapper fw_peri_blob; | ||
258 | struct debugfs_blob_wrapper uc_code_blob; | ||
259 | struct debugfs_blob_wrapper uc_data_blob; | ||
260 | struct debugfs_blob_wrapper rgf_blob; | ||
261 | }; | ||
262 | |||
263 | #define wil_to_wiphy(i) (i->wdev->wiphy) | ||
264 | #define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) | ||
265 | #define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) | ||
266 | #define wil_to_wdev(i) (i->wdev) | ||
267 | #define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) | ||
268 | #define wil_to_ndev(i) (wil_to_wdev(i)->netdev) | ||
269 | #define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) | ||
270 | |||
271 | #define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) | ||
272 | #define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) | ||
273 | #define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) | ||
274 | |||
275 | #define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) | ||
276 | #define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) | ||
277 | #define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) | ||
278 | |||
279 | #define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize, \ | ||
280 | groupsize, buf, len, ascii) \ | ||
281 | wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ | ||
282 | prefix_type, rowsize, \ | ||
283 | groupsize, buf, len, ascii) | ||
284 | |||
285 | #define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize, \ | ||
286 | groupsize, buf, len, ascii) \ | ||
287 | wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ | ||
288 | prefix_type, rowsize, \ | ||
289 | groupsize, buf, len, ascii) | ||
290 | |||
291 | void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, | ||
292 | size_t count); | ||
293 | void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, | ||
294 | size_t count); | ||
295 | |||
296 | void *wil_if_alloc(struct device *dev, void __iomem *csr); | ||
297 | void wil_if_free(struct wil6210_priv *wil); | ||
298 | int wil_if_add(struct wil6210_priv *wil); | ||
299 | void wil_if_remove(struct wil6210_priv *wil); | ||
300 | int wil_priv_init(struct wil6210_priv *wil); | ||
301 | void wil_priv_deinit(struct wil6210_priv *wil); | ||
302 | int wil_reset(struct wil6210_priv *wil); | ||
303 | void wil_link_on(struct wil6210_priv *wil); | ||
304 | void wil_link_off(struct wil6210_priv *wil); | ||
305 | int wil_up(struct wil6210_priv *wil); | ||
306 | int wil_down(struct wil6210_priv *wil); | ||
307 | void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); | ||
308 | |||
309 | void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); | ||
310 | void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); | ||
311 | int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, | ||
312 | struct wil6210_mbox_hdr *hdr); | ||
313 | int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); | ||
314 | void wmi_recv_cmd(struct wil6210_priv *wil); | ||
315 | int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, | ||
316 | u16 reply_id, void *reply, u8 reply_size, int to_msec); | ||
317 | void wmi_connect_worker(struct work_struct *work); | ||
318 | void wmi_event_worker(struct work_struct *work); | ||
319 | void wmi_event_flush(struct wil6210_priv *wil); | ||
320 | int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); | ||
321 | int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); | ||
322 | int wmi_set_channel(struct wil6210_priv *wil, int channel); | ||
323 | int wmi_get_channel(struct wil6210_priv *wil, int *channel); | ||
324 | int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); | ||
325 | int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
326 | const void *mac_addr); | ||
327 | int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
328 | const void *mac_addr, int key_len, const void *key); | ||
329 | int wmi_echo(struct wil6210_priv *wil); | ||
330 | int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); | ||
331 | |||
332 | int wil6210_init_irq(struct wil6210_priv *wil, int irq); | ||
333 | void wil6210_fini_irq(struct wil6210_priv *wil, int irq); | ||
334 | void wil6210_disable_irq(struct wil6210_priv *wil); | ||
335 | void wil6210_enable_irq(struct wil6210_priv *wil); | ||
336 | |||
337 | int wil6210_debugfs_init(struct wil6210_priv *wil); | ||
338 | void wil6210_debugfs_remove(struct wil6210_priv *wil); | ||
339 | |||
340 | struct wireless_dev *wil_cfg80211_init(struct device *dev); | ||
341 | void wil_wdev_free(struct wil6210_priv *wil); | ||
342 | |||
343 | int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); | ||
344 | int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype); | ||
345 | void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); | ||
346 | |||
347 | int wil_rx_init(struct wil6210_priv *wil); | ||
348 | void wil_rx_fini(struct wil6210_priv *wil); | ||
349 | |||
350 | /* TX API */ | ||
351 | int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, | ||
352 | int cid, int tid); | ||
353 | void wil_vring_fini_tx(struct wil6210_priv *wil, int id); | ||
354 | |||
355 | netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); | ||
356 | void wil_tx_complete(struct wil6210_priv *wil, int ringid); | ||
357 | |||
358 | /* RX API */ | ||
359 | void wil_rx_handle(struct wil6210_priv *wil); | ||
360 | |||
361 | int wil_iftype_nl2wmi(enum nl80211_iftype type); | ||
362 | |||
363 | #endif /* __WIL6210_H__ */ | ||
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c new file mode 100644 index 000000000000..12915f6e7617 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.c | |||
@@ -0,0 +1,975 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * | ||
4 | * Permission to use, copy, modify, and/or distribute this software for any | ||
5 | * purpose with or without fee is hereby granted, provided that the above | ||
6 | * copyright notice and this permission notice appear in all copies. | ||
7 | * | ||
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
15 | */ | ||
16 | |||
17 | #include <linux/pci.h> | ||
18 | #include <linux/io.h> | ||
19 | #include <linux/list.h> | ||
20 | #include <linux/etherdevice.h> | ||
21 | |||
22 | #include "wil6210.h" | ||
23 | #include "wmi.h" | ||
24 | |||
25 | /** | ||
26 | * WMI event receiving - theory of operations | ||
27 | * | ||
28 | * When firmware about to report WMI event, it fills memory area | ||
29 | * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for | ||
30 | * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. | ||
31 | * | ||
32 | * @wmi_recv_cmd reads event, allocates memory chunk and attaches it to the | ||
33 | * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up | ||
34 | * and handles events within the @wmi_event_worker. Every event get detached | ||
35 | * from list, processed and deleted. | ||
36 | * | ||
37 | * Purpose for this mechanism is to release IRQ thread; otherwise, | ||
38 | * if WMI event handling involves another WMI command flow, this 2-nd flow | ||
39 | * won't be completed because of blocked IRQ thread. | ||
40 | */ | ||
41 | |||
42 | /** | ||
43 | * Addressing - theory of operations | ||
44 | * | ||
45 | * There are several buses present on the WIL6210 card. | ||
46 | * Same memory areas are visible at different address on | ||
47 | * the different busses. There are 3 main bus masters: | ||
48 | * - MAC CPU (ucode) | ||
49 | * - User CPU (firmware) | ||
50 | * - AHB (host) | ||
51 | * | ||
52 | * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing | ||
53 | * AHB addresses starting from 0x880000 | ||
54 | * | ||
55 | * Internally, firmware uses addresses that allows faster access but | ||
56 | * are invisible from the host. To read from these addresses, alternative | ||
57 | * AHB address must be used. | ||
58 | * | ||
59 | * Memory mapping | ||
60 | * Linker address PCI/Host address | ||
61 | * 0x880000 .. 0xa80000 2Mb BAR0 | ||
62 | * 0x800000 .. 0x807000 0x900000 .. 0x907000 28k DCCM | ||
63 | * 0x840000 .. 0x857000 0x908000 .. 0x91f000 92k PERIPH | ||
64 | */ | ||
65 | |||
66 | /** | ||
67 | * @fw_mapping provides memory remapping table | ||
68 | */ | ||
69 | static const struct { | ||
70 | u32 from; /* linker address - from, inclusive */ | ||
71 | u32 to; /* linker address - to, exclusive */ | ||
72 | u32 host; /* PCI/Host address - BAR0 + 0x880000 */ | ||
73 | } fw_mapping[] = { | ||
74 | {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ | ||
75 | {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ | ||
76 | {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ | ||
77 | {0x880000, 0x88a000, 0x880000}, /* various RGF */ | ||
78 | {0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ | ||
79 | /* | ||
80 | * 920000..930000 ucode code RAM | ||
81 | * 930000..932000 ucode data RAM | ||
82 | */ | ||
83 | }; | ||
84 | |||
85 | /** | ||
86 | * return AHB address for given firmware/ucode internal (linker) address | ||
87 | * @x - internal address | ||
88 | * If address have no valid AHB mapping, return 0 | ||
89 | */ | ||
90 | static u32 wmi_addr_remap(u32 x) | ||
91 | { | ||
92 | uint i; | ||
93 | |||
94 | for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { | ||
95 | if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) | ||
96 | return x + fw_mapping[i].host - fw_mapping[i].from; | ||
97 | } | ||
98 | |||
99 | return 0; | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Check address validity for WMI buffer; remap if needed | ||
104 | * @ptr - internal (linker) fw/ucode address | ||
105 | * | ||
106 | * Valid buffer should be DWORD aligned | ||
107 | * | ||
108 | * return address for accessing buffer from the host; | ||
109 | * if buffer is not valid, return NULL. | ||
110 | */ | ||
111 | void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) | ||
112 | { | ||
113 | u32 off; | ||
114 | u32 ptr = le32_to_cpu(ptr_); | ||
115 | |||
116 | if (ptr % 4) | ||
117 | return NULL; | ||
118 | |||
119 | ptr = wmi_addr_remap(ptr); | ||
120 | if (ptr < WIL6210_FW_HOST_OFF) | ||
121 | return NULL; | ||
122 | |||
123 | off = HOSTADDR(ptr); | ||
124 | if (off > WIL6210_MEM_SIZE - 4) | ||
125 | return NULL; | ||
126 | |||
127 | return wil->csr + off; | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Check address validity | ||
132 | */ | ||
133 | void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) | ||
134 | { | ||
135 | u32 off; | ||
136 | |||
137 | if (ptr % 4) | ||
138 | return NULL; | ||
139 | |||
140 | if (ptr < WIL6210_FW_HOST_OFF) | ||
141 | return NULL; | ||
142 | |||
143 | off = HOSTADDR(ptr); | ||
144 | if (off > WIL6210_MEM_SIZE - 4) | ||
145 | return NULL; | ||
146 | |||
147 | return wil->csr + off; | ||
148 | } | ||
149 | |||
150 | int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, | ||
151 | struct wil6210_mbox_hdr *hdr) | ||
152 | { | ||
153 | void __iomem *src = wmi_buffer(wil, ptr); | ||
154 | if (!src) | ||
155 | return -EINVAL; | ||
156 | |||
157 | wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) | ||
163 | { | ||
164 | struct { | ||
165 | struct wil6210_mbox_hdr hdr; | ||
166 | struct wil6210_mbox_hdr_wmi wmi; | ||
167 | } __packed cmd = { | ||
168 | .hdr = { | ||
169 | .type = WIL_MBOX_HDR_TYPE_WMI, | ||
170 | .flags = 0, | ||
171 | .len = cpu_to_le16(sizeof(cmd.wmi) + len), | ||
172 | }, | ||
173 | .wmi = { | ||
174 | .id = cpu_to_le16(cmdid), | ||
175 | .info1 = 0, | ||
176 | }, | ||
177 | }; | ||
178 | struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; | ||
179 | struct wil6210_mbox_ring_desc d_head; | ||
180 | u32 next_head; | ||
181 | void __iomem *dst; | ||
182 | void __iomem *head = wmi_addr(wil, r->head); | ||
183 | uint retry; | ||
184 | |||
185 | if (sizeof(cmd) + len > r->entry_size) { | ||
186 | wil_err(wil, "WMI size too large: %d bytes, max is %d\n", | ||
187 | (int)(sizeof(cmd) + len), r->entry_size); | ||
188 | return -ERANGE; | ||
189 | |||
190 | } | ||
191 | |||
192 | might_sleep(); | ||
193 | |||
194 | if (!test_bit(wil_status_fwready, &wil->status)) { | ||
195 | wil_err(wil, "FW not ready\n"); | ||
196 | return -EAGAIN; | ||
197 | } | ||
198 | |||
199 | if (!head) { | ||
200 | wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); | ||
201 | return -EINVAL; | ||
202 | } | ||
203 | /* read Tx head till it is not busy */ | ||
204 | for (retry = 5; retry > 0; retry--) { | ||
205 | wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); | ||
206 | if (d_head.sync == 0) | ||
207 | break; | ||
208 | msleep(20); | ||
209 | } | ||
210 | if (d_head.sync != 0) { | ||
211 | wil_err(wil, "WMI head busy\n"); | ||
212 | return -EBUSY; | ||
213 | } | ||
214 | /* next head */ | ||
215 | next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); | ||
216 | wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); | ||
217 | /* wait till FW finish with previous command */ | ||
218 | for (retry = 5; retry > 0; retry--) { | ||
219 | r->tail = ioread32(wil->csr + HOST_MBOX + | ||
220 | offsetof(struct wil6210_mbox_ctl, tx.tail)); | ||
221 | if (next_head != r->tail) | ||
222 | break; | ||
223 | msleep(20); | ||
224 | } | ||
225 | if (next_head == r->tail) { | ||
226 | wil_err(wil, "WMI ring full\n"); | ||
227 | return -EBUSY; | ||
228 | } | ||
229 | dst = wmi_buffer(wil, d_head.addr); | ||
230 | if (!dst) { | ||
231 | wil_err(wil, "invalid WMI buffer: 0x%08x\n", | ||
232 | le32_to_cpu(d_head.addr)); | ||
233 | return -EINVAL; | ||
234 | } | ||
235 | cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); | ||
236 | /* set command */ | ||
237 | wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); | ||
238 | wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, | ||
239 | sizeof(cmd), true); | ||
240 | wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, | ||
241 | len, true); | ||
242 | wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); | ||
243 | wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); | ||
244 | /* mark entry as full */ | ||
245 | iowrite32(1, wil->csr + HOSTADDR(r->head) + | ||
246 | offsetof(struct wil6210_mbox_ring_desc, sync)); | ||
247 | /* advance next ptr */ | ||
248 | iowrite32(r->head = next_head, wil->csr + HOST_MBOX + | ||
249 | offsetof(struct wil6210_mbox_ctl, tx.head)); | ||
250 | |||
251 | /* interrupt to FW */ | ||
252 | iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) | ||
258 | { | ||
259 | int rc; | ||
260 | |||
261 | mutex_lock(&wil->wmi_mutex); | ||
262 | rc = __wmi_send(wil, cmdid, buf, len); | ||
263 | mutex_unlock(&wil->wmi_mutex); | ||
264 | |||
265 | return rc; | ||
266 | } | ||
267 | |||
268 | /*=== Event handlers ===*/ | ||
269 | static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) | ||
270 | { | ||
271 | struct net_device *ndev = wil_to_ndev(wil); | ||
272 | struct wireless_dev *wdev = wil->wdev; | ||
273 | struct wmi_ready_event *evt = d; | ||
274 | u32 ver = le32_to_cpu(evt->sw_version); | ||
275 | |||
276 | wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); | ||
277 | |||
278 | if (!is_valid_ether_addr(ndev->dev_addr)) { | ||
279 | memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); | ||
280 | memcpy(ndev->perm_addr, evt->mac, ETH_ALEN); | ||
281 | } | ||
282 | snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), | ||
283 | "%d", ver); | ||
284 | } | ||
285 | |||
286 | static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, | ||
287 | int len) | ||
288 | { | ||
289 | wil_dbg_WMI(wil, "WMI: FW ready\n"); | ||
290 | |||
291 | set_bit(wil_status_fwready, &wil->status); | ||
292 | /* reuse wmi_ready for the firmware ready indication */ | ||
293 | complete(&wil->wmi_ready); | ||
294 | } | ||
295 | |||
296 | static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) | ||
297 | { | ||
298 | struct wmi_rx_mgmt_packet_event *data = d; | ||
299 | struct wiphy *wiphy = wil_to_wiphy(wil); | ||
300 | struct ieee80211_mgmt *rx_mgmt_frame = | ||
301 | (struct ieee80211_mgmt *)data->payload; | ||
302 | int ch_no = data->info.channel+1; | ||
303 | u32 freq = ieee80211_channel_to_frequency(ch_no, | ||
304 | IEEE80211_BAND_60GHZ); | ||
305 | struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); | ||
306 | /* TODO convert LE to CPU */ | ||
307 | s32 signal = 0; /* TODO */ | ||
308 | __le16 fc = rx_mgmt_frame->frame_control; | ||
309 | u32 d_len = le32_to_cpu(data->info.len); | ||
310 | u16 d_status = le16_to_cpu(data->info.status); | ||
311 | |||
312 | wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", | ||
313 | data->info.channel, data->info.mcs, data->info.snr); | ||
314 | wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, | ||
315 | le16_to_cpu(data->info.stype)); | ||
316 | wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", | ||
317 | data->info.qid, data->info.mid, data->info.cid); | ||
318 | |||
319 | if (!channel) { | ||
320 | wil_err(wil, "Frame on unsupported channel\n"); | ||
321 | return; | ||
322 | } | ||
323 | |||
324 | if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { | ||
325 | struct cfg80211_bss *bss; | ||
326 | u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); | ||
327 | u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); | ||
328 | u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); | ||
329 | const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; | ||
330 | size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, | ||
331 | u.beacon.variable); | ||
332 | wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); | ||
333 | |||
334 | bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, | ||
335 | tsf, cap, bi, ie_buf, ie_len, | ||
336 | signal, GFP_KERNEL); | ||
337 | if (bss) { | ||
338 | wil_dbg_WMI(wil, "Added BSS %pM\n", | ||
339 | rx_mgmt_frame->bssid); | ||
340 | cfg80211_put_bss(bss); | ||
341 | } else { | ||
342 | wil_err(wil, "cfg80211_inform_bss() failed\n"); | ||
343 | } | ||
344 | } | ||
345 | } | ||
346 | |||
347 | static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, | ||
348 | void *d, int len) | ||
349 | { | ||
350 | if (wil->scan_request) { | ||
351 | struct wmi_scan_complete_event *data = d; | ||
352 | bool aborted = (data->status != 0); | ||
353 | |||
354 | wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); | ||
355 | cfg80211_scan_done(wil->scan_request, aborted); | ||
356 | wil->scan_request = NULL; | ||
357 | } else { | ||
358 | wil_err(wil, "SCAN_COMPLETE while not scanning\n"); | ||
359 | } | ||
360 | } | ||
361 | |||
362 | static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) | ||
363 | { | ||
364 | struct net_device *ndev = wil_to_ndev(wil); | ||
365 | struct wireless_dev *wdev = wil->wdev; | ||
366 | struct wmi_connect_event *evt = d; | ||
367 | int ch; /* channel number */ | ||
368 | struct station_info sinfo; | ||
369 | u8 *assoc_req_ie, *assoc_resp_ie; | ||
370 | size_t assoc_req_ielen, assoc_resp_ielen; | ||
371 | /* capinfo(u16) + listen_interval(u16) + IEs */ | ||
372 | const size_t assoc_req_ie_offset = sizeof(u16) * 2; | ||
373 | /* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ | ||
374 | const size_t assoc_resp_ie_offset = sizeof(u16) * 3; | ||
375 | |||
376 | if (len < sizeof(*evt)) { | ||
377 | wil_err(wil, "Connect event too short : %d bytes\n", len); | ||
378 | return; | ||
379 | } | ||
380 | if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + | ||
381 | evt->assoc_resp_len) { | ||
382 | wil_err(wil, | ||
383 | "Connect event corrupted : %d != %d + %d + %d + %d\n", | ||
384 | len, (int)sizeof(*evt), evt->beacon_ie_len, | ||
385 | evt->assoc_req_len, evt->assoc_resp_len); | ||
386 | return; | ||
387 | } | ||
388 | ch = evt->channel + 1; | ||
389 | wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", | ||
390 | evt->bssid, ch, evt->cid); | ||
391 | wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, | ||
392 | evt->assoc_info, len - sizeof(*evt), true); | ||
393 | |||
394 | /* figure out IE's */ | ||
395 | assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + | ||
396 | assoc_req_ie_offset]; | ||
397 | assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; | ||
398 | if (evt->assoc_req_len <= assoc_req_ie_offset) { | ||
399 | assoc_req_ie = NULL; | ||
400 | assoc_req_ielen = 0; | ||
401 | } | ||
402 | |||
403 | assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + | ||
404 | evt->assoc_req_len + | ||
405 | assoc_resp_ie_offset]; | ||
406 | assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; | ||
407 | if (evt->assoc_resp_len <= assoc_resp_ie_offset) { | ||
408 | assoc_resp_ie = NULL; | ||
409 | assoc_resp_ielen = 0; | ||
410 | } | ||
411 | |||
412 | if ((wdev->iftype == NL80211_IFTYPE_STATION) || | ||
413 | (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { | ||
414 | if (wdev->sme_state != CFG80211_SME_CONNECTING) { | ||
415 | wil_err(wil, "Not in connecting state\n"); | ||
416 | return; | ||
417 | } | ||
418 | del_timer_sync(&wil->connect_timer); | ||
419 | cfg80211_connect_result(ndev, evt->bssid, | ||
420 | assoc_req_ie, assoc_req_ielen, | ||
421 | assoc_resp_ie, assoc_resp_ielen, | ||
422 | WLAN_STATUS_SUCCESS, GFP_KERNEL); | ||
423 | |||
424 | } else if ((wdev->iftype == NL80211_IFTYPE_AP) || | ||
425 | (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { | ||
426 | memset(&sinfo, 0, sizeof(sinfo)); | ||
427 | |||
428 | sinfo.generation = wil->sinfo_gen++; | ||
429 | |||
430 | if (assoc_req_ie) { | ||
431 | sinfo.assoc_req_ies = assoc_req_ie; | ||
432 | sinfo.assoc_req_ies_len = assoc_req_ielen; | ||
433 | sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; | ||
434 | } | ||
435 | |||
436 | cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); | ||
437 | } | ||
438 | set_bit(wil_status_fwconnected, &wil->status); | ||
439 | |||
440 | /* FIXME FW can transmit only ucast frames to peer */ | ||
441 | /* FIXME real ring_id instead of hard coded 0 */ | ||
442 | memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); | ||
443 | |||
444 | wil->pending_connect_cid = evt->cid; | ||
445 | queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); | ||
446 | } | ||
447 | |||
448 | static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, | ||
449 | void *d, int len) | ||
450 | { | ||
451 | struct wmi_disconnect_event *evt = d; | ||
452 | |||
453 | wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", | ||
454 | evt->bssid, | ||
455 | evt->protocol_reason_status, evt->disconnect_reason); | ||
456 | |||
457 | wil->sinfo_gen++; | ||
458 | |||
459 | wil6210_disconnect(wil, evt->bssid); | ||
460 | clear_bit(wil_status_dontscan, &wil->status); | ||
461 | } | ||
462 | |||
463 | static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) | ||
464 | { | ||
465 | struct wmi_notify_req_done_event *evt = d; | ||
466 | |||
467 | if (len < sizeof(*evt)) { | ||
468 | wil_err(wil, "Short NOTIFY event\n"); | ||
469 | return; | ||
470 | } | ||
471 | |||
472 | wil->stats.tsf = le64_to_cpu(evt->tsf); | ||
473 | wil->stats.snr = le32_to_cpu(evt->snr_val); | ||
474 | wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); | ||
475 | wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); | ||
476 | wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); | ||
477 | wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); | ||
478 | wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); | ||
479 | wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" | ||
480 | "BF status 0x%08x SNR 0x%08x\n" | ||
481 | "Tx Tpt %d goodput %d Rx goodput %d\n" | ||
482 | "Sectors(rx:tx) my %d:%d peer %d:%d\n", | ||
483 | wil->stats.bf_mcs, wil->stats.tsf, evt->status, | ||
484 | wil->stats.snr, le32_to_cpu(evt->tx_tpt), | ||
485 | le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), | ||
486 | wil->stats.my_rx_sector, wil->stats.my_tx_sector, | ||
487 | wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * Firmware reports EAPOL frame using WME event. | ||
492 | * Reconstruct Ethernet frame and deliver it via normal Rx | ||
493 | */ | ||
494 | static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, | ||
495 | void *d, int len) | ||
496 | { | ||
497 | struct net_device *ndev = wil_to_ndev(wil); | ||
498 | struct wmi_eapol_rx_event *evt = d; | ||
499 | u16 eapol_len = le16_to_cpu(evt->eapol_len); | ||
500 | int sz = eapol_len + ETH_HLEN; | ||
501 | struct sk_buff *skb; | ||
502 | struct ethhdr *eth; | ||
503 | |||
504 | wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, | ||
505 | evt->src_mac); | ||
506 | |||
507 | if (eapol_len > 196) { /* TODO: revisit size limit */ | ||
508 | wil_err(wil, "EAPOL too large\n"); | ||
509 | return; | ||
510 | } | ||
511 | |||
512 | skb = alloc_skb(sz, GFP_KERNEL); | ||
513 | if (!skb) { | ||
514 | wil_err(wil, "Failed to allocate skb\n"); | ||
515 | return; | ||
516 | } | ||
517 | eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); | ||
518 | memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); | ||
519 | memcpy(eth->h_source, evt->src_mac, ETH_ALEN); | ||
520 | eth->h_proto = cpu_to_be16(ETH_P_PAE); | ||
521 | memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); | ||
522 | skb->protocol = eth_type_trans(skb, ndev); | ||
523 | if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { | ||
524 | ndev->stats.rx_packets++; | ||
525 | ndev->stats.rx_bytes += skb->len; | ||
526 | } else { | ||
527 | ndev->stats.rx_dropped++; | ||
528 | } | ||
529 | } | ||
530 | |||
531 | static const struct { | ||
532 | int eventid; | ||
533 | void (*handler)(struct wil6210_priv *wil, int eventid, | ||
534 | void *data, int data_len); | ||
535 | } wmi_evt_handlers[] = { | ||
536 | {WMI_READY_EVENTID, wmi_evt_ready}, | ||
537 | {WMI_FW_READY_EVENTID, wmi_evt_fw_ready}, | ||
538 | {WMI_RX_MGMT_PACKET_EVENTID, wmi_evt_rx_mgmt}, | ||
539 | {WMI_SCAN_COMPLETE_EVENTID, wmi_evt_scan_complete}, | ||
540 | {WMI_CONNECT_EVENTID, wmi_evt_connect}, | ||
541 | {WMI_DISCONNECT_EVENTID, wmi_evt_disconnect}, | ||
542 | {WMI_NOTIFY_REQ_DONE_EVENTID, wmi_evt_notify}, | ||
543 | {WMI_EAPOL_RX_EVENTID, wmi_evt_eapol_rx}, | ||
544 | }; | ||
545 | |||
546 | /* | ||
547 | * Run in IRQ context | ||
548 | * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev | ||
549 | * that will be eventually handled by the @wmi_event_worker in the thread | ||
550 | * context of thread "wil6210_wmi" | ||
551 | */ | ||
552 | void wmi_recv_cmd(struct wil6210_priv *wil) | ||
553 | { | ||
554 | struct wil6210_mbox_ring_desc d_tail; | ||
555 | struct wil6210_mbox_hdr hdr; | ||
556 | struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; | ||
557 | struct pending_wmi_event *evt; | ||
558 | u8 *cmd; | ||
559 | void __iomem *src; | ||
560 | ulong flags; | ||
561 | |||
562 | for (;;) { | ||
563 | u16 len; | ||
564 | |||
565 | r->head = ioread32(wil->csr + HOST_MBOX + | ||
566 | offsetof(struct wil6210_mbox_ctl, rx.head)); | ||
567 | if (r->tail == r->head) | ||
568 | return; | ||
569 | |||
570 | /* read cmd from tail */ | ||
571 | wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), | ||
572 | sizeof(struct wil6210_mbox_ring_desc)); | ||
573 | if (d_tail.sync == 0) { | ||
574 | wil_err(wil, "Mbox evt not owned by FW?\n"); | ||
575 | return; | ||
576 | } | ||
577 | |||
578 | if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { | ||
579 | wil_err(wil, "Mbox evt at 0x%08x?\n", | ||
580 | le32_to_cpu(d_tail.addr)); | ||
581 | return; | ||
582 | } | ||
583 | |||
584 | len = le16_to_cpu(hdr.len); | ||
585 | src = wmi_buffer(wil, d_tail.addr) + | ||
586 | sizeof(struct wil6210_mbox_hdr); | ||
587 | evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, | ||
588 | event.wmi) + len, 4), | ||
589 | GFP_KERNEL); | ||
590 | if (!evt) { | ||
591 | wil_err(wil, "kmalloc for WMI event (%d) failed\n", | ||
592 | len); | ||
593 | return; | ||
594 | } | ||
595 | evt->event.hdr = hdr; | ||
596 | cmd = (void *)&evt->event.wmi; | ||
597 | wil_memcpy_fromio_32(cmd, src, len); | ||
598 | /* mark entry as empty */ | ||
599 | iowrite32(0, wil->csr + HOSTADDR(r->tail) + | ||
600 | offsetof(struct wil6210_mbox_ring_desc, sync)); | ||
601 | /* indicate */ | ||
602 | wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", | ||
603 | le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), | ||
604 | hdr.flags); | ||
605 | if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && | ||
606 | (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { | ||
607 | wil_dbg_WMI(wil, "WMI event 0x%04x\n", | ||
608 | evt->event.wmi.id); | ||
609 | } | ||
610 | wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, | ||
611 | &evt->event.hdr, sizeof(hdr) + len, true); | ||
612 | |||
613 | /* advance tail */ | ||
614 | r->tail = r->base + ((r->tail - r->base + | ||
615 | sizeof(struct wil6210_mbox_ring_desc)) % r->size); | ||
616 | iowrite32(r->tail, wil->csr + HOST_MBOX + | ||
617 | offsetof(struct wil6210_mbox_ctl, rx.tail)); | ||
618 | |||
619 | /* add to the pending list */ | ||
620 | spin_lock_irqsave(&wil->wmi_ev_lock, flags); | ||
621 | list_add_tail(&evt->list, &wil->pending_wmi_ev); | ||
622 | spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); | ||
623 | { | ||
624 | int q = queue_work(wil->wmi_wq, | ||
625 | &wil->wmi_event_worker); | ||
626 | wil_dbg_WMI(wil, "queue_work -> %d\n", q); | ||
627 | } | ||
628 | } | ||
629 | } | ||
630 | |||
631 | int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, | ||
632 | u16 reply_id, void *reply, u8 reply_size, int to_msec) | ||
633 | { | ||
634 | int rc; | ||
635 | int remain; | ||
636 | |||
637 | mutex_lock(&wil->wmi_mutex); | ||
638 | |||
639 | rc = __wmi_send(wil, cmdid, buf, len); | ||
640 | if (rc) | ||
641 | goto out; | ||
642 | |||
643 | wil->reply_id = reply_id; | ||
644 | wil->reply_buf = reply; | ||
645 | wil->reply_size = reply_size; | ||
646 | remain = wait_for_completion_timeout(&wil->wmi_ready, | ||
647 | msecs_to_jiffies(to_msec)); | ||
648 | if (0 == remain) { | ||
649 | wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", | ||
650 | cmdid, reply_id, to_msec); | ||
651 | rc = -ETIME; | ||
652 | } else { | ||
653 | wil_dbg_WMI(wil, | ||
654 | "wmi_call(0x%04x->0x%04x) completed in %d msec\n", | ||
655 | cmdid, reply_id, | ||
656 | to_msec - jiffies_to_msecs(remain)); | ||
657 | } | ||
658 | wil->reply_id = 0; | ||
659 | wil->reply_buf = NULL; | ||
660 | wil->reply_size = 0; | ||
661 | out: | ||
662 | mutex_unlock(&wil->wmi_mutex); | ||
663 | |||
664 | return rc; | ||
665 | } | ||
666 | |||
667 | int wmi_echo(struct wil6210_priv *wil) | ||
668 | { | ||
669 | struct wmi_echo_cmd cmd = { | ||
670 | .value = cpu_to_le32(0x12345678), | ||
671 | }; | ||
672 | |||
673 | return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), | ||
674 | WMI_ECHO_RSP_EVENTID, NULL, 0, 20); | ||
675 | } | ||
676 | |||
677 | int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) | ||
678 | { | ||
679 | struct wmi_set_mac_address_cmd cmd; | ||
680 | |||
681 | memcpy(cmd.mac, addr, ETH_ALEN); | ||
682 | |||
683 | wil_dbg_WMI(wil, "Set MAC %pM\n", addr); | ||
684 | |||
685 | return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); | ||
686 | } | ||
687 | |||
688 | int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype) | ||
689 | { | ||
690 | struct wmi_bcon_ctrl_cmd cmd = { | ||
691 | .bcon_interval = cpu_to_le16(bi), | ||
692 | .network_type = wmi_nettype, | ||
693 | .disable_sec_offload = 1, | ||
694 | }; | ||
695 | |||
696 | if (!wil->secure_pcp) | ||
697 | cmd.disable_sec = 1; | ||
698 | |||
699 | return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd)); | ||
700 | } | ||
701 | |||
702 | int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) | ||
703 | { | ||
704 | struct wmi_set_ssid_cmd cmd = { | ||
705 | .ssid_len = cpu_to_le32(ssid_len), | ||
706 | }; | ||
707 | |||
708 | if (ssid_len > sizeof(cmd.ssid)) | ||
709 | return -EINVAL; | ||
710 | |||
711 | memcpy(cmd.ssid, ssid, ssid_len); | ||
712 | |||
713 | return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); | ||
714 | } | ||
715 | |||
716 | int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) | ||
717 | { | ||
718 | int rc; | ||
719 | struct { | ||
720 | struct wil6210_mbox_hdr_wmi wmi; | ||
721 | struct wmi_set_ssid_cmd cmd; | ||
722 | } __packed reply; | ||
723 | int len; /* reply.cmd.ssid_len in CPU order */ | ||
724 | |||
725 | rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, | ||
726 | &reply, sizeof(reply), 20); | ||
727 | if (rc) | ||
728 | return rc; | ||
729 | |||
730 | len = le32_to_cpu(reply.cmd.ssid_len); | ||
731 | if (len > sizeof(reply.cmd.ssid)) | ||
732 | return -EINVAL; | ||
733 | |||
734 | *ssid_len = len; | ||
735 | memcpy(ssid, reply.cmd.ssid, len); | ||
736 | |||
737 | return 0; | ||
738 | } | ||
739 | |||
740 | int wmi_set_channel(struct wil6210_priv *wil, int channel) | ||
741 | { | ||
742 | struct wmi_set_pcp_channel_cmd cmd = { | ||
743 | .channel = channel - 1, | ||
744 | }; | ||
745 | |||
746 | return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); | ||
747 | } | ||
748 | |||
749 | int wmi_get_channel(struct wil6210_priv *wil, int *channel) | ||
750 | { | ||
751 | int rc; | ||
752 | struct { | ||
753 | struct wil6210_mbox_hdr_wmi wmi; | ||
754 | struct wmi_set_pcp_channel_cmd cmd; | ||
755 | } __packed reply; | ||
756 | |||
757 | rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, | ||
758 | WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); | ||
759 | if (rc) | ||
760 | return rc; | ||
761 | |||
762 | if (reply.cmd.channel > 3) | ||
763 | return -EINVAL; | ||
764 | |||
765 | *channel = reply.cmd.channel + 1; | ||
766 | |||
767 | return 0; | ||
768 | } | ||
769 | |||
770 | int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) | ||
771 | { | ||
772 | struct wmi_eapol_tx_cmd *cmd; | ||
773 | struct ethhdr *eth; | ||
774 | u16 eapol_len = skb->len - ETH_HLEN; | ||
775 | void *eapol = skb->data + ETH_HLEN; | ||
776 | uint i; | ||
777 | int rc; | ||
778 | |||
779 | skb_set_mac_header(skb, 0); | ||
780 | eth = eth_hdr(skb); | ||
781 | wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); | ||
782 | for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { | ||
783 | if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) | ||
784 | goto found_dest; | ||
785 | } | ||
786 | |||
787 | return -EINVAL; | ||
788 | |||
789 | found_dest: | ||
790 | /* find out eapol data & len */ | ||
791 | cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); | ||
792 | if (!cmd) | ||
793 | return -EINVAL; | ||
794 | |||
795 | memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); | ||
796 | cmd->eapol_len = cpu_to_le16(eapol_len); | ||
797 | memcpy(cmd->eapol, eapol, eapol_len); | ||
798 | rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); | ||
799 | kfree(cmd); | ||
800 | |||
801 | return rc; | ||
802 | } | ||
803 | |||
804 | int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
805 | const void *mac_addr) | ||
806 | { | ||
807 | struct wmi_delete_cipher_key_cmd cmd = { | ||
808 | .key_index = key_index, | ||
809 | }; | ||
810 | |||
811 | if (mac_addr) | ||
812 | memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); | ||
813 | |||
814 | return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); | ||
815 | } | ||
816 | |||
817 | int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, | ||
818 | const void *mac_addr, int key_len, const void *key) | ||
819 | { | ||
820 | struct wmi_add_cipher_key_cmd cmd = { | ||
821 | .key_index = key_index, | ||
822 | .key_usage = WMI_KEY_USE_PAIRWISE, | ||
823 | .key_len = key_len, | ||
824 | }; | ||
825 | |||
826 | if (!key || (key_len > sizeof(cmd.key))) | ||
827 | return -EINVAL; | ||
828 | |||
829 | memcpy(cmd.key, key, key_len); | ||
830 | if (mac_addr) | ||
831 | memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); | ||
832 | |||
833 | return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); | ||
834 | } | ||
835 | |||
836 | int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) | ||
837 | { | ||
838 | int rc; | ||
839 | u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; | ||
840 | struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); | ||
841 | if (!cmd) { | ||
842 | wil_err(wil, "kmalloc(%d) failed\n", len); | ||
843 | return -ENOMEM; | ||
844 | } | ||
845 | |||
846 | cmd->mgmt_frm_type = type; | ||
847 | /* BUG: FW API define ieLen as u8. Will fix FW */ | ||
848 | cmd->ie_len = cpu_to_le16(ie_len); | ||
849 | memcpy(cmd->ie_info, ie, ie_len); | ||
850 | rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len); | ||
851 | kfree(cmd); | ||
852 | |||
853 | return rc; | ||
854 | } | ||
855 | |||
856 | void wmi_event_flush(struct wil6210_priv *wil) | ||
857 | { | ||
858 | struct pending_wmi_event *evt, *t; | ||
859 | |||
860 | wil_dbg_WMI(wil, "%s()\n", __func__); | ||
861 | |||
862 | list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { | ||
863 | list_del(&evt->list); | ||
864 | kfree(evt); | ||
865 | } | ||
866 | } | ||
867 | |||
868 | static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, | ||
869 | void *d, int len) | ||
870 | { | ||
871 | uint i; | ||
872 | |||
873 | for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { | ||
874 | if (wmi_evt_handlers[i].eventid == id) { | ||
875 | wmi_evt_handlers[i].handler(wil, id, d, len); | ||
876 | return true; | ||
877 | } | ||
878 | } | ||
879 | |||
880 | return false; | ||
881 | } | ||
882 | |||
883 | static void wmi_event_handle(struct wil6210_priv *wil, | ||
884 | struct wil6210_mbox_hdr *hdr) | ||
885 | { | ||
886 | u16 len = le16_to_cpu(hdr->len); | ||
887 | |||
888 | if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && | ||
889 | (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { | ||
890 | struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); | ||
891 | void *evt_data = (void *)(&wmi[1]); | ||
892 | u16 id = le16_to_cpu(wmi->id); | ||
893 | /* check if someone waits for this event */ | ||
894 | if (wil->reply_id && wil->reply_id == id) { | ||
895 | if (wil->reply_buf) { | ||
896 | memcpy(wil->reply_buf, wmi, | ||
897 | min(len, wil->reply_size)); | ||
898 | } else { | ||
899 | wmi_evt_call_handler(wil, id, evt_data, | ||
900 | len - sizeof(*wmi)); | ||
901 | } | ||
902 | wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); | ||
903 | complete(&wil->wmi_ready); | ||
904 | return; | ||
905 | } | ||
906 | /* unsolicited event */ | ||
907 | /* search for handler */ | ||
908 | if (!wmi_evt_call_handler(wil, id, evt_data, | ||
909 | len - sizeof(*wmi))) { | ||
910 | wil_err(wil, "Unhandled event 0x%04x\n", id); | ||
911 | } | ||
912 | } else { | ||
913 | wil_err(wil, "Unknown event type\n"); | ||
914 | print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, | ||
915 | hdr, sizeof(*hdr) + len, true); | ||
916 | } | ||
917 | } | ||
918 | |||
919 | /* | ||
920 | * Retrieve next WMI event from the pending list | ||
921 | */ | ||
922 | static struct list_head *next_wmi_ev(struct wil6210_priv *wil) | ||
923 | { | ||
924 | ulong flags; | ||
925 | struct list_head *ret = NULL; | ||
926 | |||
927 | spin_lock_irqsave(&wil->wmi_ev_lock, flags); | ||
928 | |||
929 | if (!list_empty(&wil->pending_wmi_ev)) { | ||
930 | ret = wil->pending_wmi_ev.next; | ||
931 | list_del(ret); | ||
932 | } | ||
933 | |||
934 | spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); | ||
935 | |||
936 | return ret; | ||
937 | } | ||
938 | |||
939 | /* | ||
940 | * Handler for the WMI events | ||
941 | */ | ||
942 | void wmi_event_worker(struct work_struct *work) | ||
943 | { | ||
944 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | ||
945 | wmi_event_worker); | ||
946 | struct pending_wmi_event *evt; | ||
947 | struct list_head *lh; | ||
948 | |||
949 | while ((lh = next_wmi_ev(wil)) != NULL) { | ||
950 | evt = list_entry(lh, struct pending_wmi_event, list); | ||
951 | wmi_event_handle(wil, &evt->event.hdr); | ||
952 | kfree(evt); | ||
953 | } | ||
954 | } | ||
955 | |||
956 | void wmi_connect_worker(struct work_struct *work) | ||
957 | { | ||
958 | int rc; | ||
959 | struct wil6210_priv *wil = container_of(work, struct wil6210_priv, | ||
960 | wmi_connect_worker); | ||
961 | |||
962 | if (wil->pending_connect_cid < 0) { | ||
963 | wil_err(wil, "No connection pending\n"); | ||
964 | return; | ||
965 | } | ||
966 | |||
967 | wil_dbg_WMI(wil, "Configure for connection CID %d\n", | ||
968 | wil->pending_connect_cid); | ||
969 | |||
970 | rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, | ||
971 | wil->pending_connect_cid, 0); | ||
972 | wil->pending_connect_cid = -1; | ||
973 | if (rc == 0) | ||
974 | wil_link_on(wil); | ||
975 | } | ||
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h new file mode 100644 index 000000000000..3bbf87572b07 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.h | |||
@@ -0,0 +1,1116 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. | ||
3 | * Copyright (c) 2006-2012 Wilocity . | ||
4 | * | ||
5 | * Permission to use, copy, modify, and/or distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | /* | ||
19 | * This file contains the definitions of the WMI protocol specified in the | ||
20 | * Wireless Module Interface (WMI) for the Wilocity | ||
21 | * MARLON 60 Gigabit wireless solution. | ||
22 | * It includes definitions of all the commands and events. | ||
23 | * Commands are messages from the host to the WM. | ||
24 | * Events are messages from the WM to the host. | ||
25 | */ | ||
26 | |||
27 | #ifndef __WILOCITY_WMI_H__ | ||
28 | #define __WILOCITY_WMI_H__ | ||
29 | |||
30 | /* General */ | ||
31 | |||
32 | #define WMI_MAC_LEN (6) | ||
33 | #define WMI_PROX_RANGE_NUM (3) | ||
34 | |||
35 | /* List of Commands */ | ||
36 | enum wmi_command_id { | ||
37 | WMI_CONNECT_CMDID = 0x0001, | ||
38 | WMI_DISCONNECT_CMDID = 0x0003, | ||
39 | WMI_START_SCAN_CMDID = 0x0007, | ||
40 | WMI_SET_BSS_FILTER_CMDID = 0x0009, | ||
41 | WMI_SET_PROBED_SSID_CMDID = 0x000a, | ||
42 | WMI_SET_LISTEN_INT_CMDID = 0x000b, | ||
43 | WMI_BCON_CTRL_CMDID = 0x000f, | ||
44 | WMI_ADD_CIPHER_KEY_CMDID = 0x0016, | ||
45 | WMI_DELETE_CIPHER_KEY_CMDID = 0x0017, | ||
46 | WMI_SET_APPIE_CMDID = 0x003f, | ||
47 | WMI_GET_APPIE_CMDID = 0x0040, | ||
48 | WMI_SET_WSC_STATUS_CMDID = 0x0041, | ||
49 | WMI_PXMT_RANGE_CFG_CMDID = 0x0042, | ||
50 | WMI_PXMT_SNR2_RANGE_CFG_CMDID = 0x0043, | ||
51 | WMI_FAST_MEM_ACC_MODE_CMDID = 0x0300, | ||
52 | WMI_MEM_READ_CMDID = 0x0800, | ||
53 | WMI_MEM_WR_CMDID = 0x0801, | ||
54 | WMI_ECHO_CMDID = 0x0803, | ||
55 | WMI_DEEP_ECHO_CMDID = 0x0804, | ||
56 | WMI_CONFIG_MAC_CMDID = 0x0805, | ||
57 | WMI_CONFIG_PHY_DEBUG_CMDID = 0x0806, | ||
58 | WMI_ADD_STATION_CMDID = 0x0807, | ||
59 | WMI_ADD_DEBUG_TX_PCKT_CMDID = 0x0808, | ||
60 | WMI_PHY_GET_STATISTICS_CMDID = 0x0809, | ||
61 | WMI_FS_TUNE_CMDID = 0x080a, | ||
62 | WMI_CORR_MEASURE_CMDID = 0x080b, | ||
63 | WMI_TEMP_SENSE_CMDID = 0x080e, | ||
64 | WMI_DC_CALIB_CMDID = 0x080f, | ||
65 | WMI_SEND_TONE_CMDID = 0x0810, | ||
66 | WMI_IQ_TX_CALIB_CMDID = 0x0811, | ||
67 | WMI_IQ_RX_CALIB_CMDID = 0x0812, | ||
68 | WMI_SET_UCODE_IDLE_CMDID = 0x0813, | ||
69 | WMI_SET_WORK_MODE_CMDID = 0x0815, | ||
70 | WMI_LO_LEAKAGE_CALIB_CMDID = 0x0816, | ||
71 | WMI_MARLON_R_ACTIVATE_CMDID = 0x0817, | ||
72 | WMI_MARLON_R_READ_CMDID = 0x0818, | ||
73 | WMI_MARLON_R_WRITE_CMDID = 0x0819, | ||
74 | WMI_MARLON_R_TXRX_SEL_CMDID = 0x081a, | ||
75 | MAC_IO_STATIC_PARAMS_CMDID = 0x081b, | ||
76 | MAC_IO_DYNAMIC_PARAMS_CMDID = 0x081c, | ||
77 | WMI_SILENT_RSSI_CALIB_CMDID = 0x081d, | ||
78 | WMI_CFG_RX_CHAIN_CMDID = 0x0820, | ||
79 | WMI_VRING_CFG_CMDID = 0x0821, | ||
80 | WMI_RX_ON_CMDID = 0x0822, | ||
81 | WMI_VRING_BA_EN_CMDID = 0x0823, | ||
82 | WMI_VRING_BA_DIS_CMDID = 0x0824, | ||
83 | WMI_RCP_ADDBA_RESP_CMDID = 0x0825, | ||
84 | WMI_RCP_DELBA_CMDID = 0x0826, | ||
85 | WMI_SET_SSID_CMDID = 0x0827, | ||
86 | WMI_GET_SSID_CMDID = 0x0828, | ||
87 | WMI_SET_PCP_CHANNEL_CMDID = 0x0829, | ||
88 | WMI_GET_PCP_CHANNEL_CMDID = 0x082a, | ||
89 | WMI_SW_TX_REQ_CMDID = 0x082b, | ||
90 | WMI_RX_OFF_CMDID = 0x082c, | ||
91 | WMI_READ_MAC_RXQ_CMDID = 0x0830, | ||
92 | WMI_READ_MAC_TXQ_CMDID = 0x0831, | ||
93 | WMI_WRITE_MAC_RXQ_CMDID = 0x0832, | ||
94 | WMI_WRITE_MAC_TXQ_CMDID = 0x0833, | ||
95 | WMI_WRITE_MAC_XQ_FIELD_CMDID = 0x0834, | ||
96 | WMI_MLME_PUSH_CMDID = 0x0835, | ||
97 | WMI_BEAMFORMING_MGMT_CMDID = 0x0836, | ||
98 | WMI_BF_TXSS_MGMT_CMDID = 0x0837, | ||
99 | WMI_BF_SM_MGMT_CMDID = 0x0838, | ||
100 | WMI_BF_RXSS_MGMT_CMDID = 0x0839, | ||
101 | WMI_SET_SECTORS_CMDID = 0x0849, | ||
102 | WMI_MAINTAIN_PAUSE_CMDID = 0x0850, | ||
103 | WMI_MAINTAIN_RESUME_CMDID = 0x0851, | ||
104 | WMI_RS_MGMT_CMDID = 0x0852, | ||
105 | WMI_RF_MGMT_CMDID = 0x0853, | ||
106 | /* Performance monitoring commands */ | ||
107 | WMI_BF_CTRL_CMDID = 0x0862, | ||
108 | WMI_NOTIFY_REQ_CMDID = 0x0863, | ||
109 | WMI_GET_STATUS_CMDID = 0x0864, | ||
110 | WMI_UNIT_TEST_CMDID = 0x0900, | ||
111 | WMI_HICCUP_CMDID = 0x0901, | ||
112 | WMI_FLASH_READ_CMDID = 0x0902, | ||
113 | WMI_FLASH_WRITE_CMDID = 0x0903, | ||
114 | WMI_SECURITY_UNIT_TEST_CMDID = 0x0904, | ||
115 | |||
116 | WMI_SET_MAC_ADDRESS_CMDID = 0xf003, | ||
117 | WMI_ABORT_SCAN_CMDID = 0xf007, | ||
118 | WMI_SET_PMK_CMDID = 0xf028, | ||
119 | |||
120 | WMI_SET_PROMISCUOUS_MODE_CMDID = 0xf041, | ||
121 | WMI_GET_PMK_CMDID = 0xf048, | ||
122 | WMI_SET_PASSPHRASE_CMDID = 0xf049, | ||
123 | WMI_SEND_ASSOC_RES_CMDID = 0xf04a, | ||
124 | WMI_SET_ASSOC_REQ_RELAY_CMDID = 0xf04b, | ||
125 | WMI_EAPOL_TX_CMDID = 0xf04c, | ||
126 | WMI_MAC_ADDR_REQ_CMDID = 0xf04d, | ||
127 | WMI_FW_VER_CMDID = 0xf04e, | ||
128 | }; | ||
129 | |||
130 | /* | ||
131 | * Commands data structures | ||
132 | */ | ||
133 | |||
134 | /* | ||
135 | * Frame Types | ||
136 | */ | ||
137 | enum wmi_mgmt_frame_type { | ||
138 | WMI_FRAME_BEACON = 0, | ||
139 | WMI_FRAME_PROBE_REQ = 1, | ||
140 | WMI_FRAME_PROBE_RESP = 2, | ||
141 | WMI_FRAME_ASSOC_REQ = 3, | ||
142 | WMI_FRAME_ASSOC_RESP = 4, | ||
143 | WMI_NUM_MGMT_FRAME, | ||
144 | }; | ||
145 | |||
146 | /* | ||
147 | * WMI_CONNECT_CMDID | ||
148 | */ | ||
149 | enum wmi_network_type { | ||
150 | WMI_NETTYPE_INFRA = 0x01, | ||
151 | WMI_NETTYPE_ADHOC = 0x02, | ||
152 | WMI_NETTYPE_ADHOC_CREATOR = 0x04, | ||
153 | WMI_NETTYPE_AP = 0x10, | ||
154 | WMI_NETTYPE_P2P = 0x20, | ||
155 | WMI_NETTYPE_WBE = 0x40, /* PCIE over 60g */ | ||
156 | }; | ||
157 | |||
158 | enum wmi_dot11_auth_mode { | ||
159 | WMI_AUTH11_OPEN = 0x01, | ||
160 | WMI_AUTH11_SHARED = 0x02, | ||
161 | WMI_AUTH11_LEAP = 0x04, | ||
162 | WMI_AUTH11_WSC = 0x08, | ||
163 | }; | ||
164 | |||
165 | enum wmi_auth_mode { | ||
166 | WMI_AUTH_NONE = 0x01, | ||
167 | WMI_AUTH_WPA = 0x02, | ||
168 | WMI_AUTH_WPA2 = 0x04, | ||
169 | WMI_AUTH_WPA_PSK = 0x08, | ||
170 | WMI_AUTH_WPA2_PSK = 0x10, | ||
171 | WMI_AUTH_WPA_CCKM = 0x20, | ||
172 | WMI_AUTH_WPA2_CCKM = 0x40, | ||
173 | }; | ||
174 | |||
175 | enum wmi_crypto_type { | ||
176 | WMI_CRYPT_NONE = 0x01, | ||
177 | WMI_CRYPT_WEP = 0x02, | ||
178 | WMI_CRYPT_TKIP = 0x04, | ||
179 | WMI_CRYPT_AES = 0x08, | ||
180 | WMI_CRYPT_AES_GCMP = 0x20, | ||
181 | }; | ||
182 | |||
183 | |||
184 | enum wmi_connect_ctrl_flag_bits { | ||
185 | WMI_CONNECT_ASSOC_POLICY_USER = 0x0001, | ||
186 | WMI_CONNECT_SEND_REASSOC = 0x0002, | ||
187 | WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER = 0x0004, | ||
188 | WMI_CONNECT_PROFILE_MATCH_DONE = 0x0008, | ||
189 | WMI_CONNECT_IGNORE_AAC_BEACON = 0x0010, | ||
190 | WMI_CONNECT_CSA_FOLLOW_BSS = 0x0020, | ||
191 | WMI_CONNECT_DO_WPA_OFFLOAD = 0x0040, | ||
192 | WMI_CONNECT_DO_NOT_DEAUTH = 0x0080, | ||
193 | }; | ||
194 | |||
195 | #define WMI_MAX_SSID_LEN (32) | ||
196 | |||
197 | struct wmi_connect_cmd { | ||
198 | u8 network_type; | ||
199 | u8 dot11_auth_mode; | ||
200 | u8 auth_mode; | ||
201 | u8 pairwise_crypto_type; | ||
202 | u8 pairwise_crypto_len; | ||
203 | u8 group_crypto_type; | ||
204 | u8 group_crypto_len; | ||
205 | u8 ssid_len; | ||
206 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
207 | u8 channel; | ||
208 | u8 reserved0; | ||
209 | u8 bssid[WMI_MAC_LEN]; | ||
210 | __le32 ctrl_flags; | ||
211 | u8 dst_mac[WMI_MAC_LEN]; | ||
212 | u8 reserved1[2]; | ||
213 | } __packed; | ||
214 | |||
215 | |||
216 | /* | ||
217 | * WMI_RECONNECT_CMDID | ||
218 | */ | ||
219 | struct wmi_reconnect_cmd { | ||
220 | u8 channel; /* hint */ | ||
221 | u8 reserved; | ||
222 | u8 bssid[WMI_MAC_LEN]; /* mandatory if set */ | ||
223 | } __packed; | ||
224 | |||
225 | |||
226 | /* | ||
227 | * WMI_SET_PMK_CMDID | ||
228 | */ | ||
229 | |||
230 | #define WMI_MIN_KEY_INDEX (0) | ||
231 | #define WMI_MAX_KEY_INDEX (3) | ||
232 | #define WMI_MAX_KEY_LEN (32) | ||
233 | #define WMI_PASSPHRASE_LEN (64) | ||
234 | #define WMI_PMK_LEN (32) | ||
235 | |||
236 | struct wmi_set_pmk_cmd { | ||
237 | u8 pmk[WMI_PMK_LEN]; | ||
238 | } __packed; | ||
239 | |||
240 | |||
241 | /* | ||
242 | * WMI_SET_PASSPHRASE_CMDID | ||
243 | */ | ||
244 | struct wmi_set_passphrase_cmd { | ||
245 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
246 | u8 passphrase[WMI_PASSPHRASE_LEN]; | ||
247 | u8 ssid_len; | ||
248 | u8 passphrase_len; | ||
249 | } __packed; | ||
250 | |||
251 | /* | ||
252 | * WMI_ADD_CIPHER_KEY_CMDID | ||
253 | */ | ||
254 | enum wmi_key_usage { | ||
255 | WMI_KEY_USE_PAIRWISE = 0, | ||
256 | WMI_KEY_USE_GROUP = 1, | ||
257 | WMI_KEY_USE_TX = 2, /* default Tx Key - Static WEP only */ | ||
258 | }; | ||
259 | |||
260 | struct wmi_add_cipher_key_cmd { | ||
261 | u8 key_index; | ||
262 | u8 key_type; | ||
263 | u8 key_usage; /* enum wmi_key_usage */ | ||
264 | u8 key_len; | ||
265 | u8 key_rsc[8]; /* key replay sequence counter */ | ||
266 | u8 key[WMI_MAX_KEY_LEN]; | ||
267 | u8 key_op_ctrl; /* Additional Key Control information */ | ||
268 | u8 mac[WMI_MAC_LEN]; | ||
269 | } __packed; | ||
270 | |||
271 | /* | ||
272 | * WMI_DELETE_CIPHER_KEY_CMDID | ||
273 | */ | ||
274 | struct wmi_delete_cipher_key_cmd { | ||
275 | u8 key_index; | ||
276 | u8 mac[WMI_MAC_LEN]; | ||
277 | } __packed; | ||
278 | |||
279 | |||
280 | /* | ||
281 | * WMI_START_SCAN_CMDID | ||
282 | * | ||
283 | * Start L1 scan operation | ||
284 | * | ||
285 | * Returned events: | ||
286 | * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp. | ||
287 | * - WMI_SCAN_COMPLETE_EVENTID | ||
288 | */ | ||
289 | enum wmi_scan_type { | ||
290 | WMI_LONG_SCAN = 0, | ||
291 | WMI_SHORT_SCAN = 1, | ||
292 | }; | ||
293 | |||
294 | struct wmi_start_scan_cmd { | ||
295 | u8 reserved[8]; | ||
296 | __le32 home_dwell_time; /* Max duration in the home channel(ms) */ | ||
297 | __le32 force_scan_interval; /* Time interval between scans (ms)*/ | ||
298 | u8 scan_type; /* wmi_scan_type */ | ||
299 | u8 num_channels; /* how many channels follow */ | ||
300 | struct { | ||
301 | u8 channel; | ||
302 | u8 reserved; | ||
303 | } channel_list[0]; /* channels ID's */ | ||
304 | /* 0 - 58320 MHz */ | ||
305 | /* 1 - 60480 MHz */ | ||
306 | /* 2 - 62640 MHz */ | ||
307 | } __packed; | ||
308 | |||
309 | /* | ||
310 | * WMI_SET_PROBED_SSID_CMDID | ||
311 | */ | ||
312 | #define MAX_PROBED_SSID_INDEX (15) | ||
313 | |||
314 | enum wmi_ssid_flag { | ||
315 | WMI_SSID_FLAG_DISABLE = 0, /* disables entry */ | ||
316 | WMI_SSID_FLAG_SPECIFIC = 1, /* probes specified ssid */ | ||
317 | WMI_SSID_FLAG_ANY = 2, /* probes for any ssid */ | ||
318 | }; | ||
319 | |||
320 | struct wmi_probed_ssid_cmd { | ||
321 | u8 entry_index; /* 0 to MAX_PROBED_SSID_INDEX */ | ||
322 | u8 flag; /* enum wmi_ssid_flag */ | ||
323 | u8 ssid_len; | ||
324 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
325 | } __packed; | ||
326 | |||
327 | /* | ||
328 | * WMI_SET_APPIE_CMDID | ||
329 | * Add Application specified IE to a management frame | ||
330 | */ | ||
331 | struct wmi_set_appie_cmd { | ||
332 | u8 mgmt_frm_type; /* enum wmi_mgmt_frame_type */ | ||
333 | u8 reserved; | ||
334 | __le16 ie_len; /* Length of the IE to be added to MGMT frame */ | ||
335 | u8 ie_info[0]; | ||
336 | } __packed; | ||
337 | |||
338 | #define WMI_MAX_IE_LEN (1024) | ||
339 | |||
340 | struct wmi_pxmt_range_cfg_cmd { | ||
341 | u8 dst_mac[WMI_MAC_LEN]; | ||
342 | __le16 range; | ||
343 | } __packed; | ||
344 | |||
345 | struct wmi_pxmt_snr2_range_cfg_cmd { | ||
346 | s8 snr2range_arr[WMI_PROX_RANGE_NUM-1]; | ||
347 | } __packed; | ||
348 | |||
349 | /* | ||
350 | * WMI_RF_MGMT_CMDID | ||
351 | */ | ||
352 | enum wmi_rf_mgmt_type { | ||
353 | WMI_RF_MGMT_W_DISABLE = 0, | ||
354 | WMI_RF_MGMT_W_ENABLE = 1, | ||
355 | WMI_RF_MGMT_GET_STATUS = 2, | ||
356 | }; | ||
357 | |||
358 | struct wmi_rf_mgmt_cmd { | ||
359 | __le32 rf_mgmt_type; | ||
360 | } __packed; | ||
361 | |||
362 | /* | ||
363 | * WMI_SET_SSID_CMDID | ||
364 | */ | ||
365 | struct wmi_set_ssid_cmd { | ||
366 | __le32 ssid_len; | ||
367 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
368 | } __packed; | ||
369 | |||
370 | /* | ||
371 | * WMI_SET_PCP_CHANNEL_CMDID | ||
372 | */ | ||
373 | struct wmi_set_pcp_channel_cmd { | ||
374 | u8 channel; | ||
375 | u8 reserved[3]; | ||
376 | } __packed; | ||
377 | |||
378 | /* | ||
379 | * WMI_BCON_CTRL_CMDID | ||
380 | */ | ||
381 | struct wmi_bcon_ctrl_cmd { | ||
382 | __le16 bcon_interval; | ||
383 | __le16 frag_num; | ||
384 | __le64 ss_mask; | ||
385 | u8 network_type; | ||
386 | u8 reserved; | ||
387 | u8 disable_sec_offload; | ||
388 | u8 disable_sec; | ||
389 | } __packed; | ||
390 | |||
391 | /* | ||
392 | * WMI_SW_TX_REQ_CMDID | ||
393 | */ | ||
394 | struct wmi_sw_tx_req_cmd { | ||
395 | u8 dst_mac[WMI_MAC_LEN]; | ||
396 | __le16 len; | ||
397 | u8 payload[0]; | ||
398 | } __packed; | ||
399 | |||
400 | /* | ||
401 | * WMI_VRING_CFG_CMDID | ||
402 | */ | ||
403 | |||
404 | struct wmi_sw_ring_cfg { | ||
405 | __le64 ring_mem_base; | ||
406 | __le16 ring_size; | ||
407 | __le16 max_mpdu_size; | ||
408 | } __packed; | ||
409 | |||
410 | struct wmi_vring_cfg_schd { | ||
411 | __le16 priority; | ||
412 | __le16 timeslot_us; | ||
413 | } __packed; | ||
414 | |||
415 | enum wmi_vring_cfg_encap_trans_type { | ||
416 | WMI_VRING_ENC_TYPE_802_3 = 0, | ||
417 | WMI_VRING_ENC_TYPE_NATIVE_WIFI = 1, | ||
418 | }; | ||
419 | |||
420 | enum wmi_vring_cfg_ds_cfg { | ||
421 | WMI_VRING_DS_PBSS = 0, | ||
422 | WMI_VRING_DS_STATION = 1, | ||
423 | WMI_VRING_DS_AP = 2, | ||
424 | WMI_VRING_DS_ADDR4 = 3, | ||
425 | }; | ||
426 | |||
427 | enum wmi_vring_cfg_nwifi_ds_trans_type { | ||
428 | WMI_NWIFI_TX_TRANS_MODE_NO = 0, | ||
429 | WMI_NWIFI_TX_TRANS_MODE_AP2PBSS = 1, | ||
430 | WMI_NWIFI_TX_TRANS_MODE_STA2PBSS = 2, | ||
431 | }; | ||
432 | |||
433 | enum wmi_vring_cfg_schd_params_priority { | ||
434 | WMI_SCH_PRIO_REGULAR = 0, | ||
435 | WMI_SCH_PRIO_HIGH = 1, | ||
436 | }; | ||
437 | |||
438 | struct wmi_vring_cfg { | ||
439 | struct wmi_sw_ring_cfg tx_sw_ring; | ||
440 | u8 ringid; /* 0-23 vrings */ | ||
441 | |||
442 | #define CIDXTID_CID_POS (0) | ||
443 | #define CIDXTID_CID_LEN (4) | ||
444 | #define CIDXTID_CID_MSK (0xF) | ||
445 | #define CIDXTID_TID_POS (4) | ||
446 | #define CIDXTID_TID_LEN (4) | ||
447 | #define CIDXTID_TID_MSK (0xF0) | ||
448 | u8 cidxtid; | ||
449 | |||
450 | u8 encap_trans_type; | ||
451 | u8 ds_cfg; /* 802.3 DS cfg */ | ||
452 | u8 nwifi_ds_trans_type; | ||
453 | |||
454 | #define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0) | ||
455 | #define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1) | ||
456 | #define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1) | ||
457 | #define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1) | ||
458 | #define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1) | ||
459 | #define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2) | ||
460 | u8 mac_ctrl; | ||
461 | |||
462 | #define VRING_CFG_TO_RESOLUTION_VALUE_POS (0) | ||
463 | #define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6) | ||
464 | #define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F) | ||
465 | u8 to_resolution; | ||
466 | u8 agg_max_wsize; | ||
467 | struct wmi_vring_cfg_schd schd_params; | ||
468 | } __packed; | ||
469 | |||
470 | enum wmi_vring_cfg_cmd_action { | ||
471 | WMI_VRING_CMD_ADD = 0, | ||
472 | WMI_VRING_CMD_MODIFY = 1, | ||
473 | WMI_VRING_CMD_DELETE = 2, | ||
474 | }; | ||
475 | |||
476 | struct wmi_vring_cfg_cmd { | ||
477 | __le32 action; | ||
478 | struct wmi_vring_cfg vring_cfg; | ||
479 | } __packed; | ||
480 | |||
481 | /* | ||
482 | * WMI_VRING_BA_EN_CMDID | ||
483 | */ | ||
484 | struct wmi_vring_ba_en_cmd { | ||
485 | u8 ringid; | ||
486 | u8 agg_max_wsize; | ||
487 | __le16 ba_timeout; | ||
488 | } __packed; | ||
489 | |||
490 | /* | ||
491 | * WMI_VRING_BA_DIS_CMDID | ||
492 | */ | ||
493 | struct wmi_vring_ba_dis_cmd { | ||
494 | u8 ringid; | ||
495 | u8 reserved; | ||
496 | __le16 reason; | ||
497 | } __packed; | ||
498 | |||
499 | /* | ||
500 | * WMI_NOTIFY_REQ_CMDID | ||
501 | */ | ||
502 | struct wmi_notify_req_cmd { | ||
503 | u8 cid; | ||
504 | u8 reserved[3]; | ||
505 | __le32 interval_usec; | ||
506 | } __packed; | ||
507 | |||
508 | /* | ||
509 | * WMI_CFG_RX_CHAIN_CMDID | ||
510 | */ | ||
511 | enum wmi_sniffer_cfg_mode { | ||
512 | WMI_SNIFFER_OFF = 0, | ||
513 | WMI_SNIFFER_ON = 1, | ||
514 | }; | ||
515 | |||
516 | enum wmi_sniffer_cfg_phy_info_mode { | ||
517 | WMI_SNIFFER_PHY_INFO_DISABLED = 0, | ||
518 | WMI_SNIFFER_PHY_INFO_ENABLED = 1, | ||
519 | }; | ||
520 | |||
521 | enum wmi_sniffer_cfg_phy_support { | ||
522 | WMI_SNIFFER_CP = 0, | ||
523 | WMI_SNIFFER_DP = 1, | ||
524 | WMI_SNIFFER_BOTH_PHYS = 2, | ||
525 | }; | ||
526 | |||
527 | struct wmi_sniffer_cfg { | ||
528 | __le32 mode; /* enum wmi_sniffer_cfg_mode */ | ||
529 | __le32 phy_info_mode; /* enum wmi_sniffer_cfg_phy_info_mode */ | ||
530 | __le32 phy_support; /* enum wmi_sniffer_cfg_phy_support */ | ||
531 | u8 channel; | ||
532 | u8 reserved[3]; | ||
533 | } __packed; | ||
534 | |||
535 | enum wmi_cfg_rx_chain_cmd_action { | ||
536 | WMI_RX_CHAIN_ADD = 0, | ||
537 | WMI_RX_CHAIN_DEL = 1, | ||
538 | }; | ||
539 | |||
540 | enum wmi_cfg_rx_chain_cmd_decap_trans_type { | ||
541 | WMI_DECAP_TYPE_802_3 = 0, | ||
542 | WMI_DECAP_TYPE_NATIVE_WIFI = 1, | ||
543 | }; | ||
544 | |||
545 | enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { | ||
546 | WMI_NWIFI_RX_TRANS_MODE_NO = 0, | ||
547 | WMI_NWIFI_RX_TRANS_MODE_PBSS2AP = 1, | ||
548 | WMI_NWIFI_RX_TRANS_MODE_PBSS2STA = 2, | ||
549 | }; | ||
550 | |||
551 | struct wmi_cfg_rx_chain_cmd { | ||
552 | __le32 action; | ||
553 | struct wmi_sw_ring_cfg rx_sw_ring; | ||
554 | u8 mid; | ||
555 | u8 decap_trans_type; | ||
556 | |||
557 | #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) | ||
558 | #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) | ||
559 | #define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) | ||
560 | u8 l2_802_3_offload_ctrl; | ||
561 | |||
562 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) | ||
563 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1) | ||
564 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1) | ||
565 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1) | ||
566 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1) | ||
567 | #define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2) | ||
568 | u8 l2_nwifi_offload_ctrl; | ||
569 | |||
570 | u8 vlan_id; | ||
571 | u8 nwifi_ds_trans_type; | ||
572 | |||
573 | #define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0) | ||
574 | #define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1) | ||
575 | #define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1) | ||
576 | #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1) | ||
577 | #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1) | ||
578 | #define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2) | ||
579 | u8 l3_l4_ctrl; | ||
580 | |||
581 | #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0) | ||
582 | #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1) | ||
583 | #define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1) | ||
584 | #define RING_CTRL_OVERRIDE_WB_THRSH_POS (1) | ||
585 | #define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1) | ||
586 | #define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2) | ||
587 | #define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2) | ||
588 | #define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1) | ||
589 | #define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4) | ||
590 | #define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3) | ||
591 | #define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1) | ||
592 | #define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8) | ||
593 | u8 ring_ctrl; | ||
594 | |||
595 | __le16 prefetch_thrsh; | ||
596 | __le16 wb_thrsh; | ||
597 | __le32 itr_value; | ||
598 | __le16 host_thrsh; | ||
599 | u8 reserved[2]; | ||
600 | struct wmi_sniffer_cfg sniffer_cfg; | ||
601 | } __packed; | ||
602 | |||
603 | /* | ||
604 | * WMI_RCP_ADDBA_RESP_CMDID | ||
605 | */ | ||
606 | struct wmi_rcp_addba_resp_cmd { | ||
607 | |||
608 | #define CIDXTID_CID_POS (0) | ||
609 | #define CIDXTID_CID_LEN (4) | ||
610 | #define CIDXTID_CID_MSK (0xF) | ||
611 | #define CIDXTID_TID_POS (4) | ||
612 | #define CIDXTID_TID_LEN (4) | ||
613 | #define CIDXTID_TID_MSK (0xF0) | ||
614 | u8 cidxtid; | ||
615 | |||
616 | u8 dialog_token; | ||
617 | __le16 status_code; | ||
618 | __le16 ba_param_set; /* ieee80211_ba_parameterset field to send */ | ||
619 | __le16 ba_timeout; | ||
620 | } __packed; | ||
621 | |||
622 | /* | ||
623 | * WMI_RCP_DELBA_CMDID | ||
624 | */ | ||
625 | struct wmi_rcp_delba_cmd { | ||
626 | |||
627 | #define CIDXTID_CID_POS (0) | ||
628 | #define CIDXTID_CID_LEN (4) | ||
629 | #define CIDXTID_CID_MSK (0xF) | ||
630 | #define CIDXTID_TID_POS (4) | ||
631 | #define CIDXTID_TID_LEN (4) | ||
632 | #define CIDXTID_TID_MSK (0xF0) | ||
633 | u8 cidxtid; | ||
634 | |||
635 | u8 reserved; | ||
636 | __le16 reason; | ||
637 | } __packed; | ||
638 | |||
639 | /* | ||
640 | * WMI_RCP_ADDBA_REQ_CMDID | ||
641 | */ | ||
642 | struct wmi_rcp_addba_req_cmd { | ||
643 | |||
644 | #define CIDXTID_CID_POS (0) | ||
645 | #define CIDXTID_CID_LEN (4) | ||
646 | #define CIDXTID_CID_MSK (0xF) | ||
647 | #define CIDXTID_TID_POS (4) | ||
648 | #define CIDXTID_TID_LEN (4) | ||
649 | #define CIDXTID_TID_MSK (0xF0) | ||
650 | u8 cidxtid; | ||
651 | |||
652 | u8 dialog_token; | ||
653 | /* ieee80211_ba_parameterset field as it received */ | ||
654 | __le16 ba_param_set; | ||
655 | __le16 ba_timeout; | ||
656 | /* ieee80211_ba_seqstrl field as it received */ | ||
657 | __le16 ba_seq_ctrl; | ||
658 | } __packed; | ||
659 | |||
660 | /* | ||
661 | * WMI_SET_MAC_ADDRESS_CMDID | ||
662 | */ | ||
663 | struct wmi_set_mac_address_cmd { | ||
664 | u8 mac[WMI_MAC_LEN]; | ||
665 | u8 reserved[2]; | ||
666 | } __packed; | ||
667 | |||
668 | |||
669 | /* | ||
670 | * WMI_EAPOL_TX_CMDID | ||
671 | */ | ||
672 | struct wmi_eapol_tx_cmd { | ||
673 | u8 dst_mac[WMI_MAC_LEN]; | ||
674 | __le16 eapol_len; | ||
675 | u8 eapol[0]; | ||
676 | } __packed; | ||
677 | |||
678 | /* | ||
679 | * WMI_ECHO_CMDID | ||
680 | * | ||
681 | * Check FW is alive | ||
682 | * | ||
683 | * WMI_DEEP_ECHO_CMDID | ||
684 | * | ||
685 | * Check FW and ucode are alive | ||
686 | * | ||
687 | * Returned event: WMI_ECHO_RSP_EVENTID | ||
688 | * same event for both commands | ||
689 | */ | ||
690 | struct wmi_echo_cmd { | ||
691 | __le32 value; | ||
692 | } __packed; | ||
693 | |||
694 | /* | ||
695 | * WMI Events | ||
696 | */ | ||
697 | |||
698 | /* | ||
699 | * List of Events (target to host) | ||
700 | */ | ||
701 | enum wmi_event_id { | ||
702 | WMI_IMM_RSP_EVENTID = 0x0000, | ||
703 | WMI_READY_EVENTID = 0x1001, | ||
704 | WMI_CONNECT_EVENTID = 0x1002, | ||
705 | WMI_DISCONNECT_EVENTID = 0x1003, | ||
706 | WMI_SCAN_COMPLETE_EVENTID = 0x100a, | ||
707 | WMI_REPORT_STATISTICS_EVENTID = 0x100b, | ||
708 | WMI_RD_MEM_RSP_EVENTID = 0x1800, | ||
709 | WMI_FW_READY_EVENTID = 0x1801, | ||
710 | WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID = 0x0200, | ||
711 | WMI_ECHO_RSP_EVENTID = 0x1803, | ||
712 | WMI_CONFIG_MAC_DONE_EVENTID = 0x1805, | ||
713 | WMI_CONFIG_PHY_DEBUG_DONE_EVENTID = 0x1806, | ||
714 | WMI_ADD_STATION_DONE_EVENTID = 0x1807, | ||
715 | WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID = 0x1808, | ||
716 | WMI_PHY_GET_STATISTICS_EVENTID = 0x1809, | ||
717 | WMI_FS_TUNE_DONE_EVENTID = 0x180a, | ||
718 | WMI_CORR_MEASURE_DONE_EVENTID = 0x180b, | ||
719 | WMI_TEMP_SENSE_DONE_EVENTID = 0x180e, | ||
720 | WMI_DC_CALIB_DONE_EVENTID = 0x180f, | ||
721 | WMI_IQ_TX_CALIB_DONE_EVENTID = 0x1811, | ||
722 | WMI_IQ_RX_CALIB_DONE_EVENTID = 0x1812, | ||
723 | WMI_SET_WORK_MODE_DONE_EVENTID = 0x1815, | ||
724 | WMI_LO_LEAKAGE_CALIB_DONE_EVENTID = 0x1816, | ||
725 | WMI_MARLON_R_ACTIVATE_DONE_EVENTID = 0x1817, | ||
726 | WMI_MARLON_R_READ_DONE_EVENTID = 0x1818, | ||
727 | WMI_MARLON_R_WRITE_DONE_EVENTID = 0x1819, | ||
728 | WMI_MARLON_R_TXRX_SEL_DONE_EVENTID = 0x181a, | ||
729 | WMI_SILENT_RSSI_CALIB_DONE_EVENTID = 0x181d, | ||
730 | |||
731 | WMI_CFG_RX_CHAIN_DONE_EVENTID = 0x1820, | ||
732 | WMI_VRING_CFG_DONE_EVENTID = 0x1821, | ||
733 | WMI_RX_ON_DONE_EVENTID = 0x1822, | ||
734 | WMI_BA_STATUS_EVENTID = 0x1823, | ||
735 | WMI_RCP_ADDBA_REQ_EVENTID = 0x1824, | ||
736 | WMI_ADDBA_RESP_SENT_EVENTID = 0x1825, | ||
737 | WMI_DELBA_EVENTID = 0x1826, | ||
738 | WMI_GET_SSID_EVENTID = 0x1828, | ||
739 | WMI_GET_PCP_CHANNEL_EVENTID = 0x182a, | ||
740 | WMI_SW_TX_COMPLETE_EVENTID = 0x182b, | ||
741 | WMI_RX_OFF_DONE_EVENTID = 0x182c, | ||
742 | |||
743 | WMI_READ_MAC_RXQ_EVENTID = 0x1830, | ||
744 | WMI_READ_MAC_TXQ_EVENTID = 0x1831, | ||
745 | WMI_WRITE_MAC_RXQ_EVENTID = 0x1832, | ||
746 | WMI_WRITE_MAC_TXQ_EVENTID = 0x1833, | ||
747 | WMI_WRITE_MAC_XQ_FIELD_EVENTID = 0x1834, | ||
748 | |||
749 | WMI_BEAFORMING_MGMT_DONE_EVENTID = 0x1836, | ||
750 | WMI_BF_TXSS_MGMT_DONE_EVENTID = 0x1837, | ||
751 | WMI_BF_RXSS_MGMT_DONE_EVENTID = 0x1839, | ||
752 | WMI_RS_MGMT_DONE_EVENTID = 0x1852, | ||
753 | WMI_RF_MGMT_STATUS_EVENTID = 0x1853, | ||
754 | WMI_BF_SM_MGMT_DONE_EVENTID = 0x1838, | ||
755 | WMI_RX_MGMT_PACKET_EVENTID = 0x1840, | ||
756 | |||
757 | /* Performance monitoring events */ | ||
758 | WMI_DATA_PORT_OPEN_EVENTID = 0x1860, | ||
759 | WMI_WBE_LINKDOWN_EVENTID = 0x1861, | ||
760 | |||
761 | WMI_BF_CTRL_DONE_EVENTID = 0x1862, | ||
762 | WMI_NOTIFY_REQ_DONE_EVENTID = 0x1863, | ||
763 | WMI_GET_STATUS_DONE_EVENTID = 0x1864, | ||
764 | |||
765 | WMI_UNIT_TEST_EVENTID = 0x1900, | ||
766 | WMI_FLASH_READ_DONE_EVENTID = 0x1902, | ||
767 | WMI_FLASH_WRITE_DONE_EVENTID = 0x1903, | ||
768 | |||
769 | WMI_SET_CHANNEL_EVENTID = 0x9000, | ||
770 | WMI_ASSOC_REQ_EVENTID = 0x9001, | ||
771 | WMI_EAPOL_RX_EVENTID = 0x9002, | ||
772 | WMI_MAC_ADDR_RESP_EVENTID = 0x9003, | ||
773 | WMI_FW_VER_EVENTID = 0x9004, | ||
774 | }; | ||
775 | |||
776 | /* | ||
777 | * Events data structures | ||
778 | */ | ||
779 | |||
780 | /* | ||
781 | * WMI_RF_MGMT_STATUS_EVENTID | ||
782 | */ | ||
783 | enum wmi_rf_status { | ||
784 | WMI_RF_ENABLED = 0, | ||
785 | WMI_RF_DISABLED_HW = 1, | ||
786 | WMI_RF_DISABLED_SW = 2, | ||
787 | WMI_RF_DISABLED_HW_SW = 3, | ||
788 | }; | ||
789 | |||
790 | struct wmi_rf_mgmt_status_event { | ||
791 | __le32 rf_status; | ||
792 | } __packed; | ||
793 | |||
794 | /* | ||
795 | * WMI_GET_STATUS_DONE_EVENTID | ||
796 | */ | ||
797 | struct wmi_get_status_done_event { | ||
798 | __le32 is_associated; | ||
799 | u8 cid; | ||
800 | u8 reserved0[3]; | ||
801 | u8 bssid[WMI_MAC_LEN]; | ||
802 | u8 channel; | ||
803 | u8 reserved1; | ||
804 | u8 network_type; | ||
805 | u8 reserved2[3]; | ||
806 | __le32 ssid_len; | ||
807 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
808 | __le32 rf_status; | ||
809 | __le32 is_secured; | ||
810 | } __packed; | ||
811 | |||
812 | /* | ||
813 | * WMI_FW_VER_EVENTID | ||
814 | */ | ||
815 | struct wmi_fw_ver_event { | ||
816 | u8 major; | ||
817 | u8 minor; | ||
818 | __le16 subminor; | ||
819 | __le16 build; | ||
820 | } __packed; | ||
821 | |||
822 | /* | ||
823 | * WMI_MAC_ADDR_RESP_EVENTID | ||
824 | */ | ||
825 | struct wmi_mac_addr_resp_event { | ||
826 | u8 mac[WMI_MAC_LEN]; | ||
827 | u8 auth_mode; | ||
828 | u8 crypt_mode; | ||
829 | __le32 offload_mode; | ||
830 | } __packed; | ||
831 | |||
832 | /* | ||
833 | * WMI_EAPOL_RX_EVENTID | ||
834 | */ | ||
835 | struct wmi_eapol_rx_event { | ||
836 | u8 src_mac[WMI_MAC_LEN]; | ||
837 | __le16 eapol_len; | ||
838 | u8 eapol[0]; | ||
839 | } __packed; | ||
840 | |||
841 | /* | ||
842 | * WMI_READY_EVENTID | ||
843 | */ | ||
844 | enum wmi_phy_capability { | ||
845 | WMI_11A_CAPABILITY = 1, | ||
846 | WMI_11G_CAPABILITY = 2, | ||
847 | WMI_11AG_CAPABILITY = 3, | ||
848 | WMI_11NA_CAPABILITY = 4, | ||
849 | WMI_11NG_CAPABILITY = 5, | ||
850 | WMI_11NAG_CAPABILITY = 6, | ||
851 | WMI_11AD_CAPABILITY = 7, | ||
852 | WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY, | ||
853 | }; | ||
854 | |||
855 | struct wmi_ready_event { | ||
856 | __le32 sw_version; | ||
857 | __le32 abi_version; | ||
858 | u8 mac[WMI_MAC_LEN]; | ||
859 | u8 phy_capability; /* enum wmi_phy_capability */ | ||
860 | u8 reserved; | ||
861 | } __packed; | ||
862 | |||
863 | /* | ||
864 | * WMI_NOTIFY_REQ_DONE_EVENTID | ||
865 | */ | ||
866 | struct wmi_notify_req_done_event { | ||
867 | __le32 status; | ||
868 | __le64 tsf; | ||
869 | __le32 snr_val; | ||
870 | __le32 tx_tpt; | ||
871 | __le32 tx_goodput; | ||
872 | __le32 rx_goodput; | ||
873 | __le16 bf_mcs; | ||
874 | __le16 my_rx_sector; | ||
875 | __le16 my_tx_sector; | ||
876 | __le16 other_rx_sector; | ||
877 | __le16 other_tx_sector; | ||
878 | __le16 range; | ||
879 | } __packed; | ||
880 | |||
881 | /* | ||
882 | * WMI_CONNECT_EVENTID | ||
883 | */ | ||
884 | struct wmi_connect_event { | ||
885 | u8 channel; | ||
886 | u8 reserved0; | ||
887 | u8 bssid[WMI_MAC_LEN]; | ||
888 | __le16 listen_interval; | ||
889 | __le16 beacon_interval; | ||
890 | u8 network_type; | ||
891 | u8 reserved1[3]; | ||
892 | u8 beacon_ie_len; | ||
893 | u8 assoc_req_len; | ||
894 | u8 assoc_resp_len; | ||
895 | u8 cid; | ||
896 | u8 reserved2[3]; | ||
897 | u8 assoc_info[0]; | ||
898 | } __packed; | ||
899 | |||
900 | /* | ||
901 | * WMI_DISCONNECT_EVENTID | ||
902 | */ | ||
903 | enum wmi_disconnect_reason { | ||
904 | WMI_DIS_REASON_NO_NETWORK_AVAIL = 1, | ||
905 | WMI_DIS_REASON_LOST_LINK = 2, /* bmiss */ | ||
906 | WMI_DIS_REASON_DISCONNECT_CMD = 3, | ||
907 | WMI_DIS_REASON_BSS_DISCONNECTED = 4, | ||
908 | WMI_DIS_REASON_AUTH_FAILED = 5, | ||
909 | WMI_DIS_REASON_ASSOC_FAILED = 6, | ||
910 | WMI_DIS_REASON_NO_RESOURCES_AVAIL = 7, | ||
911 | WMI_DIS_REASON_CSERV_DISCONNECT = 8, | ||
912 | WMI_DIS_REASON_INVALID_PROFILE = 10, | ||
913 | WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH = 11, | ||
914 | WMI_DIS_REASON_PROFILE_MISMATCH = 12, | ||
915 | WMI_DIS_REASON_CONNECTION_EVICTED = 13, | ||
916 | WMI_DIS_REASON_IBSS_MERGE = 14, | ||
917 | }; | ||
918 | |||
919 | struct wmi_disconnect_event { | ||
920 | __le16 protocol_reason_status; /* reason code, see 802.11 spec. */ | ||
921 | u8 bssid[WMI_MAC_LEN]; /* set if known */ | ||
922 | u8 disconnect_reason; /* see wmi_disconnect_reason_e */ | ||
923 | u8 assoc_resp_len; | ||
924 | u8 assoc_info[0]; | ||
925 | } __packed; | ||
926 | |||
927 | /* | ||
928 | * WMI_SCAN_COMPLETE_EVENTID | ||
929 | */ | ||
930 | struct wmi_scan_complete_event { | ||
931 | __le32 status; | ||
932 | } __packed; | ||
933 | |||
934 | /* | ||
935 | * WMI_BA_STATUS_EVENTID | ||
936 | */ | ||
937 | enum wmi_vring_ba_status { | ||
938 | WMI_BA_AGREED = 0, | ||
939 | WMI_BA_NON_AGREED = 1, | ||
940 | }; | ||
941 | |||
942 | struct wmi_vring_ba_status_event { | ||
943 | __le16 status; | ||
944 | u8 reserved[2]; | ||
945 | u8 ringid; | ||
946 | u8 agg_wsize; | ||
947 | __le16 ba_timeout; | ||
948 | } __packed; | ||
949 | |||
950 | /* | ||
951 | * WMI_DELBA_EVENTID | ||
952 | */ | ||
953 | struct wmi_delba_event { | ||
954 | |||
955 | #define CIDXTID_CID_POS (0) | ||
956 | #define CIDXTID_CID_LEN (4) | ||
957 | #define CIDXTID_CID_MSK (0xF) | ||
958 | #define CIDXTID_TID_POS (4) | ||
959 | #define CIDXTID_TID_LEN (4) | ||
960 | #define CIDXTID_TID_MSK (0xF0) | ||
961 | u8 cidxtid; | ||
962 | |||
963 | u8 from_initiator; | ||
964 | __le16 reason; | ||
965 | } __packed; | ||
966 | |||
967 | /* | ||
968 | * WMI_VRING_CFG_DONE_EVENTID | ||
969 | */ | ||
970 | enum wmi_vring_cfg_done_event_status { | ||
971 | WMI_VRING_CFG_SUCCESS = 0, | ||
972 | WMI_VRING_CFG_FAILURE = 1, | ||
973 | }; | ||
974 | |||
975 | struct wmi_vring_cfg_done_event { | ||
976 | u8 ringid; | ||
977 | u8 status; | ||
978 | u8 reserved[2]; | ||
979 | __le32 tx_vring_tail_ptr; | ||
980 | } __packed; | ||
981 | |||
982 | /* | ||
983 | * WMI_ADDBA_RESP_SENT_EVENTID | ||
984 | */ | ||
985 | enum wmi_rcp_addba_resp_sent_event_status { | ||
986 | WMI_ADDBA_SUCCESS = 0, | ||
987 | WMI_ADDBA_FAIL = 1, | ||
988 | }; | ||
989 | |||
990 | struct wmi_rcp_addba_resp_sent_event { | ||
991 | |||
992 | #define CIDXTID_CID_POS (0) | ||
993 | #define CIDXTID_CID_LEN (4) | ||
994 | #define CIDXTID_CID_MSK (0xF) | ||
995 | #define CIDXTID_TID_POS (4) | ||
996 | #define CIDXTID_TID_LEN (4) | ||
997 | #define CIDXTID_TID_MSK (0xF0) | ||
998 | u8 cidxtid; | ||
999 | |||
1000 | u8 reserved; | ||
1001 | __le16 status; | ||
1002 | } __packed; | ||
1003 | |||
1004 | /* | ||
1005 | * WMI_RCP_ADDBA_REQ_EVENTID | ||
1006 | */ | ||
1007 | struct wmi_rcp_addba_req_event { | ||
1008 | |||
1009 | #define CIDXTID_CID_POS (0) | ||
1010 | #define CIDXTID_CID_LEN (4) | ||
1011 | #define CIDXTID_CID_MSK (0xF) | ||
1012 | #define CIDXTID_TID_POS (4) | ||
1013 | #define CIDXTID_TID_LEN (4) | ||
1014 | #define CIDXTID_TID_MSK (0xF0) | ||
1015 | u8 cidxtid; | ||
1016 | |||
1017 | u8 dialog_token; | ||
1018 | __le16 ba_param_set; /* ieee80211_ba_parameterset as it received */ | ||
1019 | __le16 ba_timeout; | ||
1020 | __le16 ba_seq_ctrl; /* ieee80211_ba_seqstrl field as it received */ | ||
1021 | } __packed; | ||
1022 | |||
1023 | /* | ||
1024 | * WMI_CFG_RX_CHAIN_DONE_EVENTID | ||
1025 | */ | ||
1026 | enum wmi_cfg_rx_chain_done_event_status { | ||
1027 | WMI_CFG_RX_CHAIN_SUCCESS = 1, | ||
1028 | }; | ||
1029 | |||
1030 | struct wmi_cfg_rx_chain_done_event { | ||
1031 | __le32 rx_ring_tail_ptr; /* Rx V-Ring Tail pointer */ | ||
1032 | __le32 status; | ||
1033 | } __packed; | ||
1034 | |||
1035 | /* | ||
1036 | * WMI_WBE_LINKDOWN_EVENTID | ||
1037 | */ | ||
1038 | enum wmi_wbe_link_down_event_reason { | ||
1039 | WMI_WBE_REASON_USER_REQUEST = 0, | ||
1040 | WMI_WBE_REASON_RX_DISASSOC = 1, | ||
1041 | WMI_WBE_REASON_BAD_PHY_LINK = 2, | ||
1042 | }; | ||
1043 | |||
1044 | struct wmi_wbe_link_down_event { | ||
1045 | u8 cid; | ||
1046 | u8 reserved[3]; | ||
1047 | __le32 reason; | ||
1048 | } __packed; | ||
1049 | |||
1050 | /* | ||
1051 | * WMI_DATA_PORT_OPEN_EVENTID | ||
1052 | */ | ||
1053 | struct wmi_data_port_open_event { | ||
1054 | u8 cid; | ||
1055 | u8 reserved[3]; | ||
1056 | } __packed; | ||
1057 | |||
1058 | /* | ||
1059 | * WMI_GET_PCP_CHANNEL_EVENTID | ||
1060 | */ | ||
1061 | struct wmi_get_pcp_channel_event { | ||
1062 | u8 channel; | ||
1063 | u8 reserved[3]; | ||
1064 | } __packed; | ||
1065 | |||
1066 | /* | ||
1067 | * WMI_SW_TX_COMPLETE_EVENTID | ||
1068 | */ | ||
1069 | enum wmi_sw_tx_status { | ||
1070 | WMI_TX_SW_STATUS_SUCCESS = 0, | ||
1071 | WMI_TX_SW_STATUS_FAILED_NO_RESOURCES = 1, | ||
1072 | WMI_TX_SW_STATUS_FAILED_TX = 2, | ||
1073 | }; | ||
1074 | |||
1075 | struct wmi_sw_tx_complete_event { | ||
1076 | u8 status; /* enum wmi_sw_tx_status */ | ||
1077 | u8 reserved[3]; | ||
1078 | } __packed; | ||
1079 | |||
1080 | /* | ||
1081 | * WMI_GET_SSID_EVENTID | ||
1082 | */ | ||
1083 | struct wmi_get_ssid_event { | ||
1084 | __le32 ssid_len; | ||
1085 | u8 ssid[WMI_MAX_SSID_LEN]; | ||
1086 | } __packed; | ||
1087 | |||
1088 | /* | ||
1089 | * WMI_RX_MGMT_PACKET_EVENTID | ||
1090 | */ | ||
1091 | struct wmi_rx_mgmt_info { | ||
1092 | u8 mcs; | ||
1093 | s8 snr; | ||
1094 | __le16 range; | ||
1095 | __le16 stype; | ||
1096 | __le16 status; | ||
1097 | __le32 len; | ||
1098 | u8 qid; | ||
1099 | u8 mid; | ||
1100 | u8 cid; | ||
1101 | u8 channel; /* From Radio MNGR */ | ||
1102 | } __packed; | ||
1103 | |||
1104 | struct wmi_rx_mgmt_packet_event { | ||
1105 | struct wmi_rx_mgmt_info info; | ||
1106 | u8 payload[0]; | ||
1107 | } __packed; | ||
1108 | |||
1109 | /* | ||
1110 | * WMI_ECHO_RSP_EVENTID | ||
1111 | */ | ||
1112 | struct wmi_echo_event { | ||
1113 | __le32 echoed_value; | ||
1114 | } __packed; | ||
1115 | |||
1116 | #endif /* __WILOCITY_WMI_H__ */ | ||