rx.c 8.5 KB
Newer Older
L
Luciano Coelho 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * This file is part of wl1271
 *
 * Copyright (C) 2009 Nokia Corporation
 *
 * Contact: Luciano Coelho <luciano.coelho@nokia.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

24
#include <linux/gfp.h>
25
#include <linux/sched.h>
26

27
#include "wlcore.h"
28
#include "debug.h"
S
Shahar Levi 已提交
29 30
#include "acx.h"
#include "rx.h"
E
Eliad Peller 已提交
31
#include "tx.h"
S
Shahar Levi 已提交
32
#include "io.h"
33
#include "hw_ops.h"
L
Luciano Coelho 已提交
34

35 36 37 38 39 40
/*
 * TODO: this is here just for now, it must be removed when the data
 * operations are in place.
 */
#include "../wl12xx/reg.h"

41 42
static u32 wlcore_rx_get_buf_size(struct wl1271 *wl,
				  u32 rx_pkt_desc)
L
Luciano Coelho 已提交
43
{
44 45 46 47 48 49 50 51 52 53 54 55 56
	if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN)
		return (rx_pkt_desc & ALIGNED_RX_BUF_SIZE_MASK) >>
		       ALIGNED_RX_BUF_SIZE_SHIFT;

	return (rx_pkt_desc & RX_BUF_SIZE_MASK) >> RX_BUF_SIZE_SHIFT_DIV;
}

static u32 wlcore_rx_get_align_buf_size(struct wl1271 *wl, u32 pkt_len)
{
	if (wl->quirks & WLCORE_QUIRK_RX_BLOCKSIZE_ALIGN)
		return ALIGN(pkt_len, WL12XX_BUS_BLOCK_SIZE);

	return pkt_len;
L
Luciano Coelho 已提交
57 58 59 60 61 62 63 64 65
}

static void wl1271_rx_status(struct wl1271 *wl,
			     struct wl1271_rx_descriptor *desc,
			     struct ieee80211_rx_status *status,
			     u8 beacon)
{
	memset(status, 0, sizeof(struct ieee80211_rx_status));

66
	if ((desc->flags & WL1271_RX_DESC_BAND_MASK) == WL1271_RX_DESC_BAND_BG)
67
		status->band = IEEE80211_BAND_2GHZ;
68
	else
69
		status->band = IEEE80211_BAND_5GHZ;
70

71
	status->rate_idx = wlcore_rate_to_idx(wl, desc->rate, status->band);
72

73
	/* 11n support */
74
	if (desc->rate <= wl->hw_min_ht_rate)
75 76
		status->flag |= RX_FLAG_HT;

L
Luciano Coelho 已提交
77 78
	status->signal = desc->rssi;

79 80 81 82 83 84 85
	/*
	 * FIXME: In wl1251, the SNR should be divided by two.  In wl1271 we
	 * need to divide by two for now, but TI has been discussing about
	 * changing it.  This needs to be rechecked.
	 */
	wl->noise = desc->rssi - (desc->snr >> 1);

86 87
	status->freq = ieee80211_channel_to_frequency(desc->channel,
						      status->band);
L
Luciano Coelho 已提交
88 89

	if (desc->flags & WL1271_RX_DESC_ENCRYPT_MASK) {
90
		u8 desc_err_code = desc->status & WL1271_RX_DESC_STATUS_MASK;
L
Luciano Coelho 已提交
91

92 93 94 95
		status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED |
				RX_FLAG_DECRYPTED;

		if (unlikely(desc_err_code == WL1271_RX_DESC_MIC_FAIL)) {
96
			status->flag |= RX_FLAG_MMIC_ERROR;
97 98
			wl1271_warning("Michael MIC error");
		}
L
Luciano Coelho 已提交
99 100 101
	}
}

102
static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length,
103
				 enum wl_rx_buf_align rx_align, u8 *hlid)
