Beispiel #1
0
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);
}
Beispiel #2
0
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;
}