static int bcm_rpc_tp_tx_agg(rpc_tp_info_t *rpcb, rpc_buf_t *b) { uint totlen; uint pktlen; int err = 0; ASSERT(rpcb->tp_tx_aggregation); pktlen = pkttotlen(rpcb->osh, b); totlen = pktlen + rpcb->tp_tx_agg_bytes; if ((totlen > rpcb->tp_tx_agg_bytes_max) || (rpcb->tp_tx_agg_sframes + 1 > rpcb->tp_tx_agg_sframes_limit)) { RPC_TP_AGG(("%s: terminte TP agg for txbyte %d or txframe %d\n", __FUNCTION__, rpcb->tp_tx_agg_bytes_max, rpcb->tp_tx_agg_sframes_limit)); /* release current agg, continue with new agg */ err = bcm_rpc_tp_tx_agg_release(rpcb); } bcm_rpc_tp_tx_agg_append(rpcb, b); /* if the new frag is also already over the agg limit, release it */ if (pktlen >= rpcb->tp_tx_agg_bytes_max) { int new_err; new_err = bcm_rpc_tp_tx_agg_release(rpcb); if (!err) err = new_err; } return err; }
/* * 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)); }
static void bcm_rpc_tp_tx_encap(rpc_tp_info_t * rpcb, rpc_buf_t *b) { uint32 *tp_lenp; uint32 rpc_len; rpc_len = pkttotlen(rpcb->osh, b); tp_lenp = (uint32*)bcm_rpc_buf_push(rpcb, b, BCM_RPC_TP_ENCAP_LEN); *tp_lenp = htol32(rpc_len); }
void* osl_pktdup(void *drv, void *p) { END_OBJ *end; M_BLK_ID m; int len = pkttotlen(drv, p); /* drv is a pointer to an END_OBJ in disguise */ end = (END_OBJ*)drv; /* dont use a real dup until PR14233 is fixed m = netMblkChainDup(end->pNetPool, (M_BLK_ID)p, 0, M_COPYALL, M_DONTWAIT); */ /* alloc a new mbuf */ if ((m = PKTGET(drv, len, TRUE)) == NULL) return (NULL); /* copy data into mbuf */ pktcopy(drv, p, 0, len, (uchar*)PKTDATA(drv, m)); ASSERT(len == PKTLEN(drv, m)); return (m); }
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); }