brcmfmac: streamline header parse code of sdio glom read

Use brcmf_sdio_hdparser to handle header of super frame and sub
frame in glomming frame read.

Signed-off-by: Franky Lin <frankyl@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Franky Lin 2012-10-22 10:36:24 -07:00 committed by John W. Linville
parent ac24be6fe6
commit 9d7d6f95bd
1 changed files with 62 additions and 112 deletions

View File

@ -614,6 +614,12 @@ static const uint max_roundup = 512;
#define ALIGNMENT 4
enum brcmf_sdio_frmtype {
BRCMF_SDIO_FT_NORMAL,
BRCMF_SDIO_FT_SUPER,
BRCMF_SDIO_FT_SUB,
};
static void pkt_align(struct sk_buff *p, int len, int align)
{
uint datalign;
@ -1032,7 +1038,8 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)
}
static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
struct brcmf_sdio_read *rd)
struct brcmf_sdio_read *rd,
enum brcmf_sdio_frmtype type)
{
u16 len, checksum;
u8 rx_seq, fc, tx_seq_max;
@ -1059,6 +1066,15 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
brcmf_dbg(ERROR, "HW header length error\n");
return false;
}
if (type == BRCMF_SDIO_FT_SUPER &&
(roundup(len, bus->blocksize) != rd->len)) {
brcmf_dbg(ERROR, "HW superframe header length error\n");
return false;
}
if (type == BRCMF_SDIO_FT_SUB && len > rd->len) {
brcmf_dbg(ERROR, "HW subframe header length error\n");
return false;
}
rd->len = len;
/*
@ -1071,9 +1087,16 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
* Byte 5: Maximum Sequence number allow for Tx
* Byte 6~7: Reserved
*/
if (type == BRCMF_SDIO_FT_SUPER &&
SDPCM_GLOMDESC(&header[SDPCM_FRAMETAG_LEN])) {
brcmf_dbg(ERROR, "Glom descriptor found in superframe head\n");
rd->len = 0;
return false;
}
rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]);
rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]);
if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL) {
if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL &&
type != BRCMF_SDIO_FT_SUPER) {
brcmf_dbg(ERROR, "HW header length too long\n");
bus->sdiodev->bus_if->dstats.rx_errors++;
bus->sdcnt.rx_toolong++;
@ -1081,6 +1104,17 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
rd->len = 0;
return false;
}
if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) {
brcmf_dbg(ERROR, "Wrong channel for superframe\n");
rd->len = 0;
return false;
}
if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL &&
rd->channel != SDPCM_EVENT_CHANNEL) {
brcmf_dbg(ERROR, "Wrong channel for subframe\n");
rd->len = 0;
return false;
}
rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]);
if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {
brcmf_dbg(ERROR, "seq %d: bad data offset\n", rx_seq);
@ -1095,6 +1129,9 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header,
bus->sdcnt.rx_badseq++;
rd->seq_num = rx_seq;
}
/* no need to check the reset for subframe */
if (type == BRCMF_SDIO_FT_SUB)
return true;
rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) {
/* only warm for NON glom packet */
@ -1126,16 +1163,16 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
u16 dlen, totlen;
u8 *dptr, num = 0;
u16 sublen, check;
u16 sublen;
struct sk_buff *pfirst, *pnext;
int errcode;
u8 chan, seq, doff, sfdoff;
u8 txmax;
u8 doff, sfdoff;
int ifidx = 0;
bool usechain = bus->use_rxchain;
u16 next_len;
struct brcmf_sdio_read rd_new;
/* If packets, issue read(s) and send up packet chain */
/* Return sequence numbers consumed? */
@ -1279,68 +1316,15 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
pfirst->data, min_t(int, pfirst->len, 48),
"SUPERFRAME:\n");
/* Validate the superframe header */
dptr = (u8 *) (pfirst->data);
sublen = get_unaligned_le16(dptr);
check = get_unaligned_le16(dptr + sizeof(u16));
chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
next_len = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
if ((next_len << 4) > MAX_RX_DATASZ) {
brcmf_dbg(INFO, "nextlen too large (%d) seq %d\n",
next_len, seq);
next_len = 0;
}
bus->cur_read.len = next_len << 4;
doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
errcode = 0;
if ((u16)~(sublen ^ check)) {
brcmf_dbg(ERROR, "(superframe): HW hdr error: len/check 0x%04x/0x%04x\n",
sublen, check);
errcode = -1;
} else if (roundup(sublen, bus->blocksize) != dlen) {
brcmf_dbg(ERROR, "(superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n",
sublen, roundup(sublen, bus->blocksize),
dlen);
errcode = -1;
} else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) !=
SDPCM_GLOM_CHANNEL) {
brcmf_dbg(ERROR, "(superframe): bad channel %d\n",
SDPCM_PACKET_CHANNEL(
&dptr[SDPCM_FRAMETAG_LEN]));
errcode = -1;
} else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
brcmf_dbg(ERROR, "(superframe): got 2nd descriptor?\n");
errcode = -1;
} else if ((doff < SDPCM_HDRLEN) ||
(doff > (pfirst->len - SDPCM_HDRLEN))) {
brcmf_dbg(ERROR, "(superframe): Bad data offset %d: HW %d pkt %d min %d\n",
doff, sublen, pfirst->len, SDPCM_HDRLEN);
errcode = -1;
}
/* Check sequence number of superframe SW header */
if (rxseq != seq) {
brcmf_dbg(INFO, "(superframe) rx_seq %d, expected %d\n",
seq, rxseq);
bus->sdcnt.rx_badseq++;
rxseq = seq;
}
/* Check window for sanity */
if ((u8) (txmax - bus->tx_seq) > 0x40) {
brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n",
txmax, bus->tx_seq);
txmax = bus->tx_seq + 2;
}
bus->tx_max = txmax;
rd_new.seq_num = rxseq;
rd_new.len = dlen;
errcode = -!brcmf_sdio_hdparser(bus, pfirst->data, &rd_new,
BRCMF_SDIO_FT_SUPER);
bus->cur_read.len = rd_new.len_nxtfrm << 4;
/* Remove superframe header, remember offset */
skb_pull(pfirst, doff);
sfdoff = doff;
skb_pull(pfirst, rd_new.dat_offset);
sfdoff = rd_new.dat_offset;
num = 0;
/* Validate all the subframe headers */
@ -1349,34 +1333,14 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
if (errcode)
break;
dptr = (u8 *) (pnext->data);
dlen = (u16) (pnext->len);
sublen = get_unaligned_le16(dptr);
check = get_unaligned_le16(dptr + sizeof(u16));
chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
rd_new.len = pnext->len;
rd_new.seq_num = rxseq++;
errcode = -!brcmf_sdio_hdparser(bus, pnext->data,
&rd_new,
BRCMF_SDIO_FT_SUB);
brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
dptr, 32, "subframe:\n");
pnext->data, 32, "subframe:\n");
if ((u16)~(sublen ^ check)) {
brcmf_dbg(ERROR, "(subframe %d): HW hdr error: len/check 0x%04x/0x%04x\n",
num, sublen, check);
errcode = -1;
} else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
brcmf_dbg(ERROR, "(subframe %d): length mismatch: len 0x%04x, expect 0x%04x\n",
num, sublen, dlen);
errcode = -1;
} else if ((chan != SDPCM_DATA_CHANNEL) &&
(chan != SDPCM_EVENT_CHANNEL)) {
brcmf_dbg(ERROR, "(subframe %d): bad channel %d\n",
num, chan);
errcode = -1;
} else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
brcmf_dbg(ERROR, "(subframe %d): Bad data offset %d: HW %d min %d\n",
num, doff, sublen, SDPCM_HDRLEN);
errcode = -1;
}
/* increase the subframe count */
num++;
}
@ -1402,27 +1366,11 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
dptr = (u8 *) (pfirst->data);
sublen = get_unaligned_le16(dptr);
chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
brcmf_dbg(GLOM, "Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n",
num, pfirst, pfirst->data,
pfirst->len, sublen, chan, seq);
/* precondition: chan == SDPCM_DATA_CHANNEL ||
chan == SDPCM_EVENT_CHANNEL */
if (rxseq != seq) {
brcmf_dbg(GLOM, "rx_seq %d, expected %d\n",
seq, rxseq);
bus->sdcnt.rx_badseq++;
rxseq = seq;
}
rxseq++;
brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
dptr, dlen, "Rx Subframe Data:\n");
dptr, pfirst->len,
"Rx Subframe Data:\n");
__skb_trim(pfirst, sublen);
skb_pull(pfirst, doff);
@ -1642,7 +1590,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
bus->rxhdr, SDPCM_HDRLEN,
"RxHdr:\n");
if (!brcmf_sdio_hdparser(bus, bus->rxhdr, rd)) {
if (!brcmf_sdio_hdparser(bus, bus->rxhdr, rd,
BRCMF_SDIO_FT_NORMAL)) {
if (!bus->rxpending)
break;
else
@ -1701,7 +1650,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)
} else {
memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN);
rd_new.seq_num = rd->seq_num;
if (!brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new)) {
if (!brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new,
BRCMF_SDIO_FT_NORMAL)) {
rd->len = 0;
brcmu_pkt_buf_free_skb(pkt);
}