L
Luciano Coelho 已提交
104 105 106
{
	struct wl1271_rx_descriptor *desc;
	struct sk_buff *skb;
107
	struct ieee80211_hdr *hdr;
L
Luciano Coelho 已提交
108 109
	u8 *buf;
	u8 beacon = 0;
110
	u8 is_data = 0;
111
	u8 reserved = 0;
112
	u16 seq_num;
113
	u32 pkt_data_len;
L
Luciano Coelho 已提交
114

115 116 117 118
	/*
	 * In PLT mode we seem to get frames and mac80211 warns about them,
	 * workaround this by not retrieving them at all.
	 */
119
	if (unlikely(wl->plt))
120
		return -EINVAL;
121

122 123 124 125 126 127 128
	pkt_data_len = wlcore_hw_get_rx_packet_len(wl, data, length);
	if (!pkt_data_len) {
		wl1271_error("Invalid packet arrived from HW. length %d",
			     length);
		return -EINVAL;
	}

129 130 131
	if (rx_align == WLCORE_RX_BUF_UNALIGNED)
		reserved = NET_IP_ALIGN;

132 133 134
	/* the data read starts with the descriptor */
	desc = (struct wl1271_rx_descriptor *) data;

135 136 137 138 139 140 141
	if (desc->packet_class == WL12XX_RX_CLASS_LOGGER) {
		size_t len = length - sizeof(*desc);
		wl12xx_copy_fwlog(wl, data + sizeof(*desc), len);
		wake_up_interruptible(&wl->fwlog_waitq);
		return 0;
	}

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
	switch (desc->status & WL1271_RX_DESC_STATUS_MASK) {
	/* discard corrupted packets */
	case WL1271_RX_DESC_DRIVER_RX_Q_FAIL:
	case WL1271_RX_DESC_DECRYPT_FAIL:
		wl1271_warning("corrupted packet in RX with status: 0x%x",
			       desc->status & WL1271_RX_DESC_STATUS_MASK);
		return -EINVAL;
	case WL1271_RX_DESC_SUCCESS:
	case WL1271_RX_DESC_MIC_FAIL:
		break;
	default:
		wl1271_error("invalid RX descriptor status: 0x%x",
			     desc->status & WL1271_RX_DESC_STATUS_MASK);
		return -EINVAL;
	}

158 159
	/* skb length not including rx descriptor */
	skb = __dev_alloc_skb(pkt_data_len + reserved, GFP_KERNEL);
L
Luciano Coelho 已提交
160 161
	if (!skb) {
		wl1271_error("Couldn't allocate RX frame");
162
		return -ENOMEM;
L
Luciano Coelho 已提交
163 164
	}

165 166 167
	/* reserve the unaligned payload(if any) */
	skb_reserve(skb, reserved);

168
	buf = skb_put(skb, pkt_data_len);
L
Luciano Coelho 已提交
169

170 171 172 173 174 175
	/*
	 * Copy packets from aggregation buffer to the skbs without rx
	 * descriptor and with packet payload aligned care. In case of unaligned
	 * packets copy the packets in offset of 2 bytes guarantee IP header
	 * payload aligned to 4 bytes.
	 */
176
	memcpy(buf, data + sizeof(*desc), pkt_data_len);
177 178 179
	if (rx_align == WLCORE_RX_BUF_PADDED)
		skb_pull(skb, NET_IP_ALIGN);

E
Eliad Peller 已提交
180
	*hlid = desc->hlid;
L
Luciano Coelho 已提交
181

182 183
	hdr = (struct ieee80211_hdr *)skb->data;
	if (ieee80211_is_beacon(hdr->frame_control))
L
Luciano Coelho 已提交
184
		beacon = 1;
185 186
	if (ieee80211_is_data_present(hdr->frame_control))
		is_data = 1;
L
Luciano Coelho 已提交
187

188
	wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon);
189
	wlcore_hw_set_rx_csum(wl, desc, skb);
L
Luciano Coelho 已提交
190

191
	seq_num = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4;
E
Eliad Peller 已提交
192
	wl1271_debug(DEBUG_RX, "rx skb 0x%p: %d B %s seq %d hlid %d", skb,
193
		     skb->len - desc->pad_len,
194
		     beacon ? "beacon" : "",
E
Eliad Peller 已提交
195
		     seq_num, *hlid);
L
Luciano Coelho 已提交
196

197
	skb_queue_tail(&wl->deferred_rx_queue, skb);
198
	queue_work(wl->freezable_wq, &wl->netstack_work);
199

200
	return is_data;
L
Luciano Coelho 已提交
201 202
}

