diff options
author | Franky Lin <frankyl@broadcom.com> | 2012-10-22 13:36:24 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-10-29 15:28:28 -0400 |
commit | 9d7d6f95bda1fdc10847996ab849b5dd065bf047 (patch) | |
tree | 6e77d582bc15289e2b1b26323771daacab0941a5 /drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | |
parent | ac24be6fe6593564be99a88e41cddc6c0fc1899f (diff) |
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>
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c')
-rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 174 |
1 files changed, 62 insertions, 112 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 3564686add9a..415f2be36375 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | |||
@@ -614,6 +614,12 @@ static const uint max_roundup = 512; | |||
614 | 614 | ||
615 | #define ALIGNMENT 4 | 615 | #define ALIGNMENT 4 |
616 | 616 | ||
617 | enum brcmf_sdio_frmtype { | ||
618 | BRCMF_SDIO_FT_NORMAL, | ||
619 | BRCMF_SDIO_FT_SUPER, | ||
620 | BRCMF_SDIO_FT_SUB, | ||
621 | }; | ||
622 | |||
617 | static void pkt_align(struct sk_buff *p, int len, int align) | 623 | static void pkt_align(struct sk_buff *p, int len, int align) |
618 | { | 624 | { |
619 | uint datalign; | 625 | uint datalign; |
@@ -1032,7 +1038,8 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) | |||
1032 | } | 1038 | } |
1033 | 1039 | ||
1034 | static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, | 1040 | static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, |
1035 | struct brcmf_sdio_read *rd) | 1041 | struct brcmf_sdio_read *rd, |
1042 | enum brcmf_sdio_frmtype type) | ||
1036 | { | 1043 | { |
1037 | u16 len, checksum; | 1044 | u16 len, checksum; |
1038 | u8 rx_seq, fc, tx_seq_max; | 1045 | u8 rx_seq, fc, tx_seq_max; |
@@ -1059,6 +1066,15 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, | |||
1059 | brcmf_dbg(ERROR, "HW header length error\n"); | 1066 | brcmf_dbg(ERROR, "HW header length error\n"); |
1060 | return false; | 1067 | return false; |
1061 | } | 1068 | } |
1069 | if (type == BRCMF_SDIO_FT_SUPER && | ||
1070 | (roundup(len, bus->blocksize) != rd->len)) { | ||
1071 | brcmf_dbg(ERROR, "HW superframe header length error\n"); | ||
1072 | return false; | ||
1073 | } | ||
1074 | if (type == BRCMF_SDIO_FT_SUB && len > rd->len) { | ||
1075 | brcmf_dbg(ERROR, "HW subframe header length error\n"); | ||
1076 | return false; | ||
1077 | } | ||
1062 | rd->len = len; | 1078 | rd->len = len; |
1063 | 1079 | ||
1064 | /* | 1080 | /* |
@@ -1071,9 +1087,16 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, | |||
1071 | * Byte 5: Maximum Sequence number allow for Tx | 1087 | * Byte 5: Maximum Sequence number allow for Tx |
1072 | * Byte 6~7: Reserved | 1088 | * Byte 6~7: Reserved |
1073 | */ | 1089 | */ |
1090 | if (type == BRCMF_SDIO_FT_SUPER && | ||
1091 | SDPCM_GLOMDESC(&header[SDPCM_FRAMETAG_LEN])) { | ||
1092 | brcmf_dbg(ERROR, "Glom descriptor found in superframe head\n"); | ||
1093 | rd->len = 0; | ||
1094 | return false; | ||
1095 | } | ||
1074 | rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]); | 1096 | rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]); |
1075 | rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]); | 1097 | rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]); |
1076 | if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL) { | 1098 | if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL && |
1099 | type != BRCMF_SDIO_FT_SUPER) { | ||
1077 | brcmf_dbg(ERROR, "HW header length too long\n"); | 1100 | brcmf_dbg(ERROR, "HW header length too long\n"); |
1078 | bus->sdiodev->bus_if->dstats.rx_errors++; | 1101 | bus->sdiodev->bus_if->dstats.rx_errors++; |
1079 | bus->sdcnt.rx_toolong++; | 1102 | bus->sdcnt.rx_toolong++; |
@@ -1081,6 +1104,17 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, | |||
1081 | rd->len = 0; | 1104 | rd->len = 0; |
1082 | return false; | 1105 | return false; |
1083 | } | 1106 | } |
1107 | if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) { | ||
1108 | brcmf_dbg(ERROR, "Wrong channel for superframe\n"); | ||
1109 | rd->len = 0; | ||
1110 | return false; | ||
1111 | } | ||
1112 | if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL && | ||
1113 | rd->channel != SDPCM_EVENT_CHANNEL) { | ||
1114 | brcmf_dbg(ERROR, "Wrong channel for subframe\n"); | ||
1115 | rd->len = 0; | ||
1116 | return false; | ||
1117 | } | ||
1084 | rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]); | 1118 | rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]); |
1085 | if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { | 1119 | if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { |
1086 | brcmf_dbg(ERROR, "seq %d: bad data offset\n", rx_seq); | 1120 | 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, | |||
1095 | bus->sdcnt.rx_badseq++; | 1129 | bus->sdcnt.rx_badseq++; |
1096 | rd->seq_num = rx_seq; | 1130 | rd->seq_num = rx_seq; |
1097 | } | 1131 | } |
1132 | /* no need to check the reset for subframe */ | ||
1133 | if (type == BRCMF_SDIO_FT_SUB) | ||
1134 | return true; | ||
1098 | rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; | 1135 | rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; |
1099 | if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { | 1136 | if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { |
1100 | /* only warm for NON glom packet */ | 1137 | /* only warm for NON glom packet */ |
@@ -1126,16 +1163,16 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) | |||
1126 | u16 dlen, totlen; | 1163 | u16 dlen, totlen; |
1127 | u8 *dptr, num = 0; | 1164 | u8 *dptr, num = 0; |
1128 | 1165 | ||
1129 | u16 sublen, check; | 1166 | u16 sublen; |
1130 | struct sk_buff *pfirst, *pnext; | 1167 | struct sk_buff *pfirst, *pnext; |
1131 | 1168 | ||
1132 | int errcode; | 1169 | int errcode; |
1133 | u8 chan, seq, doff, sfdoff; | 1170 | u8 doff, sfdoff; |
1134 | u8 txmax; | ||
1135 | 1171 | ||
1136 | int ifidx = 0; | 1172 | int ifidx = 0; |
1137 | bool usechain = bus->use_rxchain; | 1173 | bool usechain = bus->use_rxchain; |
1138 | u16 next_len; | 1174 | |
1175 | struct brcmf_sdio_read rd_new; | ||
1139 | 1176 | ||
1140 | /* If packets, issue read(s) and send up packet chain */ | 1177 | /* If packets, issue read(s) and send up packet chain */ |
1141 | /* Return sequence numbers consumed? */ | 1178 | /* Return sequence numbers consumed? */ |
@@ -1279,68 +1316,15 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) | |||
1279 | pfirst->data, min_t(int, pfirst->len, 48), | 1316 | pfirst->data, min_t(int, pfirst->len, 48), |
1280 | "SUPERFRAME:\n"); | 1317 | "SUPERFRAME:\n"); |
1281 | 1318 | ||
1282 | /* Validate the superframe header */ | 1319 | rd_new.seq_num = rxseq; |
1283 | dptr = (u8 *) (pfirst->data); | 1320 | rd_new.len = dlen; |
1284 | sublen = get_unaligned_le16(dptr); | 1321 | errcode = -!brcmf_sdio_hdparser(bus, pfirst->data, &rd_new, |
1285 | check = get_unaligned_le16(dptr + sizeof(u16)); | 1322 | BRCMF_SDIO_FT_SUPER); |
1286 | 1323 | bus->cur_read.len = rd_new.len_nxtfrm << 4; | |
1287 | chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); | ||
1288 | seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); | ||
1289 | next_len = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; | ||
1290 | if ((next_len << 4) > MAX_RX_DATASZ) { | ||
1291 | brcmf_dbg(INFO, "nextlen too large (%d) seq %d\n", | ||
1292 | next_len, seq); | ||
1293 | next_len = 0; | ||
1294 | } | ||
1295 | bus->cur_read.len = next_len << 4; | ||
1296 | doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); | ||
1297 | txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); | ||
1298 | |||
1299 | errcode = 0; | ||
1300 | if ((u16)~(sublen ^ check)) { | ||
1301 | brcmf_dbg(ERROR, "(superframe): HW hdr error: len/check 0x%04x/0x%04x\n", | ||
1302 | sublen, check); | ||
1303 | errcode = -1; | ||
1304 | } else if (roundup(sublen, bus->blocksize) != dlen) { | ||
1305 | brcmf_dbg(ERROR, "(superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n", | ||
1306 | sublen, roundup(sublen, bus->blocksize), | ||
1307 | dlen); | ||
1308 | errcode = -1; | ||
1309 | } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) != | ||
1310 | SDPCM_GLOM_CHANNEL) { | ||
1311 | brcmf_dbg(ERROR, "(superframe): bad channel %d\n", | ||
1312 | SDPCM_PACKET_CHANNEL( | ||
1313 | &dptr[SDPCM_FRAMETAG_LEN])); | ||
1314 | errcode = -1; | ||
1315 | } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) { | ||
1316 | brcmf_dbg(ERROR, "(superframe): got 2nd descriptor?\n"); | ||
1317 | errcode = -1; | ||
1318 | } else if ((doff < SDPCM_HDRLEN) || | ||
1319 | (doff > (pfirst->len - SDPCM_HDRLEN))) { | ||
1320 | brcmf_dbg(ERROR, "(superframe): Bad data offset %d: HW %d pkt %d min %d\n", | ||
1321 | doff, sublen, pfirst->len, SDPCM_HDRLEN); | ||
1322 | errcode = -1; | ||
1323 | } | ||
1324 | |||
1325 | /* Check sequence number of superframe SW header */ | ||
1326 | if (rxseq != seq) { | ||
1327 | brcmf_dbg(INFO, "(superframe) rx_seq %d, expected %d\n", | ||
1328 | seq, rxseq); | ||
1329 | bus->sdcnt.rx_badseq++; | ||
1330 | rxseq = seq; | ||
1331 | } | ||
1332 | |||
1333 | /* Check window for sanity */ | ||
1334 | if ((u8) (txmax - bus->tx_seq) > 0x40) { | ||
1335 | brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n", | ||
1336 | txmax, bus->tx_seq); | ||
1337 | txmax = bus->tx_seq + 2; | ||
1338 | } | ||
1339 | bus->tx_max = txmax; | ||
1340 | 1324 | ||
1341 | /* Remove superframe header, remember offset */ | 1325 | /* Remove superframe header, remember offset */ |
1342 | skb_pull(pfirst, doff); | 1326 | skb_pull(pfirst, rd_new.dat_offset); |
1343 | sfdoff = doff; | 1327 | sfdoff = rd_new.dat_offset; |
1344 | num = 0; | 1328 | num = 0; |
1345 | 1329 | ||
1346 | /* Validate all the subframe headers */ | 1330 | /* Validate all the subframe headers */ |
@@ -1349,34 +1333,14 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) | |||
1349 | if (errcode) | 1333 | if (errcode) |
1350 | break; | 1334 | break; |
1351 | 1335 | ||
1352 | dptr = (u8 *) (pnext->data); | 1336 | rd_new.len = pnext->len; |
1353 | dlen = (u16) (pnext->len); | 1337 | rd_new.seq_num = rxseq++; |
1354 | sublen = get_unaligned_le16(dptr); | 1338 | errcode = -!brcmf_sdio_hdparser(bus, pnext->data, |
1355 | check = get_unaligned_le16(dptr + sizeof(u16)); | 1339 | &rd_new, |
1356 | chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); | 1340 | BRCMF_SDIO_FT_SUB); |
1357 | doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); | ||
1358 | brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), | 1341 | brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), |
1359 | dptr, 32, "subframe:\n"); | 1342 | pnext->data, 32, "subframe:\n"); |
1360 | 1343 | ||
1361 | if ((u16)~(sublen ^ check)) { | ||
1362 | brcmf_dbg(ERROR, "(subframe %d): HW hdr error: len/check 0x%04x/0x%04x\n", | ||
1363 | num, sublen, check); | ||
1364 | errcode = -1; | ||
1365 | } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) { | ||
1366 | brcmf_dbg(ERROR, "(subframe %d): length mismatch: len 0x%04x, expect 0x%04x\n", | ||
1367 | num, sublen, dlen); | ||
1368 | errcode = -1; | ||
1369 | } else if ((chan != SDPCM_DATA_CHANNEL) && | ||
1370 | (chan != SDPCM_EVENT_CHANNEL)) { | ||
1371 | brcmf_dbg(ERROR, "(subframe %d): bad channel %d\n", | ||
1372 | num, chan); | ||
1373 | errcode = -1; | ||
1374 | } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) { | ||
1375 | brcmf_dbg(ERROR, "(subframe %d): Bad data offset %d: HW %d min %d\n", | ||
1376 | num, doff, sublen, SDPCM_HDRLEN); | ||
1377 | errcode = -1; | ||
1378 | } | ||
1379 | /* increase the subframe count */ | ||
1380 | num++; | 1344 | num++; |
1381 | } | 1345 | } |
1382 | 1346 | ||
@@ -1402,27 +1366,11 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) | |||
1402 | skb_queue_walk_safe(&bus->glom, pfirst, pnext) { | 1366 | skb_queue_walk_safe(&bus->glom, pfirst, pnext) { |
1403 | dptr = (u8 *) (pfirst->data); | 1367 | dptr = (u8 *) (pfirst->data); |
1404 | sublen = get_unaligned_le16(dptr); | 1368 | sublen = get_unaligned_le16(dptr); |
1405 | chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]); | ||
1406 | seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]); | ||
1407 | doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); | 1369 | doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]); |
1408 | 1370 | ||
1409 | brcmf_dbg(GLOM, "Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n", | ||
1410 | num, pfirst, pfirst->data, | ||
1411 | pfirst->len, sublen, chan, seq); | ||
1412 | |||
1413 | /* precondition: chan == SDPCM_DATA_CHANNEL || | ||
1414 | chan == SDPCM_EVENT_CHANNEL */ | ||
1415 | |||
1416 | if (rxseq != seq) { | ||
1417 | brcmf_dbg(GLOM, "rx_seq %d, expected %d\n", | ||
1418 | seq, rxseq); | ||
1419 | bus->sdcnt.rx_badseq++; | ||
1420 | rxseq = seq; | ||
1421 | } | ||
1422 | rxseq++; | ||
1423 | |||
1424 | brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), | 1371 | brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), |
1425 | dptr, dlen, "Rx Subframe Data:\n"); | 1372 | dptr, pfirst->len, |
1373 | "Rx Subframe Data:\n"); | ||
1426 | 1374 | ||
1427 | __skb_trim(pfirst, sublen); | 1375 | __skb_trim(pfirst, sublen); |
1428 | skb_pull(pfirst, doff); | 1376 | skb_pull(pfirst, doff); |
@@ -1642,7 +1590,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) | |||
1642 | bus->rxhdr, SDPCM_HDRLEN, | 1590 | bus->rxhdr, SDPCM_HDRLEN, |
1643 | "RxHdr:\n"); | 1591 | "RxHdr:\n"); |
1644 | 1592 | ||
1645 | if (!brcmf_sdio_hdparser(bus, bus->rxhdr, rd)) { | 1593 | if (!brcmf_sdio_hdparser(bus, bus->rxhdr, rd, |
1594 | BRCMF_SDIO_FT_NORMAL)) { | ||
1646 | if (!bus->rxpending) | 1595 | if (!bus->rxpending) |
1647 | break; | 1596 | break; |
1648 | else | 1597 | else |
@@ -1701,7 +1650,8 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) | |||
1701 | } else { | 1650 | } else { |
1702 | memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); | 1651 | memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); |
1703 | rd_new.seq_num = rd->seq_num; | 1652 | rd_new.seq_num = rd->seq_num; |
1704 | if (!brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new)) { | 1653 | if (!brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new, |
1654 | BRCMF_SDIO_FT_NORMAL)) { | ||
1705 | rd->len = 0; | 1655 | rd->len = 0; |
1706 | brcmu_pkt_buf_free_skb(pkt); | 1656 | brcmu_pkt_buf_free_skb(pkt); |
1707 | } | 1657 | } |