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); }
static void BCMFASTPATH rpc_dbus_recv_buf(void *handle, uint8 *buf, int len) { rpc_tp_info_t *rpcb = handle; void *pkt; uint32 rpc_len; uint frag; uint agglen; if ((rpcb == NULL) || (buf == NULL)) return; frag = rpcb->tp_host_deagg_cnt_sf; agglen = len; /* TP pkt should have more than encapsulation header */ if (len <= BCM_RPC_TP_ENCAP_LEN) { RPC_TP_ERR(("%s: wrong len %d\n", __FUNCTION__, len)); goto error; } while (len > BCM_RPC_TP_ENCAP_LEN) { rpc_len = ltoh32_ua(buf); if (rpc_len > (uint32)(len - BCM_RPC_TP_ENCAP_LEN)) { rpcb->tp_host_deagg_cnt_badsflen++; return; } /* RPC_BUFFER_RX: allocate */ #if defined(BCM_RPC_ROC) if ((pkt = PKTGET(rpcb->osh, rpc_len, FALSE)) == NULL) { #else if ((pkt = bcm_rpc_tp_pktget(rpcb, rpc_len, FALSE)) == NULL) { #endif printf("%s: bcm_rpc_tp_pktget failed (len %d)\n", __FUNCTION__, len); goto error; } /* RPC_BUFFER_RX: BYTE_COPY from dbus buffer */ bcopy(buf + BCM_RPC_TP_ENCAP_LEN, bcm_rpc_buf_data(rpcb, pkt), rpc_len); /* !! send up */ bcm_rpc_tp_rx(rpcb, pkt); len -= (BCM_RPC_TP_ENCAP_LEN + rpc_len); buf += (BCM_RPC_TP_ENCAP_LEN + rpc_len); if (len > BCM_RPC_TP_ENCAP_LEN) { /* more frag */ rpcb->tp_host_deagg_cnt_sf++; RPC_TP_DEAGG(("%s: deagg %d(remaining %d) bytes\n", __FUNCTION__, rpc_len, len)); } else { if (len != 0) { printf("%s: deagg, remaining len %d is not 0\n", __FUNCTION__, len); } rpcb->tp_host_deagg_cnt_pass++; } } if (frag < rpcb->tp_host_deagg_cnt_sf) { /* aggregated frames */ rpcb->tp_host_deagg_cnt_sf++; /* last one was not counted */ rpcb->tp_host_deagg_cnt_chain++; rpcb->tp_host_deagg_cnt_bytes += agglen; } error: return; } int BCMFASTPATH bcm_rpc_tp_recv_rtn(rpc_tp_info_t *rpcb) { void *pkt; int status = 0; if (!rpcb) return BCME_BADARG; if ((pkt = bcm_rpc_tp_pktget(rpcb, PKTBUFSZ, FALSE)) == NULL) { return BCME_NORESOURCE; } RPC_TP_LOCK(rpcb); if (rpcb->rx_rtn_pkt != NULL) { RPC_TP_UNLOCK(rpcb); if (pkt != NULL) bcm_rpc_tp_pktfree(rpcb, pkt, FALSE); return BCME_BUSY; } rpcb->rx_rtn_pkt = pkt; RPC_TP_UNLOCK(rpcb); #ifndef BCMUSBDEV_EP_FOR_RPCRETURN status = dbus_recv_ctl(rpcb->bus, bcm_rpc_buf_data(rpcb, rpcb->rx_rtn_pkt), PKTBUFSZ); #else if (rpcb->has_2nd_bulk_in_ep) { status = dbus_recv_bulk(rpcb->bus, USBDEV_BULK_IN_EP2); } else { status = dbus_recv_ctl(rpcb->bus, bcm_rpc_buf_data(rpcb, rpcb->rx_rtn_pkt), PKTBUFSZ); } #endif /* BCMUSBDEV_EP_FOR_RPCRETURN */ if (status) { /* May have been cleared by complete routine */ RPC_TP_LOCK(rpcb); pkt = rpcb->rx_rtn_pkt; rpcb->rx_rtn_pkt = NULL; RPC_TP_UNLOCK(rpcb); if (pkt != NULL) bcm_rpc_tp_pktfree(rpcb, pkt, FALSE); if (status == DBUS_ERR_RXFAIL) status = BCME_RXFAIL; else if (status == DBUS_ERR_NODEVICE) status = BCME_NODEVICE; else status = BCME_ERROR; } return status; }