/* 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; }
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; }