/* copy a pkt buffer chain into a buffer */ uint pktcopy(void *drv, void *p, uint offset, int len, uchar *buf) { uint n, ret = 0; if (len < 0) len = 4096; /* "infinite" */ /* skip 'offset' bytes */ for (; p && offset; p = PKTNEXT(drv, p)) { if (offset < (uint)PKTLEN(drv, p)) break; offset -= PKTLEN(drv, p); } if (!p) return 0; /* copy the data */ for (; p && len; p = PKTNEXT(drv, p)) { n = MIN((uint)PKTLEN(drv, p) - offset, (uint)len); bcopy(PKTDATA(drv, p) + offset, buf, n); buf += n; len -= n; ret += n; offset = 0; } return ret; }
/* copy a buffer into a pkt buffer chain */ uint pktfrombuf(osl_t *osh, void *p, uint offset, int len, uchar *buf) { uint n, ret = 0; /* skip 'offset' bytes */ for (; p && offset; p = PKTNEXT(osh, p)) { if (offset < (uint)PKTLEN(osh, p)) break; offset -= PKTLEN(osh, p); } if (!p) return 0; /* copy the data */ for (; p && len; p = PKTNEXT(osh, p)) { n = MIN((uint)PKTLEN(osh, p) - offset, (uint)len); bcopy(buf, PKTDATA(osh, p) + offset, n); buf += n; len -= n; ret += n; offset = 0; } return ret; }
/* * tp_tx_agg_p points to the header lbuf, tp_tx_agg_ptail points to the tail lbuf * * The TP agg format typically will be below * | TP header(len) | subframe1 rpc_header | subframe1 data | * | TP header(len) | subframe2 rpc_header | subframe2 data | * ... * | TP header(len) | subframeN rpc_header | subframeN data | * no padding */ static void bcm_rpc_tp_tx_agg_append(rpc_tp_info_t * rpcb, rpc_buf_t *b) { uint tp_len; tp_len = pkttotlen(rpcb->osh, b); if (rpcb->tp_tx_agg_p == NULL) { /* toc, set tail to last fragment */ if (PKTNEXT(rpcb->osh, b)) { rpcb->tp_tx_agg_p = b; rpcb->tp_tx_agg_ptail = pktlast(rpcb->osh, b); } else rpcb->tp_tx_agg_p = rpcb->tp_tx_agg_ptail = b; } else { /* chain the pkts at the end of current one */ ASSERT(rpcb->tp_tx_agg_ptail != NULL); PKTSETNEXT(rpcb->osh, rpcb->tp_tx_agg_ptail, b); /* toc, set tail to last fragment */ if (PKTNEXT(rpcb->osh, b)) { rpcb->tp_tx_agg_ptail = pktlast(rpcb->osh, b); } else rpcb->tp_tx_agg_ptail = b; } rpcb->tp_tx_agg_sframes++; rpcb->tp_tx_agg_bytes += tp_len; RPC_TP_AGG(("%s: tp_len %d tot %d, sframe %d\n", __FUNCTION__, tp_len, rpcb->tp_tx_agg_bytes, rpcb->tp_tx_agg_sframes)); }
/* return the last buffer of chained pkt */ void * pktlast(osl_t *osh, void *p) { for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p)) ; return (p); }
/* return the last buffer of chained pkt */ void * BCMROMFN(pktlast)(osl_t *osh, void *p) { for (; PKTNEXT(osh, p); p = PKTNEXT(osh, p)) ; return (p); }
int bcm_rpc_buf_totlen_get(rpc_tp_info_t * rpc_th, rpc_buf_t* b) { int totlen = 0; for (; b; b = (rpc_buf_t *) PKTNEXT(rpc_th->osh, b)) { totlen += PKTLEN(rpc_th->osh, b); } return totlen; }
/* return total length of buffer chain */ uint pkttotlen(void *drv, void *p) { uint total; total = 0; for (; p; p = PKTNEXT(drv, p)) total += PKTLEN(drv, p); return (total); }
/* return total length of buffer chain */ uint pkttotlen(osl_t *osh, void *p) { uint total; total = 0; for (; p; p = PKTNEXT(osh, p)) total += PKTLEN(osh, p); return (total); }
/* count segments of a chained packet */ uint pktsegcnt(osl_t *osh, void *p) { uint cnt; for (cnt = 0; p; p = PKTNEXT(osh, p)) cnt++; return cnt; }
/* copy data from one pkt buffer (chain) to another */ uint pkt2pktcopy(osl_t *osh, void *p1, uint offs1, void *p2, uint offs2, int maxlen) { uint8 *dp1, *dp2; uint len1, len2, copylen, totallen; for (; p1 && offs; p1 = PKTNEXT(osh, p1)) { if (offs1 < (uint)PKTLEN(osh, p1)) break; offs1 -= PKTLEN(osh, p1); } for (; p2 && offs; p2 = PKTNEXT(osh, p2)) { if (offs2 < (uint)PKTLEN(osh, p2)) break; offs2 -= PKTLEN(osh, p2); } /* Heck w/it, only need the above for now */ }
/* count segments of a chained packet */ uint BCMROMFN(pktsegcnt)(osl_t *osh, void *p) { uint cnt; for (cnt = 0; p; p = PKTNEXT(osh, p)) cnt++; return cnt; }
/* pretty hex print a pkt buffer chain */ void prpkt(char *msg, void *drv, void *p0) { void *p; if (msg && (msg[0] != '\0')) printf("%s: ", msg); for (p = p0; p; p = PKTNEXT(drv, p)) prhex(NULL, PKTDATA(drv, p), PKTLEN(drv, p)); }
uint etc_totlen(etc_info_t *etc, void *p) { uint total; total = 0; for (; p; p = PKTNEXT(etc->et, p)) total += PKTLEN(etc->et, p); return (total); }
uint BCMFASTPATH osl_dma_map(osl_t *osh, void *va, uint size, int direction, void *p, hnddma_seg_map_t *dmah) { int dir; ASSERT((osh && (osh->magic == OS_HANDLE_MAGIC))); dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE; #if defined(__ARM_ARCH_7A__) && defined(BCMDMASGLISTOSL) if (dmah != NULL) { int32 nsegs, i, totsegs = 0, totlen = 0; struct scatterlist *sg, _sg[16]; struct sk_buff *skb; for (skb = (struct sk_buff *)p; skb != NULL; skb = PKTNEXT(osh, skb)) { sg = &_sg[totsegs]; if (skb_is_nonlinear(skb)) { nsegs = skb_to_sgvec(skb, sg, 0, PKTLEN(osh, skb)); ASSERT((nsegs > 0) && (nsegs <= 16)); pci_map_sg(osh->pdev, sg, nsegs, dir); } else { nsegs = 1; sg->page_link = 0; sg_set_buf(sg, PKTDATA(osh, skb), PKTLEN(osh, skb)); pci_map_single(osh->pdev, PKTDATA(osh, skb), PKTISCTF(osh, skb) ? CTFMAPSZ : PKTLEN(osh, skb), dir); } totsegs += nsegs; totlen += PKTLEN(osh, skb); } dmah->nsegs = totsegs; dmah->origsize = totlen; for (i = 0, sg = _sg; i < totsegs; i++, sg++) { dmah->segs[i].addr = sg_phys(sg); dmah->segs[i].length = sg->length; } return dmah->segs[0].addr; } #endif return (pci_map_single(osh->pdev, va, size, dir)); }
void bcm_rpc_tp_rx_from_dnglbus(rpc_tp_info_t *rpc_th, struct lbuf *lb) { void *orig_p, *p; void *rpc_p, *rpc_prev; uint pktlen, tp_len, iter = 0; osl_t *osh; bool dbg_agg; uint dbg_data[16], i; /* must fit host agg limit BCM_RPC_TP_HOST_AGG_MAX_SFRAME+1 */ dbg_agg = FALSE; rpc_th->rx_cnt++; if (rpc_th->rx_pkt == NULL) { RPC_TP_ERR(("%s: no rpc rx fn, dropping\n", __FUNCTION__)); rpc_th->rxdrop_cnt++; lb_free(lb); return; } orig_p = PKTFRMNATIVE(rpc_th->osh, lb); osh = rpc_th->osh; /* take ownership of the dnglbus packet chain * since it will be freed by bcm_rpc_tp_buf_free() */ rpc_th->buf_cnt_inuse += pktsegcnt(rpc_th->osh, orig_p); dbg_data[0] = pktsegcnt(rpc_th->osh, orig_p); pktlen = PKTLEN(osh, orig_p); p = orig_p; /* while we have more data in the TP frame's packet chain, * create a packet chain(could be cloned) for the next RPC frame * then give it away to high layer for process(buffer not freed) */ while (p != NULL) { iter++; /* read TP_HDR(len of rpc frame) and pull the data pointer past the length word */ if (pktlen >= BCM_RPC_TP_ENCAP_LEN) { ASSERT(((uint)PKTDATA(osh, p) & 0x3) == 0); /* ensure aligned word read */ tp_len = ltoh32(*(uint32*)PKTDATA(osh, p)); PKTPULL(osh, p, BCM_RPC_TP_ENCAP_LEN); pktlen -= BCM_RPC_TP_ENCAP_LEN; } else { /* error case: less data than the encapsulation size * treat as an empty tp buffer, at end of current buffer */ tp_len = 0; pktlen = 0; rpc_th->tp_dngl_deagg_cnt_badsflen++; /* bad sf len */ } /* if TP header finished a buffer(rpc header in next chained buffer), open next */ if (pktlen == 0) { void *next_p = PKTNEXT(osh, p); PKTSETNEXT(osh, p, NULL); rpc_th->buf_cnt_inuse--; PKTFREE(osh, p, FALSE); p = next_p; if (p) pktlen = PKTLEN(osh, p); } dbg_data[iter] = tp_len; if (tp_len < pktlen || dbg_agg) { dbg_agg = TRUE; RPC_TP_DEAGG(("DEAGG: [%d] p %p data %p pktlen %d tp_len %d\n", iter, p, PKTDATA(osh, p), pktlen, tp_len)); rpc_th->tp_dngl_deagg_cnt_sf++; rpc_th->tp_dngl_deagg_cnt_bytes += tp_len; } /* empty TP buffer (special case: use tp_len to pad for some USB pktsize bugs) */ if (tp_len == 0) { rpc_th->tp_dngl_deagg_cnt_pass++; continue; } else if (tp_len > 10000 ) { /* something is wrong */ /* print out msgs according to value of p -- in case it is NULL */ if (p != NULL) { RPC_TP_ERR(("DEAGG: iter %d, p(%p data %p pktlen %d)\n", iter, p, PKTDATA(osh, p), PKTLEN(osh, p))); } else { RPC_TP_ERR(("DEAGG: iter %d, p is NULL", iter)); } } /* ========= For this TP subframe, find the end, build a chain, sendup ========= */ /* RPC frame packet chain starts with this packet */ rpc_prev = NULL; rpc_p = p; ASSERT(p != NULL); /* find the last frag in this rpc chain */ while ((tp_len >= pktlen) && p) { if (dbg_agg) RPC_TP_DEAGG(("DEAGG: tp_len %d consumes p(%p pktlen %d)\n", tp_len, p, pktlen)); rpc_prev = p; p = PKTNEXT(osh, p); tp_len -= pktlen; if (p != NULL) { pktlen = PKTLEN(osh, p); } else { if (tp_len != 0) { uint totlen, seg; totlen = pkttotlen(osh, rpc_p); seg = pktsegcnt(rpc_th->osh, rpc_p); RPC_TP_ERR(("DEAGG, toss[%d], orig_p %p segcnt %d", iter, orig_p, dbg_data[0])); RPC_TP_ERR(("DEAGG,rpc_p %p totlen %d pktl %d tp_len %d\n", rpc_p, totlen, pktlen, tp_len)); for (i = 1; i <= iter; i++) RPC_TP_ERR(("tplen[%d] = %d ", i, dbg_data[i])); RPC_TP_ERR(("\n")); p = rpc_p; while (p != NULL) { RPC_TP_ERR(("this seg len %d\n", PKTLEN(osh, p))); p = PKTNEXT(osh, p); } rpc_th->buf_cnt_inuse -= seg; PKTFREE(osh, rpc_p, FALSE); rpc_th->tp_dngl_deagg_cnt_badfmt++; /* big hammer to recover USB * extern void dngl_reboot(void); dngl_reboot(); */ goto end; } pktlen = 0; break; } } /* fix up the last frag */ if (tp_len == 0) { /* if the whole RPC buffer chain ended at the end of the prev TP buffer, * end the RPC buffer chain. we are done */ if (dbg_agg) RPC_TP_DEAGG(("DEAGG: END rpc chain p %p len %d\n\n", rpc_prev, pktlen)); PKTSETNEXT(osh, rpc_prev, NULL); if (iter > 1) { rpc_th->tp_dngl_deagg_cnt_chain++; RPC_TP_DEAGG(("this frag %d totlen %d\n", pktlen, pkttotlen(osh, orig_p))); } } else { /* if pktlen has more bytes than tp_len, another tp frame must follow * create a clone of the sub-range of the current TP buffer covered * by the RPC buffer, attach to the end of the RPC buffer chain * (cut off the original chain link) * continue chain looping(p != NULL) */ void *new_p; ASSERT(p != NULL); RPC_TP_DEAGG(("DEAGG: cloning %d bytes out of p(%p data %p) len %d\n", tp_len, p, PKTDATA(osh, p), pktlen)); new_p = osl_pktclone(osh, p, 0, tp_len); rpc_th->buf_cnt_inuse++; rpc_th->tp_dngl_deagg_cnt_clone++; RPC_TP_DEAGG(("DEAGG: after clone, newp(%p data %p pktlen %d)\n", new_p, PKTDATA(osh, new_p), PKTLEN(osh, new_p))); if (rpc_prev) { RPC_TP_DEAGG(("DEAGG: chaining: %p->%p(clone)\n", rpc_prev, new_p)); PKTSETNEXT(osh, rpc_prev, new_p); } else { RPC_TP_DEAGG(("DEAGG: clone %p is a complete rpc pkt\n", new_p)); rpc_p = new_p; } PKTPULL(osh, p, tp_len); pktlen -= tp_len; RPC_TP_DEAGG(("DEAGG: remainder packet p %p data %p pktlen %d\n", p, PKTDATA(osh, p), PKTLEN(osh, p))); } /* !! send up */ (rpc_th->rx_pkt)(rpc_th->rx_context, rpc_p); } end: ASSERT(p == NULL); }
/* * Just like above except go through the extra effort of splitting * buffers that cross 4Kbyte boundaries into multiple tx descriptors. */ int dma_tx(dma_info_t *di, void *p0, uint32 coreflags) { void *p, *next; uchar *data; uint plen, len; uchar *page, *start, *end; uint txout; uint32 ctrl; uint32 pa; DMA_TRACE(("%s: dma_tx\n", di->name)); txout = di->txout; ctrl = 0; /* * Walk the chain of packet buffers * splitting those that cross 4 Kbyte boundaries * allocating and initializing transmit descriptor entries. */ for (p = p0; p; p = next) { data = PKTDATA(di->drv, p); plen = PKTLEN(di->drv, p); next = PKTNEXT(di->drv, p); /* PR988 - skip zero length buffers */ if (plen == 0) continue; for (page = (uchar*)PAGEBASE(data); page <= (uchar*)PAGEBASE(data + plen - 1); page += PAGESZ) { /* return nonzero if out of tx descriptors */ if (NEXTTXD(txout) == di->txin) goto outoftxd; start = (page == (uchar*)PAGEBASE(data))? data: page; end = (page == (uchar*)PAGEBASE(data + plen))? (data + plen): (page + PAGESZ); len = end - start; /* build the descriptor control value */ ctrl = len & CTRL_BC_MASK; /* PR3697: Descriptor flags are not ignored for descriptors where SOF is clear */ ctrl |= coreflags; if ((p == p0) && (start == data)) ctrl |= CTRL_SOF; if ((next == NULL) && (end == (data + plen))) ctrl |= (CTRL_IOC | CTRL_EOF); if (txout == (di->ntxd - 1)) ctrl |= CTRL_EOT; /* get physical address of buffer start */ pa = (uint32) DMA_MAP(di->dev, start, len, DMA_TX, p); /* init the tx descriptor */ W_SM(&di->txd[txout].ctrl, BUS_SWAP32(ctrl)); W_SM(&di->txd[txout].addr, BUS_SWAP32(pa + di->dataoffset)); ASSERT(di->txp[txout] == NULL); txout = NEXTTXD(txout); } } /* if last txd eof not set, fix it */ if (!(ctrl & CTRL_EOF)) W_SM(&di->txd[PREVTXD(txout)].ctrl, BUS_SWAP32(ctrl | CTRL_IOC | CTRL_EOF)); /* save the packet */ di->txp[di->txout] = p0; /* bump the tx descriptor index */ di->txout = txout; /* kick the chip */ W_REG(&di->regs->xmtptr, I2B(txout)); /* tx flow control */ di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1; return (0); outoftxd: DMA_ERROR(("%s: dma_tx: out of txds\n", di->name)); PKTFREE(di->drv, p0, TRUE); di->txavail = 0; di->hnddma.txnobuf++; return (-1); }