Beispiel #1
0
/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly
 * allocating a new one within skb
 */
static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign, size_t reserve)
{
	struct usb_cdc_ncm_ndp16 *ndp16 = NULL;
	struct usb_cdc_ncm_nth16 *nth16 = (void *)skb->data;
	size_t ndpoffset = le16_to_cpu(nth16->wNdpIndex);

	/* follow the chain of NDPs, looking for a match */
	while (ndpoffset) {
		ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb->data + ndpoffset);
		if  (ndp16->dwSignature == sign)
			return ndp16;
		ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex);
	}

	/* align new NDP */
	cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max);

	/* verify that there is room for the NDP and the datagram (reserve) */
	if ((ctx->tx_max - skb->len - reserve) < CDC_NCM_NDP_SIZE)
		return NULL;

	/* link to it */
	if (ndp16)
		ndp16->wNextNdpIndex = cpu_to_le16(skb->len);
	else
		nth16->wNdpIndex = cpu_to_le16(skb->len);

	/* push a new empty NDP */
	ndp16 = (struct usb_cdc_ncm_ndp16 *)memset(skb_put(skb, CDC_NCM_NDP_SIZE), 0, CDC_NCM_NDP_SIZE);
	ndp16->dwSignature = sign;
	ndp16->wLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_ndp16) + sizeof(struct usb_cdc_ncm_dpe16));
	return ndp16;
}
/* return a pointer to a valid struct usb_cdc_ncm_ndp16 of type sign, possibly
* allocating a new one within skb
*/
PUSB_CDC_NCM_NDP16 cdc_ncm_ndp(PMP_ADAPTER Adapter,PTCB ptcb, NCMDWORD sign, size_t reserve)
{
	PUSB_CDC_NCM_NDP16 ndp16 = NULL;
	PUSB_CDC_NCM_NTH16 nth16 = (void *)ptcb->pData;
	PMP_USBPIPE usbpipe=Adapter->UsbPipeForNIC;
	size_t ndpoffset = le16_to_cpu(nth16->wFpIndex);

	/* follow the chain of NDPs, looking for a match */
	while (ndpoffset) {
		ndp16 = (PUSB_CDC_NCM_NDP16)(ptcb->pData + ndpoffset);
		if (ndp16->dwSignature==sign)
			return ndp16;
		ndpoffset = le16_to_cpu(ndp16->wNextFpIndex);
	}

	/* align new NDP */
	cdc_ncm_align_tail(ptcb,usbpipe->tx_ndp_modulus, 0, usbpipe->tx_max);

	/* verify that there is room for the NDP and the datagram (reserve) */
	if ((usbpipe->tx_max - (ptcb->ulSize) - reserve) < CDC_NCM_NDP_SIZE)
		return NULL;

	/* link to it */
	if (ndp16)
		ndp16->wNextFpIndex = cpu_to_le16((USHORT)ptcb->ulSize);
	else
		nth16->wFpIndex = cpu_to_le16((USHORT)ptcb->ulSize);

	/* push a new empty NDP */
	ndp16 = (PUSB_CDC_NCM_NDP16)tcb_put(ptcb, CDC_NCM_NDP_SIZE);
	NdisZeroMemory(ndp16, CDC_NCM_NDP_SIZE);
	ndp16->dwSignature = sign;
	ndp16->wLength = cpu_to_le16(sizeof(USB_CDC_NCM_NDP16));
	return ndp16;
}
Beispiel #3
0
struct sk_buff *
cdc_ncm_fill_tx_frame(struct cdc_ncm_ctx *ctx, struct sk_buff *skb, __le32 sign)
{
	struct usb_cdc_ncm_nth16 *nth16;
	struct usb_cdc_ncm_ndp16 *ndp16;
	struct sk_buff *skb_out;
	u16 n = 0, index, ndplen;
	u8 ready2send = 0;

	/* if there is a remaining skb, it gets priority */
	if (skb != NULL) {
		swap(skb, ctx->tx_rem_skb);
		swap(sign, ctx->tx_rem_sign);
	} else {
		ready2send = 1;
	}

	/* check if we are resuming an OUT skb */
	skb_out = ctx->tx_curr_skb;

	/* allocate a new OUT skb */
	if (!skb_out) {
		skb_out = alloc_skb((ctx->tx_max + 1), GFP_ATOMIC);
		if (skb_out == NULL) {
			if (skb != NULL) {
				dev_kfree_skb_any(skb);
				ctx->netdev->stats.tx_dropped++;
			}
			goto exit_no_skb;
		}
		/* fill out the initial 16-bit NTB header */
		nth16 = (struct usb_cdc_ncm_nth16 *)memset(skb_put(skb_out, sizeof(struct usb_cdc_ncm_nth16)), 0, sizeof(struct usb_cdc_ncm_nth16));
		nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
		nth16->wHeaderLength = cpu_to_le16(sizeof(struct usb_cdc_ncm_nth16));
		nth16->wSequence = cpu_to_le16(ctx->tx_seq++);

		/* count total number of frames in this NTB */
		ctx->tx_curr_frame_num = 0;
	}

	for (n = ctx->tx_curr_frame_num; n < ctx->tx_max_datagrams; n++) {
		/* send any remaining skb first */
		if (skb == NULL) {
			skb = ctx->tx_rem_skb;
			sign = ctx->tx_rem_sign;
			ctx->tx_rem_skb = NULL;

			/* check for end of skb */
			if (skb == NULL)
				break;
		}

		/* get the appropriate NDP for this skb */
		ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder);

		/* align beginning of next frame */
		cdc_ncm_align_tail(skb_out,  ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max);

		/* check if we had enough room left for both NDP and frame */
		if (!ndp16 || skb_out->len + skb->len > ctx->tx_max) {
			if (n == 0) {
				/* won't fit, MTU problem? */
				dev_kfree_skb_any(skb);
				skb = NULL;
				ctx->netdev->stats.tx_dropped++;
			} else {
				/* no room for skb - store for later */
				if (ctx->tx_rem_skb != NULL) {
					dev_kfree_skb_any(ctx->tx_rem_skb);
					ctx->netdev->stats.tx_dropped++;
				}
				ctx->tx_rem_skb = skb;
				ctx->tx_rem_sign = sign;
				skb = NULL;
				ready2send = 1;
			}
			break;
		}

		/* calculate frame number withing this NDP */
		ndplen = le16_to_cpu(ndp16->wLength);
		index = (ndplen - sizeof(struct usb_cdc_ncm_ndp16)) / sizeof(struct usb_cdc_ncm_dpe16) - 1;

		/* OK, add this skb */
		ndp16->dpe16[index].wDatagramLength = cpu_to_le16(skb->len);
		ndp16->dpe16[index].wDatagramIndex = cpu_to_le16(skb_out->len);
		ndp16->wLength = cpu_to_le16(ndplen + sizeof(struct usb_cdc_ncm_dpe16));
		memcpy(skb_put(skb_out, skb->len), skb->data, skb->len);
		dev_kfree_skb_any(skb);
		skb = NULL;

		/* send now if this NDP is full */
		if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
			ready2send = 1;
			break;
		}
	}

	/* free up any dangling skb */
	if (skb != NULL) {
		dev_kfree_skb_any(skb);
		skb = NULL;
		ctx->netdev->stats.tx_dropped++;
	}

	ctx->tx_curr_frame_num = n;

	if (n == 0) {
		/* wait for more frames */
		/* push variables */
		ctx->tx_curr_skb = skb_out;
		goto exit_no_skb;

	} else if ((n < ctx->tx_max_datagrams) && (ready2send == 0)) {
		/* wait for more frames */
		/* push variables */
		ctx->tx_curr_skb = skb_out;
		/* set the pending count */
		if (n < CDC_NCM_RESTART_TIMER_DATAGRAM_CNT)
			ctx->tx_timer_pending = CDC_NCM_TIMER_PENDING_CNT;
		goto exit_no_skb;

	} else {
		/* frame goes out */
		/* variables will be reset at next call */
	}

	/*
	 * If collected data size is less or equal CDC_NCM_MIN_TX_PKT bytes,
	 * we send buffers as it is. If we get more data, it would be more
	 * efficient for USB HS mobile device with DMA engine to receive a full
	 * size NTB, than canceling DMA transfer and receiving a short packet.
	 */
	if (skb_out->len > CDC_NCM_MIN_TX_PKT)
		/* final zero padding */
		memset(skb_put(skb_out, ctx->tx_max - skb_out->len), 0, ctx->tx_max - skb_out->len);

	/* do we need to prevent a ZLP? */
	if (((skb_out->len % le16_to_cpu(ctx->out_ep->desc.wMaxPacketSize)) == 0) &&
	    (skb_out->len < le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize)) && skb_tailroom(skb_out))
		*skb_put(skb_out, 1) = 0;	/* force short packet */

	/* set final frame length */
	nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data;
	nth16->wBlockLength = cpu_to_le16(skb_out->len);

	/* return skb */
	ctx->tx_curr_skb = NULL;
	ctx->netdev->stats.tx_packets += ctx->tx_curr_frame_num;
	return skb_out;