203
void wl12xx_rx(struct wl1271 *wl, struct wl_fw_status_1 *status)
L
Luciano Coelho 已提交
204
{
E
Eliad Peller 已提交
205
	unsigned long active_hlids[BITS_TO_LONGS(WL12XX_MAX_LINKS)] = {0};
L
Luciano Coelho 已提交
206
	u32 buf_size;
207 208
	u32 fw_rx_counter = status->fw_rx_counter % wl->num_rx_desc;
	u32 drv_rx_counter = wl->rx_counter % wl->num_rx_desc;
209
	u32 rx_counter;
210 211
	u32 pkt_len, align_pkt_len;
	u32 pkt_offset, des;
E
Eliad Peller 已提交
212
	u8 hlid;
213
	enum wl_rx_buf_align rx_align;
L
Luciano Coelho 已提交
214 215

	while (drv_rx_counter != fw_rx_counter) {
216 217 218
		buf_size = 0;
		rx_counter = drv_rx_counter;
		while (rx_counter != fw_rx_counter) {
219 220 221 222 223
			des = le32_to_cpu(status->rx_pkt_descs[rx_counter]);
			pkt_len = wlcore_rx_get_buf_size(wl, des);
			align_pkt_len = wlcore_rx_get_align_buf_size(wl,
								     pkt_len);
			if (buf_size + align_pkt_len > WL1271_AGGR_BUFFER_SIZE)
224
				break;
225
			buf_size += align_pkt_len;
226
			rx_counter++;
227
			rx_counter %= wl->num_rx_desc;
228
		}
L
Luciano Coelho 已提交
229 230 231 232 233 234

		if (buf_size == 0) {
			wl1271_warning("received empty data");
			break;
		}

235
		/* Read all available packets at once */
236 237
		des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]);
		wlcore_hw_prepare_read(wl, des, buf_size);
238 239
		wlcore_read_data(wl, REG_SLV_MEM_DATA, wl->aggr_buf,
				 buf_size, true);
240 241 242 243

		/* Split data into separate packets */
		pkt_offset = 0;
		while (pkt_offset < buf_size) {
244 245
			des = le32_to_cpu(status->rx_pkt_descs[drv_rx_counter]);
			pkt_len = wlcore_rx_get_buf_size(wl, des);
246
			rx_align = wlcore_hw_get_rx_buf_align(wl, des);
247

J
Juuso Oikarinen 已提交
248 249 250 251 252
			/*
			 * the handle data call can only fail in memory-outage
			 * conditions, in that case the received frame will just
			 * be dropped.
			 */
253 254
			if (wl1271_rx_handle_data(wl,
						  wl->aggr_buf + pkt_offset,
255
						  pkt_len, rx_align,
E
Eliad Peller 已提交
256
						  &hlid) == 1) {
257 258 259 260 261 262
				if (hlid < WL12XX_MAX_LINKS)
					__set_bit(hlid, active_hlids);
				else
					WARN(1,
					     "hlid exceeded WL12XX_MAX_LINKS "
					     "(%d)\n", hlid);
E
Eliad Peller 已提交
263
			}
264

265 266
			wl->rx_counter++;
			drv_rx_counter++;
267
			drv_rx_counter %= wl->num_rx_desc;
268
			pkt_offset += wlcore_rx_get_align_buf_size(wl, pkt_len);
269
		}
L
Luciano Coelho 已提交
270
	}
271 272 273 274 275

	/*
	 * Write the driver's packet counter to the FW. This is only required
	 * for older hardware revisions
	 */
276
	if (wl->quirks & WLCORE_QUIRK_END_OF_TRANSACTION)
277 278
		wl1271_write32(wl, WL12XX_REG_RX_DRIVER_COUNTER,
			       wl->rx_counter);
279

E
Eliad Peller 已提交
280
	wl12xx_rearm_rx_streaming(wl, active_hlids);
L
Luciano Coelho 已提交
281
}
282

283
#ifdef CONFIG_PM
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
int wl1271_rx_filter_enable(struct wl1271 *wl,
			    int index, bool enable,
			    struct wl12xx_rx_filter *filter)
{
	int ret;

	if (wl->rx_filter_enabled[index] == enable) {
		wl1271_warning("Request to enable an already "
			     "enabled rx filter %d", index);
		return 0;
	}

	ret = wl1271_acx_set_rx_filter(wl, index, enable, filter);

	if (ret) {
		wl1271_error("Failed to %s rx data filter %d (err=%d)",
			     enable ? "enable" : "disable", index, ret);
		return ret;
	}

	wl->rx_filter_enabled[index] = enable;

	return 0;
}

void wl1271_rx_filter_clear_all(struct wl1271 *wl)
{
	int i;

	for (i = 0; i < WL1271_MAX_RX_FILTERS; i++) {
		if (!wl->rx_filter_enabled[i])
			continue;
		wl1271_rx_filter_enable(wl, i, 0, NULL);
	}
}
319
#endif /* CONFIG_PM */