exit_no_skb:
	/* Start timer, if there is a remaining skb */
	if (ctx->tx_curr_skb != NULL)
		cdc_ncm_tx_timeout_start(ctx);
	return NULL;
}
BOOLEAN  
cdc_ncm_fill_tx_frame(PMP_ADAPTER Adapter,PNDIS_PACKET Packet,PTCB ptcb, NCMDWORD sign)
{
	PMP_USBPIPE  usbpipe = Adapter->UsbPipeForNIC;
	PUSB_CDC_NCM_NTH16  nth16;
	PUSB_CDC_NCM_NDP16 ndp16;
	USHORT n = 0, index, ndplen;
	UINT           PacketLength;  
	PNDIS_BUFFER   CurrentBuffer = NULL;
	PVOID          VirtualAddress = NULL;
	UINT           CurrentLength;
	BOOLEAN        bResult = TRUE;
	int            maxpacketsize;
	int max_datagrams;
	maxpacketsize=usbpipe->InterfaceData->Pipes[usbpipe->BulkPipeOutput].MaximumPacketSize;

	if (Packet == NULL) {
		return TRUE;
	}

	 max_datagrams=usbpipe->tx_max_datagrams;

	if (0==ptcb->ulSize) {

		/* fill out the initial 16-bit NTB header */
		nth16 = (PUSB_CDC_NCM_NTH16 )memset(tcb_put(ptcb, sizeof(USB_CDC_NCM_NTH16)), 0, sizeof(USB_CDC_NCM_NTH16));
		nth16->dwSignature = cpu_to_le32(USB_CDC_NCM_NTH16_SIGN);
		nth16->wHeaderLength = cpu_to_le16(sizeof(USB_CDC_NCM_NTH16));
		nth16->wSequence = cpu_to_le16(usbpipe->tx_seq++);

		/* count total number of frames in this NTB */
		ptcb->NumofOrgSendPacket= 0;
	}

	NdisQueryPacket(Packet,
		NULL,
		NULL,
		&CurrentBuffer,
		&PacketLength);

    n = ptcb->NumofOrgSendPacket;

	if(n>=max_datagrams)
	{
		ptcb->bRead2Send=1;
	}
	while(n<max_datagrams) {

		/* get the appropriate NDP for this skb */
		ndp16 = cdc_ncm_ndp(Adapter, ptcb, sign, PacketLength + usbpipe->tx_modulus + usbpipe->tx_remainder);

		/* align beginning of next frame */
		cdc_ncm_align_tail(ptcb,  usbpipe->tx_modulus, usbpipe->tx_remainder, usbpipe->tx_max);

		/* check if we had enough room left for both NDP and frame */
		if (NULL==ndp16 || ptcb->ulSize + PacketLength > usbpipe->tx_max) {
			if (n == 0) {
				ptcb->bRead2Send=0;
			} else {
				ptcb->bRead2Send = 1;
			}
			bResult=FALSE;
			break;
		}

		/* calculate frame number withing this NDP */
		ndplen = le16_to_cpu(ndp16->wLength);
		index = (ndplen - sizeof(USB_CDC_NCM_NDP16)) / sizeof(USB_CDC_NCM_DPE16);

		/* OK, add this Packet */
		ndp16->dpe16[index].wDatagramLength = cpu_to_le16((USHORT)PacketLength);
		ndp16->dpe16[index].wDatagramIndex = cpu_to_le16((USHORT)ptcb->ulSize);
		ndp16->wLength = cpu_to_le16(ndplen + sizeof(USB_CDC_NCM_DPE16));

		while(CurrentBuffer)
		{
			NdisQueryBufferSafe(
				CurrentBuffer,
				&VirtualAddress,
				&CurrentLength,
				NormalPagePriority);

			ASSERT(NULL!=VirtualAddress);

			CurrentLength = min(CurrentLength, PacketLength);         

			if(CurrentLength)
			{
				// Copy the data.
				NdisMoveMemory(tcb_put(ptcb, CurrentLength), VirtualAddress, CurrentLength);
				PacketLength -= CurrentLength;            
			}
			NdisGetNextBuffer(
				CurrentBuffer,
				&CurrentBuffer);
		}
		if(PacketLength){
			NdisZeroMemory(tcb_put(ptcb, PacketLength), PacketLength);
		    PacketLength=0;
		}


		InsertTailList(
			&ptcb->ListOrgSendPacket, 
			(PLIST_ENTRY)&Packet->MiniportReserved[0]);

		ptcb->NumofOrgSendPacket++;

		/* send now if this NDP is full */
		if (index >= CDC_NCM_DPT_DATAGRAMS_MAX) {
			ptcb->bRead2Send = 1;
			break;
		}
		break;
	}

	/* If collected data size is less or equal CDC_NCM_MIN_TX_PKT
	* bytes, we send buffers as it is. If we get more data, it
	* would be more efficient for USB HS mobile device with DMA
	* engine to receive a full size NTB, than canceling DMA
	* transfer and receiving a short packet.
	*
	* This optimization support is pointless if we end up sending
	* a ZLP after full sized NTBs.
	*/
	if (ptcb->bRead2Send==1&&(ptcb->ulSize%maxpacketsize== 0))
		memset(tcb_put(ptcb, 1), 0, 1);/* force short packet */

	/* set final frame length */
	nth16 = (PUSB_CDC_NCM_NTH16 )ptcb->pData;
	nth16->wBlockLength = cpu_to_le16((USHORT)ptcb->ulSize);

	return bResult;
}