1 /*
2  * This file contains NDIS5.X Implementation of adapter driver procedures.
3  *
4  * Copyright (c) 2008-2017 Red Hat, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met :
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and / or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of their contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 #include "ParaNdis5.h"
30 
31 
32 #ifdef WPP_EVENT_TRACING
33 #include "ParaNdis5-Impl.tmh"
34 #endif
35 
36 
37 /**********************************************************
38 Per-packet information holder
39 ***********************************************************/
40 #define SEND_ENTRY_FLAG_READY       0x0001
41 #define SEND_ENTRY_TSO_USED         0x0002
42 #define SEND_ENTRY_NO_INDIRECT      0x0004
43 #define SEND_ENTRY_TCP_CS           0x0008
44 #define SEND_ENTRY_UDP_CS           0x0010
45 #define SEND_ENTRY_IP_CS            0x0020
46 
47 
48 
49 typedef struct _tagSendEntry
50 {
51     LIST_ENTRY      list;
52     PNDIS_PACKET    packet;
53     ULONG           flags;
54     ULONG           ipTransferUnit;
55     union
56     {
57         ULONG PriorityDataLong;
58         UCHAR PriorityData[4];
59     };
60 } tSendEntry;
61 
62 /**********************************************************
63 This defines field in NDIS_PACKET structure to use as holder
64 of our reference pointer for indicated packets
65 ***********************************************************/
66 #define IDXTOUSE    0
67 #define REF_MINIPORT(Packet) ((PVOID *)(Packet->MiniportReservedEx + IDXTOUSE * sizeof(PVOID)))
68 
69 
70 /**********************************************************
71 Memory allocation procedure
72 Parameters:
73     context(not used)
74     ULONG ulRequiredSize    size of block to allocate
75 Return value:
76     PVOID                   pointer to block or NULL if failed
77 ***********************************************************/
78 PVOID ParaNdis_AllocateMemory(PARANDIS_ADAPTER *pContext, ULONG ulRequiredSize)
79 {
80     PVOID p;
81     UNREFERENCED_PARAMETER(pContext);
82     if (NDIS_STATUS_SUCCESS != NdisAllocateMemoryWithTag(&p, ulRequiredSize, PARANDIS_MEMORY_TAG))
83         p = NULL;
84     if (!p)
85     {
86         DPrintf(0, ("[%s] failed (%d bytes)", __FUNCTION__, ulRequiredSize));
87     }
88     return p;
89 }
90 
91 /**********************************************************
92 Implementation of "open adapter configuration" operation
93 Parameters:
94     context
95 Return value:
96     NDIS_HANDLE     Handle to open configuration or NULL, if failed
97 ***********************************************************/
98 NDIS_HANDLE ParaNdis_OpenNICConfiguration(PARANDIS_ADAPTER *pContext)
99 {
100     NDIS_STATUS status;
101     NDIS_HANDLE cfg;
102     DEBUG_ENTRY(2);
103     NdisOpenConfiguration(&status, &cfg, pContext->WrapperConfigurationHandle);
104     if (status != NDIS_STATUS_SUCCESS)
105         cfg = NULL;
106     DEBUG_EXIT_STATUS(0, status);
107     return cfg;
108 }
109 
110 void ParaNdis_RestoreDeviceConfigurationAfterReset(
111     PARANDIS_ADAPTER *pContext)
112 {
113 
114 }
115 
116 
117 /**********************************************************
118 Indicates connect/disconnect events
119 Parameters:
120     context
121     BOOLEAN bConnected  1/0 connect/disconnect
122 ***********************************************************/
123 VOID ParaNdis_IndicateConnect(PARANDIS_ADAPTER *pContext, BOOLEAN bConnected, BOOLEAN bForce)
124 {
125     // indicate disconnect always
126     if (bConnected != pContext->bConnected || bForce)
127     {
128         pContext->bConnected = bConnected;
129         DPrintf(0, ("Indicating %sconnect", bConnected ? "" : "dis"));
130         ParaNdis_DebugHistory(pContext, hopConnectIndication, NULL, bConnected, 0, 0);
131         NdisMIndicateStatus(
132             pContext->MiniportHandle,
133             bConnected ? NDIS_STATUS_MEDIA_CONNECT : NDIS_STATUS_MEDIA_DISCONNECT,
134             0,
135             0);
136         NdisMIndicateStatusComplete(pContext->MiniportHandle);
137     }
138 }
139 
140 VOID ParaNdis_SetPowerState(PARANDIS_ADAPTER *pContext, NDIS_DEVICE_POWER_STATE newState)
141 {
142     //NDIS_DEVICE_POWER_STATE prev = pContext->powerState;
143     pContext->powerState = newState;
144 }
145 
146 
147 /**********************************************************
148 Callback of timer for connect indication, if used
149 Parameters:
150     context (on FunctionContext)
151     all the rest are irrelevant
152 ***********************************************************/
153 static VOID NTAPI OnConnectTimer(
154     IN PVOID  SystemSpecific1,
155     IN PVOID  FunctionContext,
156     IN PVOID  SystemSpecific2,
157     IN PVOID  SystemSpecific3
158     )
159 {
160     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)FunctionContext;
161     ParaNdis_ReportLinkStatus(pContext, FALSE);
162 }
163 
164 /**********************************************************
165 NDIS5 implementation of shared memory allocation
166 Parameters:
167     context
168     tCompletePhysicalAddress *pAddresses
169             the structure accumulates all our knowledge
170             about the allocation (size, addresses, cacheability etc)
171 Return value:
172     TRUE if the allocation was successful
173 ***********************************************************/
174 BOOLEAN ParaNdis_InitialAllocatePhysicalMemory(
175     PARANDIS_ADAPTER *pContext,
176     tCompletePhysicalAddress *pAddresses)
177 {
178     NdisMAllocateSharedMemory(
179         pContext->MiniportHandle,
180         pAddresses->size,
181         (BOOLEAN)pAddresses->IsCached,
182         &pAddresses->Virtual,
183         &pAddresses->Physical);
184     return pAddresses->Virtual != NULL;
185 }
186 
187 /**********************************************************
188 Callback of timer for pending events cleanup after regular DPC processing
189 Parameters:
190     context (on FunctionContext)
191     all the rest are irrelevant
192 ***********************************************************/
193 static VOID NTAPI OnDPCPostProcessTimer(
194     IN PVOID  SystemSpecific1,
195     IN PVOID  FunctionContext,
196     IN PVOID  SystemSpecific2,
197     IN PVOID  SystemSpecific3
198     )
199 {
200     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)FunctionContext;
201     ULONG requiresProcessing;
202     requiresProcessing = ParaNdis_DPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE);
203     if (requiresProcessing)
204     {
205         // we need to request additional DPC
206         InterlockedOr(&pContext->InterruptStatus, requiresProcessing);
207         NdisSetTimer(&pContext->DPCPostProcessTimer, 10);
208     }
209 }
210 
211 /**********************************************************
212 NDIS5 implementation of shared memory freeing
213 Parameters:
214     context
215     tCompletePhysicalAddress *pAddresses
216             the structure accumulates all our knowledge
217             about the allocation (size, addresses, cacheability etc)
218             filled by ParaNdis_InitialAllocatePhysicalMemory
219 ***********************************************************/
220 VOID ParaNdis_FreePhysicalMemory(
221     PARANDIS_ADAPTER *pContext,
222     tCompletePhysicalAddress *pAddresses)
223 {
224 
225     NdisMFreeSharedMemory(
226         pContext->MiniportHandle,
227         pAddresses->size,
228         (BOOLEAN)pAddresses->IsCached,
229         pAddresses->Virtual,
230         pAddresses->Physical);
231 }
232 
233 static void DebugParseOffloadBits()
234 {
235     NDIS_TCP_IP_CHECKSUM_PACKET_INFO info;
236     tChecksumCheckResult res;
237     ULONG val = 1;
238     int level = 1;
239     while (val)
240     {
241         info.Value = val;
242         if (info.Receive.NdisPacketIpChecksumFailed) DPrintf(level, ("W.%X=IPCS failed", val));
243         if (info.Receive.NdisPacketIpChecksumSucceeded) DPrintf(level, ("W.%X=IPCS OK", val));
244         if (info.Receive.NdisPacketTcpChecksumFailed) DPrintf(level, ("W.%X=TCPCS failed", val));
245         if (info.Receive.NdisPacketTcpChecksumSucceeded) DPrintf(level, ("W.%X=TCPCS OK", val));
246         if (info.Receive.NdisPacketUdpChecksumFailed) DPrintf(level, ("W.%X=UDPCS failed", val));
247         if (info.Receive.NdisPacketUdpChecksumSucceeded) DPrintf(level, ("W.%X=UDPCS OK", val));
248         val = val << 1;
249     }
250     val = 1;
251     while (val)
252     {
253         res.value = val;
254         if (res.flags.IpFailed) DPrintf(level, ("C.%X=IPCS failed", val));
255         if (res.flags.IpOK) DPrintf(level, ("C.%X=IPCS OK", val));
256         if (res.flags.TcpFailed) DPrintf(level, ("C.%X=TCPCS failed", val));
257         if (res.flags.TcpOK) DPrintf(level, ("C.%X=TCPCS OK", val));
258         if (res.flags.UdpFailed) DPrintf(level, ("C.%X=UDPCS failed", val));
259         if (res.flags.UdpOK) DPrintf(level, ("C.%X=UDPCS OK", val));
260         val = val << 1;
261     }
262 }
263 
264 /**********************************************************
265 Procedure for NDIS5 specific initialization:
266     register interrupt handler
267     allocate pool of packets to indicate
268     allocate pool of buffers to indicate
269     initialize halt event
270 Parameters:
271     context
272 Return value:
273     SUCCESS or failure code
274 ***********************************************************/
275 NDIS_STATUS NTAPI ParaNdis_FinishSpecificInitialization(
276     PARANDIS_ADAPTER *pContext)
277 {
278     NDIS_STATUS     status;
279     UINT            nPackets = pContext->NetMaxReceiveBuffers * 2;
280     DEBUG_ENTRY(2);
281     NdisInitializeEvent(&pContext->HaltEvent);
282     InitializeListHead(&pContext->SendQueue);
283     InitializeListHead(&pContext->TxWaitingList);
284     NdisInitializeTimer(&pContext->ConnectTimer, OnConnectTimer, pContext);
285     NdisInitializeTimer(&pContext->DPCPostProcessTimer, OnDPCPostProcessTimer, pContext);
286 
287     status = NdisMRegisterInterrupt(
288         &pContext->Interrupt,
289         pContext->MiniportHandle,
290         pContext->AdapterResources.Vector,
291         pContext->AdapterResources.Level,
292         TRUE,
293         TRUE,
294         NdisInterruptLevelSensitive);
295 
296     if (status == NDIS_STATUS_SUCCESS)
297     {
298         NdisAllocatePacketPool(
299             &status,
300             &pContext->PacketPool,
301             nPackets,
302             PROTOCOL_RESERVED_SIZE_IN_PACKET );
303     }
304     if (status == NDIS_STATUS_SUCCESS)
305     {
306         NdisAllocateBufferPool(
307             &status,
308             &pContext->BuffersPool,
309             nPackets);
310     }
311 
312 #if !DO_MAP_REGISTERS
313     if (status == NDIS_STATUS_SUCCESS)
314     {
315         status = NdisMInitializeScatterGatherDma(
316             pContext->MiniportHandle,
317             TRUE,
318             0x10000);
319         pContext->bDmaInitialized = status == NDIS_STATUS_SUCCESS;
320     }
321 #else
322     if (status == NDIS_STATUS_SUCCESS)
323     {
324         status = NdisMAllocateMapRegisters(
325             pContext->MiniportHandle,
326             0,
327             NDIS_DMA_32BITS,
328             64,
329             PAGE_SIZE);
330         pContext->bDmaInitialized = status == NDIS_STATUS_SUCCESS;
331     }
332 #endif
333     if (status == NDIS_STATUS_SUCCESS)
334     {
335         DebugParseOffloadBits();
336     }
337     DEBUG_EXIT_STATUS(status ? 0 : 2, status);
338     return status;
339 }
340 
341 /**********************************************************
342 Procedure of NDIS5-specific cleanup:
343     deregister interrupt
344     free buffer and packet pool
345 Parameters:
346     context
347 ***********************************************************/
348 VOID ParaNdis_FinalizeCleanup(PARANDIS_ADAPTER *pContext)
349 {
350     if (pContext->Interrupt.InterruptObject)
351     {
352         NdisMDeregisterInterrupt(&pContext->Interrupt);
353     }
354     if (pContext->BuffersPool)
355     {
356         NdisFreeBufferPool(pContext->BuffersPool);
357     }
358     if (pContext->PacketPool)
359     {
360         NdisFreePacketPool(pContext->PacketPool);
361     }
362 #if DO_MAP_REGISTERS
363     if (pContext->bDmaInitialized)
364     {
365         NdisMFreeMapRegisters(pContext->MiniportHandle);
366     }
367 #endif
368 }
369 
370 
371 static FORCEINLINE ULONG MaxNdisBufferDataSize(PARANDIS_ADAPTER *pContext, pIONetDescriptor pBufferDesc)
372 {
373     ULONG size  = pBufferDesc->DataInfo.size;
374     if (pContext->bUseMergedBuffers) size -= pContext->nVirtioHeaderSize;
375     return size;
376 }
377 
378 
379 /**********************************************************
380 NDIS5-specific procedure for binding RX buffer to
381 NDIS_PACKET and NDIS_BUFFER
382 Parameters:
383     context
384     pIONetDescriptor pBuffersDesc   VirtIO buffer descriptor
385 
386 Return value:
387     TRUE, if bound successfully
388     FALSE, if no buffer or packet can be allocated
389 ***********************************************************/
390 BOOLEAN ParaNdis_BindBufferToPacket(
391     PARANDIS_ADAPTER *pContext,
392     pIONetDescriptor pBufferDesc)
393 {
394     NDIS_STATUS     status;
395     PNDIS_BUFFER    pBuffer = NULL;
396     PNDIS_PACKET    Packet = NULL;
397     NdisAllocatePacket(&status, &Packet, pContext->PacketPool);
398     if (status == NDIS_STATUS_SUCCESS)
399     {
400         NdisReinitializePacket(Packet);
401         NdisAllocateBuffer(
402             &status,
403             &pBuffer,
404             pContext->BuffersPool,
405             RtlOffsetToPointer(pBufferDesc->DataInfo.Virtual, pContext->bUseMergedBuffers ? pContext->nVirtioHeaderSize : 0),
406             MaxNdisBufferDataSize(pContext, pBufferDesc));
407     }
408     if (status == NDIS_STATUS_SUCCESS)
409     {
410         PNDIS_PACKET_OOB_DATA pOOB = NDIS_OOB_DATA_FROM_PACKET(Packet);
411         NdisZeroMemory(pOOB, sizeof(NDIS_PACKET_OOB_DATA));
412         NDIS_SET_PACKET_HEADER_SIZE(Packet, ETH_HEADER_SIZE);
413         NdisChainBufferAtFront(Packet, pBuffer);
414         pBufferDesc->pHolder = Packet;
415     }
416     else
417     {
418         if (pBuffer) NdisFreeBuffer(pBuffer);
419         if (Packet)  NdisFreePacket(Packet);
420     }
421     return status == NDIS_STATUS_SUCCESS;
422 }
423 
424 
425 /**********************************************************
426 NDIS5-specific procedure for unbinding
427 previously bound RX buffer from it's NDIS_PACKET and NDIS_BUFFER
428 Parameters:
429     context
430     pIONetDescriptor pBuffersDesc   VirtIO buffer descriptor
431 ***********************************************************/
432 void ParaNdis_UnbindBufferFromPacket(
433     PARANDIS_ADAPTER *pContext,
434     pIONetDescriptor pBufferDesc)
435 {
436     if (pBufferDesc->pHolder)
437     {
438         PNDIS_BUFFER    pBuffer = NULL;
439         PNDIS_PACKET    Packet = pBufferDesc->pHolder;
440         pBufferDesc->pHolder = NULL;
441         NdisUnchainBufferAtFront(Packet, &pBuffer);
442         if (pBuffer)
443         {
444             NdisAdjustBufferLength(pBuffer, MaxNdisBufferDataSize(pContext, pBufferDesc));
445             NdisFreeBuffer(pBuffer);
446         }
447         NdisFreePacket(Packet);
448     }
449 }
450 
451 /**********************************************************
452 NDIS5-specific procedure to indicate received packets
453 
454 Parameters:
455     context
456     pIONetDescriptor pBuffersDescriptor - VirtIO buffer descriptor of data buffer
457     PVOID dataBuffer  - data buffer to pass to network stack
458     PULONG pLength - size of received packet.
459     BOOLEAN bPrepareOnly - only return NBL for further indication in batch
460 Return value:
461     TRUE  is packet indicated
462     FALSE if not (in this case, the descriptor should be freed now)
463 If priority header is in the packet. it will be removed and *pLength decreased
464 ***********************************************************/
465 tPacketIndicationType ParaNdis_IndicateReceivedPacket(
466     PARANDIS_ADAPTER *pContext,
467     PVOID dataBuffer,
468     PULONG pLength,
469     BOOLEAN bPrepareOnly,
470     pIONetDescriptor pBuffersDesc)
471 {
472     PNDIS_BUFFER    pBuffer = NULL;
473     PNDIS_BUFFER    pNoBuffer = NULL;
474     PNDIS_PACKET    Packet = pBuffersDesc->pHolder;
475     ULONG length = *pLength;
476     if (Packet) NdisUnchainBufferAtFront(Packet, &pBuffer);
477     if (Packet) NdisUnchainBufferAtFront(Packet, &pNoBuffer);
478     if (pBuffer)
479     {
480         UINT uTotalLength;
481         NDIS_PACKET_8021Q_INFO qInfo;
482         qInfo.Value = NULL;
483         if ((pContext->ulPriorityVlanSetting && length > (ETH_PRIORITY_HEADER_OFFSET + ETH_PRIORITY_HEADER_SIZE)) ||
484             length > pContext->MaxPacketSize.nMaxFullSizeOS)
485         {
486             PUCHAR pPriority = (PUCHAR)dataBuffer + ETH_PRIORITY_HEADER_OFFSET;
487             if (ETH_HAS_PRIO_HEADER(dataBuffer))
488             {
489                 if (IsPrioritySupported(pContext))
490                     qInfo.TagHeader.UserPriority = (pPriority[2] & 0xE0) >> 5;
491                 if (IsVlanSupported(pContext))
492                 {
493                     qInfo.TagHeader.VlanId = (((USHORT)(pPriority[2] & 0x0F)) << 8) | pPriority[3];
494                     if (pContext->VlanId && pContext->VlanId != qInfo.TagHeader.VlanId)
495                     {
496                         DPrintf(0, ("[%s] Failing unexpected VlanID %d", __FUNCTION__, qInfo.TagHeader.VlanId));
497                         pContext->extraStatistics.framesFilteredOut++;
498                         pBuffer = NULL;
499                     }
500                 }
501                 RtlMoveMemory(
502                     pPriority,
503                     pPriority + ETH_PRIORITY_HEADER_SIZE,
504                     length - ETH_PRIORITY_HEADER_OFFSET - ETH_PRIORITY_HEADER_SIZE);
505                 length -= ETH_PRIORITY_HEADER_SIZE;
506                 if (length > pContext->MaxPacketSize.nMaxFullSizeOS)
507                 {
508                     DPrintf(0, ("[%s] Can not indicate up packet of %d", __FUNCTION__, length));
509                     pBuffer = NULL;
510                 }
511                 DPrintf(1, ("[%s] Found priority data %p", __FUNCTION__, qInfo.Value));
512                 pContext->extraStatistics.framesRxPriority++;
513             }
514         }
515 
516         if (pBuffer)
517         {
518             PVOID headerBuffer = pContext->bUseMergedBuffers ? pBuffersDesc->DataInfo.Virtual:pBuffersDesc->HeaderInfo.Virtual;
519             virtio_net_hdr_basic *pHeader = (virtio_net_hdr_basic *)headerBuffer;
520             tChecksumCheckResult csRes;
521             NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo) = qInfo.Value;
522             NDIS_SET_PACKET_STATUS(Packet, STATUS_SUCCESS);
523             ParaNdis_PadPacketReceived(dataBuffer, &length);
524             NdisAdjustBufferLength(pBuffer, length);
525             NdisChainBufferAtFront(Packet, pBuffer);
526             NdisQueryPacket(Packet, NULL, NULL, NULL, &uTotalLength);
527             *REF_MINIPORT(Packet) = pBuffersDesc;
528             csRes = ParaNdis_CheckRxChecksum(pContext, pHeader->flags, dataBuffer, length);
529             if (csRes.value)
530             {
531                 NDIS_TCP_IP_CHECKSUM_PACKET_INFO qCSInfo;
532                 qCSInfo.Value = 0;
533                 qCSInfo.Receive.NdisPacketIpChecksumFailed = csRes.flags.IpFailed;
534                 qCSInfo.Receive.NdisPacketIpChecksumSucceeded = csRes.flags.IpOK;
535                 qCSInfo.Receive.NdisPacketTcpChecksumFailed = csRes.flags.TcpFailed;
536                 qCSInfo.Receive.NdisPacketTcpChecksumSucceeded = csRes.flags.TcpOK;
537                 qCSInfo.Receive.NdisPacketUdpChecksumFailed = csRes.flags.UdpFailed;
538                 qCSInfo.Receive.NdisPacketUdpChecksumSucceeded = csRes.flags.UdpOK;
539                 NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpIpChecksumPacketInfo) = (PVOID) (ULONG_PTR) qCSInfo.Value;
540                 DPrintf(1, ("Reporting CS %X->%X", csRes.value, qCSInfo.Value));
541             }
542 
543             DPrintf(4, ("[%s] buffer %p(%d b.)", __FUNCTION__, pBuffersDesc, length));
544             if (!bPrepareOnly)
545             {
546                 NdisMIndicateReceivePacket(
547                     pContext->MiniportHandle,
548                     &Packet,
549                     1);
550             }
551         }
552         *pLength = length;
553     }
554     if (!pBuffer)
555     {
556         DPrintf(0, ("[%s] Error: %p(%d b.) with packet %p", __FUNCTION__,
557             pBuffersDesc, length, Packet));
558         Packet = NULL;
559     }
560     if (pNoBuffer)
561     {
562         DPrintf(0, ("[%s] Error: %p(%d b.) with packet %p, buf %p,%p", __FUNCTION__,
563             pBuffersDesc, length, Packet, pBuffer, pNoBuffer));
564     }
565     return Packet;
566 }
567 
568 VOID ParaNdis_IndicateReceivedBatch(
569     PARANDIS_ADAPTER *pContext,
570     tPacketIndicationType *pBatch,
571     ULONG nofPackets)
572 {
573     NdisMIndicateReceivePacket(
574         pContext->MiniportHandle,
575         pBatch,
576         nofPackets);
577 }
578 
579 static FORCEINLINE void GET_NUMBER_OF_SG_ELEMENTS(PNDIS_PACKET Packet, UINT *pNum)
580 {
581     PSCATTER_GATHER_LIST pSGList;
582     pSGList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo);
583     if (pSGList)
584     {
585         *pNum = pSGList->NumberOfElements;
586     }
587 }
588 
589 /**********************************************************
590 Complete TX packets to NDIS with status, indicated inside packet
591 Parameters:
592     context
593     PNDIS_PACKET Packet     packet to complete
594 ***********************************************************/
595 static void CompletePacket(PARANDIS_ADAPTER *pContext, PNDIS_PACKET Packet)
596 {
597     LONG lRestToReturn;
598     NDIS_STATUS status = NDIS_GET_PACKET_STATUS(Packet);
599     lRestToReturn = NdisInterlockedDecrement(&pContext->NetTxPacketsToReturn);
600     ParaNdis_DebugHistory(pContext, hopSendComplete, Packet, 0, lRestToReturn, status);
601     NdisMSendComplete(pContext->MiniportHandle, Packet, status);
602 }
603 
604 /**********************************************************
605 Copy data from specified packet to VirtIO buffer, minimum 60 bytes
606 Parameters:
607     PNDIS_PACKET Packet     packet to copy data from
608     PVOID dest              destination to copy
609     ULONG maxSize           maximal size of destination
610 Return value:
611     size = number of bytes copied
612     if 0, the packet is not transmitted and should be dropped
613     ( should never happen)
614     request
615 ***********************************************************/
616 tCopyPacketResult ParaNdis_PacketCopier(
617     PNDIS_PACKET Packet, PVOID dest, ULONG maxSize, PVOID refValue, BOOLEAN bPreview)
618 {
619     PNDIS_BUFFER pBuffer;
620     ULONG PriorityDataLong = ((tSendEntry *)refValue)->PriorityDataLong;
621     tCopyPacketResult result;
622     /* the copier called also for getting Ethernet header
623        for statistics, when the transfer uses SG table */
624     UINT uLength = 0;
625     ULONG nCopied  = 0;
626     ULONG ulToCopy = 0;
627     if (bPreview) PriorityDataLong = 0;
628     NdisQueryPacket(Packet,
629                     NULL,
630                     NULL,
631                     &pBuffer,
632                     (PUINT)&ulToCopy);
633 
634     if (ulToCopy > maxSize) ulToCopy = bPreview ? maxSize : 0;
635     while (pBuffer && ulToCopy)
636     {
637         PVOID VirtualAddress = NULL;
638         NdisQueryBufferSafe(pBuffer,
639                             &VirtualAddress,
640                             &uLength,
641                             NormalPagePriority);
642         if (!VirtualAddress)
643         {
644             /* the packet copy failed */
645             nCopied = 0;
646             break;
647         }
648         if(uLength)
649         {
650             // Copy the data.
651             if (uLength > ulToCopy) uLength = ulToCopy;
652             ulToCopy -= uLength;
653             if ((PriorityDataLong & 0xFFFF) &&
654                 nCopied < ETH_PRIORITY_HEADER_OFFSET &&
655                 (nCopied + uLength) >= ETH_PRIORITY_HEADER_OFFSET)
656             {
657                 ULONG ulCopyNow = ETH_PRIORITY_HEADER_OFFSET - nCopied;
658                 NdisMoveMemory(dest, VirtualAddress, ulCopyNow);
659                 dest = (PUCHAR)dest + ulCopyNow;
660                 VirtualAddress = (PUCHAR)VirtualAddress + ulCopyNow;
661                 NdisMoveMemory(dest, &PriorityDataLong, 4);
662                 nCopied += 4;
663                 dest = (PCHAR)dest + 4;
664                 ulCopyNow = uLength - ulCopyNow;
665                 if (ulCopyNow) NdisMoveMemory(dest, VirtualAddress, ulCopyNow);
666                 dest = (PCHAR)dest + ulCopyNow;
667                 nCopied += uLength;
668             }
669             else
670             {
671                 NdisMoveMemory(dest, VirtualAddress, uLength);
672                 nCopied += uLength;
673                 dest = (PUCHAR)dest + uLength;
674             }
675         }
676         NdisGetNextBuffer(pBuffer, &pBuffer);
677     }
678 
679     DEBUG_EXIT_STATUS(4, nCopied);
680     result.size = nCopied;
681     return result;
682 }
683 
684 
685 /**********************************************************
686     Callback on finished Tx descriptor
687 ***********************************************************/
688 VOID ParaNdis_OnTransmitBufferReleased(PARANDIS_ADAPTER *pContext, IONetDescriptor *pDesc)
689 {
690     tSendEntry *pEntry = (tSendEntry *)pDesc->ReferenceValue;
691     if (pEntry)
692     {
693         DPrintf(2, ("[%s] Entry %p (packet %p, %d buffers) ready!", __FUNCTION__, pEntry, pEntry->packet, pDesc->nofUsedBuffers));
694         pEntry->flags |= SEND_ENTRY_FLAG_READY;
695         pDesc->ReferenceValue = NULL;
696         ParaNdis_DebugHistory(pContext, hopBufferSent, pEntry->packet, 0, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors);
697     }
698     else
699     {
700         ParaNdis_DebugHistory(pContext, hopBufferSent, NULL, 0, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors);
701         DPrintf(0, ("[%s] ERROR: Send Entry not set!", __FUNCTION__));
702     }
703 }
704 
705 
706 static FORCEINLINE ULONG CalculateTotalOffloadSize(
707     ULONG packetSize,
708     ULONG mss,
709     ULONG ipheaderOffset,
710     ULONG maxPossiblePacketSize,
711     tTcpIpPacketParsingResult packetReview)
712 {
713     ULONG ul = 0;
714     ULONG tcpipHeaders = packetReview.XxpIpHeaderSize;
715     ULONG allHeaders = tcpipHeaders + ipheaderOffset;
716     if (tcpipHeaders && (mss + allHeaders) <= maxPossiblePacketSize)
717     {
718         ULONG nFragments = (packetSize - allHeaders)/mss;
719         ULONG last = (packetSize - allHeaders)%mss;
720         ul = nFragments * (mss + allHeaders) + last + (last ? allHeaders : 0);
721     }
722     DPrintf(1, ("[%s]%s %d/%d, headers %d)",
723         __FUNCTION__, !ul ? "ERROR:" : "", ul, mss, allHeaders));
724     return ul;
725 }
726 
727 /**********************************************************
728 Maps the HW buffers of the packet into entries of VirtIO queue
729 Parameters:
730     miniport context
731     PNDIS_PACKET Packet     packet to copy data from
732     PVOID        ReferenceValue - tSendEntry * of the packet
733     VirtIOBufferDescriptor buffers = array of buffers to map packet buffers
734     (it contains number of SG entries >= number of hw elements in the packet)
735     pIONetDescriptor pDesc - holder of VirtIO header and reserved data buffer
736        for possible replacement of one or more HW buffers
737 
738 Returns @pMapperResult: (zeroed before call)
739     .usBuffersMapped - number of buffers mapped (one of them may be our own)
740     .ulDataSize - number of bytes to report as transmitted (802.1P tag is not counted)
741     .usBufferSpaceUsed - number of bytes used in data space of pIONetDescriptor pDesc
742 ***********************************************************/
743 VOID ParaNdis_PacketMapper(
744     PARANDIS_ADAPTER *pContext,
745     PNDIS_PACKET packet,
746     PVOID ReferenceValue,
747     struct VirtIOBufferDescriptor *buffers,
748     pIONetDescriptor pDesc,
749     tMapperResult *pMapperResult)
750 {
751     tSendEntry *pSendEntry = (tSendEntry *)ReferenceValue;
752     ULONG PriorityDataLong = pSendEntry->PriorityDataLong;
753     PSCATTER_GATHER_LIST pSGList = NDIS_PER_PACKET_INFO_FROM_PACKET(packet, ScatterGatherListPacketInfo);
754     SCATTER_GATHER_ELEMENT *pSGElements = pSGList->Elements;
755 
756 
757     if (pSGList && pSGList->NumberOfElements)
758     {
759         UINT i, lengthGet = 0, lengthPut = 0, nCompleteBuffersToSkip = 0, nBytesSkipInFirstBuffer = 0;
760         if (pSendEntry->flags & (SEND_ENTRY_TSO_USED | SEND_ENTRY_TCP_CS | SEND_ENTRY_UDP_CS | SEND_ENTRY_IP_CS))
761             lengthGet = pContext->Offload.ipHeaderOffset + MAX_IPV4_HEADER_SIZE + sizeof(TCPHeader);
762         if (PriorityDataLong && !lengthGet)
763             lengthGet = ETH_HEADER_SIZE;
764         if (lengthGet)
765         {
766             ULONG len = 0;
767             for (i = 0; i < pSGList->NumberOfElements; ++i)
768             {
769                 len += pSGElements[i].Length;
770                 if (len > lengthGet)
771                 {
772                     nBytesSkipInFirstBuffer = pSGList->Elements[i].Length - (len - lengthGet);
773                     break;
774                 }
775                 DPrintf(2, ("[%s] skipping buffer %d of %d", __FUNCTION__, nCompleteBuffersToSkip, pSGElements[i].Length));
776                 nCompleteBuffersToSkip++;
777             }
778             // just for case of UDP packet shorter than TCP header
779             if (lengthGet > len) lengthGet = len;
780             lengthPut = lengthGet + (PriorityDataLong ? ETH_PRIORITY_HEADER_SIZE : 0);
781         }
782 
783         if (lengthPut > pDesc->DataInfo.size)
784         {
785             DPrintf(0, ("[%s] ERROR: can not substitute %d bytes, sending as is", __FUNCTION__, lengthPut));
786             nCompleteBuffersToSkip = 0;
787             nBytesSkipInFirstBuffer = 0;
788             lengthGet = lengthPut = 0;
789         }
790 
791         if (lengthPut)
792         {
793             // we replace 1 or more HW buffers with one buffer preallocated for data
794             buffers->physAddr = pDesc->DataInfo.Physical;
795             buffers->length   = lengthPut;
796             pMapperResult->usBufferSpaceUsed = (USHORT)lengthPut;
797             pMapperResult->ulDataSize += lengthGet;
798             pMapperResult->usBuffersMapped = (USHORT)(pSGList->NumberOfElements - nCompleteBuffersToSkip + 1);
799             pSGElements += nCompleteBuffersToSkip;
800             buffers++;
801             DPrintf(1, ("[%s](%d bufs) skip %d buffers + %d bytes",
802                 __FUNCTION__, pSGList->NumberOfElements, nCompleteBuffersToSkip, nBytesSkipInFirstBuffer));
803         }
804         else
805         {
806             pMapperResult->usBuffersMapped = (USHORT)pSGList->NumberOfElements;
807         }
808 
809         for (i = nCompleteBuffersToSkip; i < pSGList->NumberOfElements; ++i)
810         {
811             if (nBytesSkipInFirstBuffer)
812             {
813                 buffers->physAddr.QuadPart = pSGElements->Address.QuadPart + nBytesSkipInFirstBuffer;
814                 buffers->length   = pSGElements->Length - nBytesSkipInFirstBuffer;
815                 DPrintf(2, ("[%s] using HW buffer %d of %d-%d", __FUNCTION__, i, pSGElements->Length, nBytesSkipInFirstBuffer));
816                 nBytesSkipInFirstBuffer = 0;
817             }
818             else
819             {
820                 buffers->physAddr = pSGElements->Address;
821                 buffers->length   = pSGElements->Length;
822             }
823             pMapperResult->ulDataSize += buffers->length;
824             pSGElements++;
825             buffers++;
826         }
827 
828         if (lengthPut)
829         {
830             PVOID pBuffer = pDesc->DataInfo.Virtual;
831             PVOID pIpHeader = RtlOffsetToPointer(pBuffer, pContext->Offload.ipHeaderOffset);
832             ParaNdis_PacketCopier(packet, pBuffer, lengthGet, ReferenceValue, TRUE);
833 
834             if (pSendEntry->flags & SEND_ENTRY_TSO_USED)
835             {
836                 tTcpIpPacketParsingResult packetReview;
837                 ULONG dummyTransferSize = 0;
838                 USHORT saveBuffers = pMapperResult->usBuffersMapped;
839                 ULONG flags = pcrIpChecksum | pcrTcpChecksum | pcrFixIPChecksum | pcrFixPHChecksum;
840                 pMapperResult->usBuffersMapped = 0;
841                 packetReview = ParaNdis_CheckSumVerify(
842                     pIpHeader,
843                     lengthGet - pContext->Offload.ipHeaderOffset,
844                     flags,
845                     __FUNCTION__);
846                 /* uncomment to verify */
847                 /*
848                 packetReview = ParaNdis_CheckSumVerify(
849                     pIpHeader,
850                     lengthGet - pContext->Offload.ipHeaderOffset,
851                     pcrIpChecksum | pcrTcpChecksum,
852                     __FUNCTION__);
853                 */
854                 if (packetReview.ipCheckSum == ppresCSOK || packetReview.fixedIpCS)
855                 {
856                     dummyTransferSize = CalculateTotalOffloadSize(
857                         pMapperResult->ulDataSize,
858                         pSendEntry->ipTransferUnit,
859                         pContext->Offload.ipHeaderOffset,
860                         pContext->MaxPacketSize.nMaxFullSizeOS,
861                         packetReview);
862                 }
863                 else
864                 {
865                     DPrintf(0, ("[%s] ERROR locating IP header in %d bytes(IP header of %d)", __FUNCTION__,
866                         lengthGet, packetReview.ipHeaderSize));
867                 }
868                 NDIS_PER_PACKET_INFO_FROM_PACKET(packet, TcpLargeSendPacketInfo) = (PVOID)(ULONG_PTR)dummyTransferSize;
869                 if (dummyTransferSize)
870                 {
871                     virtio_net_hdr_basic *pheader = pDesc->HeaderInfo.Virtual;
872                     unsigned short addPriorityLen = PriorityDataLong ? ETH_PRIORITY_HEADER_SIZE : 0;
873                     pheader->flags = VIRTIO_NET_HDR_F_NEEDS_CSUM;
874                     pheader->gso_type = VIRTIO_NET_HDR_GSO_TCPV4;
875                     pheader->hdr_len  = (USHORT)(packetReview.XxpIpHeaderSize + pContext->Offload.ipHeaderOffset) + addPriorityLen;
876                     pheader->gso_size = (USHORT)pSendEntry->ipTransferUnit;
877                     pheader->csum_start = (USHORT)pContext->Offload.ipHeaderOffset + (USHORT)packetReview.ipHeaderSize + addPriorityLen;
878                     pheader->csum_offset = TCP_CHECKSUM_OFFSET;
879                     pMapperResult->usBuffersMapped = saveBuffers;
880                 }
881             }
882             else if (pSendEntry->flags & SEND_ENTRY_IP_CS)
883             {
884                 ParaNdis_CheckSumVerify(
885                     pIpHeader,
886                     lengthGet - pContext->Offload.ipHeaderOffset,
887                     pcrIpChecksum | pcrFixIPChecksum,
888                     __FUNCTION__);
889             }
890 
891             if (PriorityDataLong && pMapperResult->usBuffersMapped)
892             {
893                 RtlMoveMemory(
894                     RtlOffsetToPointer(pBuffer, ETH_PRIORITY_HEADER_OFFSET + ETH_PRIORITY_HEADER_SIZE),
895                     RtlOffsetToPointer(pBuffer, ETH_PRIORITY_HEADER_OFFSET),
896                     lengthGet - ETH_PRIORITY_HEADER_OFFSET
897                     );
898                 NdisMoveMemory(
899                     RtlOffsetToPointer(pBuffer, ETH_PRIORITY_HEADER_OFFSET),
900                     &PriorityDataLong,
901                     sizeof(ETH_PRIORITY_HEADER_SIZE));
902                 DPrintf(1, ("[%s] Populated priority value %lX", __FUNCTION__, PriorityDataLong));
903             }
904         }
905     }
906 
907 }
908 
909 static void InitializeTransferParameters(tTxOperationParameters *pParams, tSendEntry *pEntry)
910 {
911     ULONG flags = (pEntry->flags & SEND_ENTRY_TSO_USED) ? pcrLSO : 0;
912     if (pEntry->flags & SEND_ENTRY_NO_INDIRECT) flags |= pcrNoIndirect;
913     NdisQueryPacket(pEntry->packet, &pParams->nofSGFragments, NULL, NULL, (PUINT)&pParams->ulDataSize);
914     pParams->ReferenceValue = pEntry;
915     pParams->packet = pEntry->packet;
916     pParams->offloadMss = (pEntry->flags & SEND_ENTRY_TSO_USED) ? pEntry->ipTransferUnit : 0;
917     // on NDIS5 it is unknown
918     pParams->tcpHeaderOffset = 0;
919     // fills only if SGList present in the packet
920     GET_NUMBER_OF_SG_ELEMENTS(pEntry->packet, &pParams->nofSGFragments);
921     if (NDIS_GET_PACKET_PROTOCOL_TYPE(pEntry->packet) == NDIS_PROTOCOL_ID_TCP_IP)
922     {
923         flags |= pcrIsIP;
924         if (pEntry->flags & SEND_ENTRY_TCP_CS)
925         {
926             flags |= pcrTcpChecksum;
927         }
928         if (pEntry->flags & SEND_ENTRY_UDP_CS)
929         {
930             flags |= pcrUdpChecksum;
931         }
932         if (pEntry->flags & SEND_ENTRY_IP_CS)
933         {
934             flags |= pcrIpChecksum;
935         }
936     }
937     if (pEntry->PriorityDataLong) flags |= pcrPriorityTag;
938     pParams->flags = flags;
939 }
940 
941 BOOLEAN ParaNdis_ProcessTx(
942     PARANDIS_ADAPTER *pContext,
943     BOOLEAN IsDpc,
944     BOOLEAN IsInterrupt)
945 {
946     LIST_ENTRY DoneList;
947     BOOLEAN bDoKick = FALSE;
948     UINT nBuffersSent = 0, nBytesSent = 0;
949     BOOLEAN bDataAvailable = FALSE;
950     tSendEntry *pEntry;
951     ONPAUSECOMPLETEPROC CallbackToCall = NULL;
952     InitializeListHead(&DoneList);
953     UNREFERENCED_PARAMETER(IsDpc);
954     NdisAcquireSpinLock(&pContext->SendLock);
955 
956     ParaNdis_DebugHistory(pContext, hopTxProcess, NULL, 1, pContext->nofFreeHardwareBuffers, pContext->nofFreeTxDescriptors);
957     do
958     {
959         if(IsTimeToReleaseTx(pContext))
960         {
961             // release some buffers
962             ParaNdis_VirtIONetReleaseTransmitBuffers(pContext);
963         }
964         pEntry = NULL;
965         if (!IsListEmpty(&pContext->SendQueue))
966         {
967             tCopyPacketResult result;
968             tTxOperationParameters Params;
969             pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue);
970             InitializeTransferParameters(&Params, pEntry);
971             bDataAvailable = TRUE;
972             result = ParaNdis_DoSubmitPacket(pContext, &Params);
973             if (result.error == cpeNoBuffer)
974             {
975                 // can not send now, try next time
976                 InsertHeadList(&pContext->SendQueue, &pEntry->list);
977                 pEntry = NULL;
978             }
979             else if (result.error == cpeNoIndirect)
980             {
981                 InsertHeadList(&pContext->SendQueue, &pEntry->list);
982                 pEntry->flags |= SEND_ENTRY_NO_INDIRECT;
983             }
984             else
985             {
986                 InsertTailList(&pContext->TxWaitingList, &pEntry->list);
987                 ParaNdis_DebugHistory(pContext, hopSubmittedPacket, pEntry->packet, 0, result.error, Params.flags);
988                 if (!result.size)
989                 {
990                     NDIS_STATUS status = NDIS_STATUS_FAILURE;
991                     DPrintf(0, ("[%s] ERROR %d copying packet!", __FUNCTION__, result.error));
992                     if (result.error == cpeTooLarge)
993                     {
994                         status = NDIS_STATUS_BUFFER_OVERFLOW;
995                         pContext->Statistics.ifOutErrors++;
996                     }
997                     NDIS_SET_PACKET_STATUS(pEntry->packet, status);
998                     pEntry->flags |= SEND_ENTRY_FLAG_READY;
999                     // do not worry, go to the next one
1000 
1001                 }
1002                 else
1003                 {
1004                     nBuffersSent++;
1005                     nBytesSent += result.size;
1006                     DPrintf(2, ("[%s] Scheduled packet %p, entry %p(%d bytes)!", __FUNCTION__,
1007                         pEntry->packet, pEntry, result.size));
1008                 }
1009             }
1010         }
1011     } while (pEntry);
1012 
1013     if (nBuffersSent)
1014     {
1015         if(IsInterrupt)
1016         {
1017             bDoKick = TRUE;
1018         }
1019         else
1020         {
1021 #ifdef PARANDIS_TEST_TX_KICK_ALWAYS
1022             virtqueue_kick_always(pContext->NetSendQueue);
1023 #else
1024             virtqueue_kick(pContext->NetSendQueue);
1025 #endif
1026         }
1027         DPrintf(2, ("[%s] sent down %d p.(%d b.)", __FUNCTION__, nBuffersSent, nBytesSent));
1028     }
1029     else if (bDataAvailable)
1030     {
1031         DPrintf(2, ("[%s] nothing sent", __FUNCTION__));
1032     }
1033 
1034     /* now check the waiting list of packets */
1035     while (!IsListEmpty(&pContext->TxWaitingList))
1036     {
1037         pEntry = (tSendEntry *)RemoveHeadList(&pContext->TxWaitingList);
1038         if (pEntry->flags & SEND_ENTRY_FLAG_READY)
1039         {
1040             InsertTailList(&DoneList, &pEntry->list);
1041         }
1042         else
1043         {
1044             InsertHeadList(&pContext->TxWaitingList, &pEntry->list);
1045             break;
1046         }
1047     }
1048 
1049     if (IsListEmpty(&pContext->TxWaitingList) && pContext->SendState == srsPausing && pContext->SendPauseCompletionProc)
1050     {
1051         CallbackToCall = pContext->SendPauseCompletionProc;
1052         pContext->SendPauseCompletionProc = NULL;
1053         pContext->SendState = srsDisabled;
1054         ParaNdis_DebugHistory(pContext, hopInternalSendPause, NULL, 0, 0, 0);
1055     }
1056     NdisReleaseSpinLock(&pContext->SendLock);
1057 
1058     while (!IsListEmpty(&DoneList))
1059     {
1060         pEntry = (tSendEntry *)RemoveHeadList(&DoneList);
1061         CompletePacket(pContext, pEntry->packet);
1062         NdisFreeMemory(pEntry, 0, 0);
1063     }
1064     if (CallbackToCall) CallbackToCall(pContext);
1065 
1066     return bDoKick;
1067 }
1068 
1069 /**********************************************************
1070 NDIS releases packets previously indicated by miniport
1071 Free the packet's buffer and the packet back to their pools
1072 Returns VirtIO buffer back to queue of free blocks
1073 Parameters:
1074     context
1075     IN PNDIS_PACKET Packet      returned packet
1076 ***********************************************************/
1077 VOID NTAPI ParaNdis5_ReturnPacket(IN NDIS_HANDLE  MiniportAdapterContext,IN PNDIS_PACKET Packet)
1078 {
1079     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
1080     pIONetDescriptor pBufferDescriptor;
1081     pBufferDescriptor = (pIONetDescriptor) *REF_MINIPORT(Packet);
1082     DPrintf(4, ("[%s] buffer %p", __FUNCTION__, pBufferDescriptor));
1083 
1084     NdisAcquireSpinLock(&pContext->ReceiveLock);
1085     pContext->ReuseBufferProc(pContext, pBufferDescriptor);
1086     NdisReleaseSpinLock(&pContext->ReceiveLock);
1087 }
1088 
1089 static __inline tSendEntry * PrepareSendEntry(PARANDIS_ADAPTER *pContext, PNDIS_PACKET Packet, ULONG len)
1090 {
1091     ULONG mss = (ULONG)(ULONG_PTR)NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpLargeSendPacketInfo);
1092     UINT  protocol = NDIS_GET_PACKET_PROTOCOL_TYPE(Packet);
1093     LPCSTR errorFmt = NULL;
1094     LPCSTR offloadName = "NO offload";
1095     tSendEntry *pse = (tSendEntry *)ParaNdis_AllocateMemory(pContext, sizeof(tSendEntry));
1096     if (pse)
1097     {
1098         NDIS_PACKET_8021Q_INFO qInfo;
1099         pse->packet = Packet;
1100         pse->flags  = 0;
1101         pse->PriorityDataLong = 0;
1102         pse->ipTransferUnit = len;
1103         //pse->fullTCPCheckSum = 0;
1104         qInfo.Value = pContext->ulPriorityVlanSetting ?
1105             NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo) : NULL;
1106         if (!qInfo.TagHeader.VlanId) qInfo.TagHeader.VlanId = pContext->VlanId;
1107         if (qInfo.TagHeader.CanonicalFormatId || !IsValidVlanId(pContext, qInfo.TagHeader.VlanId))
1108         {
1109             DPrintf(0, ("[%s] Discarding priority tag %p", __FUNCTION__, qInfo.Value));
1110             errorFmt = "invalid priority tag";
1111         }
1112         else if (qInfo.Value)
1113         {
1114             // ignore priority, if configured
1115             if (!IsPrioritySupported(pContext))
1116                 qInfo.TagHeader.UserPriority = 0;
1117             // ignore VlanId, if specified
1118             if (!IsVlanSupported(pContext))
1119                 qInfo.TagHeader.VlanId = 0;
1120             SetPriorityData(pse->PriorityData, qInfo.TagHeader.UserPriority, qInfo.TagHeader.VlanId);
1121             DPrintf(1, ("[%s] Populated priority tag %p", __FUNCTION__, qInfo.Value));
1122         }
1123 
1124         if (!errorFmt && !mss && len > pContext->MaxPacketSize.nMaxFullSizeOS)
1125         {
1126             DPrintf(0, ("[%s] Request for offload with NO MSS, lso %d, ipheader %d",
1127                 __FUNCTION__, pContext->Offload.flags.fTxLso, pContext->Offload.ipHeaderOffset));
1128             if (pContext->Offload.flags.fTxLso && pContext->Offload.ipHeaderOffset)
1129             {
1130                 mss = pContext->MaxPacketSize.nMaxFullSizeOS;
1131             }
1132             else
1133                 errorFmt = "illegal LSO request";
1134         }
1135 
1136         if (errorFmt)
1137         {
1138             // already failed
1139         }
1140         else if (mss > pContext->MaxPacketSize.nMaxFullSizeOS)
1141             errorFmt = "mss is too big";
1142         else if (len > 0xFFFF)
1143             errorFmt = "packet is bigger than we able to send";
1144         else if (mss && pContext->Offload.flags.fTxLso)
1145         {
1146             offloadName = "LSO";
1147             pse->ipTransferUnit = mss;
1148             pse->flags |= SEND_ENTRY_TSO_USED;
1149             // todo: move to common space
1150             // to transmit 'len' with 'mss' we usually need 2 additional buffers
1151             if ((len / mss + 3) > pContext->maxFreeHardwareBuffers)
1152                 errorFmt = "packet too big to fragment";
1153             else if (len < pContext->Offload.ipHeaderOffset)
1154                 errorFmt = "ip offset is bigger than packet";
1155             else if (protocol != NDIS_PROTOCOL_ID_TCP_IP)
1156                 errorFmt = "attempt to offload non-IP packet";
1157             else if (mss < pContext->Offload.ipHeaderOffset)
1158                 errorFmt = "mss is too small";
1159         }
1160         else
1161         {
1162             // unexpected CS requests we do not fail - WHQL expects us to send them as is
1163             NDIS_TCP_IP_CHECKSUM_PACKET_INFO csInfo;
1164             csInfo.Value = (ULONG)(ULONG_PTR)NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpIpChecksumPacketInfo);
1165             if (csInfo.Transmit.NdisPacketChecksumV4)
1166             {
1167                 if (csInfo.Transmit.NdisPacketTcpChecksum)
1168                 {
1169                     offloadName = "TCP CS";
1170                     if (pContext->Offload.flags.fTxTCPChecksum)
1171                         pse->flags |= SEND_ENTRY_TCP_CS;
1172                     else
1173                         errorFmt = "TCP CS requested but not enabled";
1174                 }
1175                 if (csInfo.Transmit.NdisPacketUdpChecksum)
1176                 {
1177                     offloadName = "UDP CS";
1178                     if (pContext->Offload.flags.fTxUDPChecksum)
1179                         pse->flags |= SEND_ENTRY_UDP_CS;
1180                     else
1181                         errorFmt = "UDP CS requested but not enabled";
1182                 }
1183                 if (csInfo.Transmit.NdisPacketIpChecksum)
1184                 {
1185                     if (pContext->Offload.flags.fTxIPChecksum)
1186                         pse->flags |= SEND_ENTRY_IP_CS;
1187                     else
1188                         errorFmt = "IP CS requested but not enabled";
1189                 }
1190                 if (errorFmt)
1191                 {
1192                     DPrintf(0, ("[%s] ERROR: %s (len %d)", __FUNCTION__, errorFmt, len));
1193                     errorFmt = NULL;
1194                 }
1195             }
1196         }
1197     }
1198 
1199     if (errorFmt)
1200     {
1201         DPrintf(0, ("[%s] ERROR: %s (len %d, mss %d)", __FUNCTION__, errorFmt, len, mss));
1202         if (pse) NdisFreeMemory(pse, 0, 0);
1203         pse = NULL;
1204     }
1205     else
1206     {
1207         NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, TcpLargeSendPacketInfo) = (PVOID)(ULONG_PTR)0;
1208         DPrintf(1, ("[%s] Sending packet of %d with %s", __FUNCTION__, len, offloadName));
1209         if (pContext->bDoIPCheckTx)
1210         {
1211             tTcpIpPacketParsingResult res;
1212             VOID *pcopy = ParaNdis_AllocateMemory(pContext, len);
1213             ParaNdis_PacketCopier(pse->packet, pcopy, len, pse, TRUE);
1214             res = ParaNdis_CheckSumVerify(
1215                 RtlOffsetToPointer(pcopy, pContext->Offload.ipHeaderOffset),
1216                 len,
1217                 pcrAnyChecksum/* | pcrFixAnyChecksum*/,
1218                 __FUNCTION__);
1219             /*
1220             if (res.xxpStatus == ppresXxpKnown)
1221             {
1222                 TCPHeader *ptcp = (TCPHeader *)
1223                     RtlOffsetToPointer(pcopy, pContext->Offload.ipHeaderOffset + res.ipHeaderSize);
1224                 pse->fullTCPCheckSum = ptcp->tcp_xsum;
1225             }
1226             */
1227             NdisFreeMemory(pcopy, 0, 0);
1228         }
1229     }
1230     return pse;
1231 }
1232 
1233 /**********************************************************
1234 NDIS sends us packets
1235     Queues packets internally and calls the procedure to process the queue
1236 
1237 Parameters:
1238     context
1239     IN PPNDIS_PACKET PacketArray        Array of packets to send
1240     IN UINT NumberOfPackets             number of packets
1241 
1242 ***********************************************************/
1243 VOID NTAPI ParaNdis5_SendPackets(IN NDIS_HANDLE MiniportAdapterContext,
1244                                IN PPNDIS_PACKET PacketArray,
1245                                IN UINT NumberOfPackets)
1246 {
1247     UINT i;
1248     LIST_ENTRY FailedList, DoneList;
1249     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
1250     InitializeListHead(&FailedList);
1251     InitializeListHead(&DoneList);
1252     DPrintf(3, ("[%s] %d packets", __FUNCTION__, NumberOfPackets));
1253     ParaNdis_DebugHistory(pContext, hopSend, NULL, 1, NumberOfPackets, 0);
1254 
1255     NdisAcquireSpinLock(&pContext->SendLock);
1256 
1257     for (i = 0; i < NumberOfPackets; ++i)
1258     {
1259         UINT uPacketLength = 0;
1260         NdisQueryPacketLength(PacketArray[i], &uPacketLength);
1261         NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_SUCCESS);
1262         NdisInterlockedIncrement(&pContext->NetTxPacketsToReturn);
1263         if (!pContext->bSurprizeRemoved && pContext->bConnected && pContext->SendState == srsEnabled && uPacketLength)
1264         {
1265             tSendEntry *pse = PrepareSendEntry(pContext, PacketArray[i], uPacketLength);
1266             if (!pse)
1267             {
1268                 NDIS_SET_PACKET_STATUS(PacketArray[i], NDIS_STATUS_FAILURE);
1269                 CompletePacket(pContext, PacketArray[i]);
1270             }
1271             else
1272             {
1273                 UINT nFragments = 0;
1274                 GET_NUMBER_OF_SG_ELEMENTS(PacketArray[i], &nFragments);
1275                 ParaNdis_DebugHistory(pContext, hopSendPacketMapped, PacketArray[i], 0, nFragments, 0);
1276                 InsertTailList(&pContext->SendQueue, &pse->list);
1277             }
1278         }
1279         else
1280         {
1281             NDIS_STATUS status = NDIS_STATUS_FAILURE;
1282             if (pContext->bSurprizeRemoved) status = NDIS_STATUS_NOT_ACCEPTED;
1283             NDIS_SET_PACKET_STATUS(PacketArray[i], status);
1284             CompletePacket(pContext, PacketArray[i]);
1285             DPrintf(1, ("[%s] packet of %d rejected", __FUNCTION__, uPacketLength));
1286         }
1287     }
1288 
1289     NdisReleaseSpinLock(&pContext->SendLock);
1290 
1291     ParaNdis_ProcessTx(pContext, FALSE, FALSE);
1292 }
1293 
1294 /**********************************************************
1295 NDIS procedure, not easy to test
1296 NDIS asks us to cancel packets with specified CancelID
1297 
1298 Parameters:
1299     context
1300     PVOID CancelId              ID to cancel
1301 
1302 ***********************************************************/
1303 VOID NTAPI ParaNdis5_CancelSendPackets(IN NDIS_HANDLE MiniportAdapterContext,IN PVOID CancelId)
1304 {
1305     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
1306     LIST_ENTRY DoneList, KeepList;
1307     UINT n = 0;
1308     tSendEntry *pEntry;
1309     DEBUG_ENTRY(0);
1310     InitializeListHead(&DoneList);
1311     InitializeListHead(&KeepList);
1312     NdisAcquireSpinLock(&pContext->SendLock);
1313     while ( !IsListEmpty(&pContext->SendQueue))
1314     {
1315         PNDIS_PACKET  Packet;
1316         pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue);
1317         Packet = pEntry->packet;
1318         if (NDIS_GET_PACKET_CANCEL_ID(Packet) == CancelId)
1319         {
1320             InsertTailList(&DoneList, &pEntry->list);
1321             ++n;
1322         }
1323         else InsertTailList(&KeepList, &pEntry->list);
1324     }
1325     while ( !IsListEmpty(&KeepList))
1326     {
1327         pEntry = (tSendEntry *)RemoveHeadList(&KeepList);
1328         InsertTailList(&pContext->SendQueue, &pEntry->list);
1329     }
1330     NdisReleaseSpinLock(&pContext->SendLock);
1331     while (!IsListEmpty(&DoneList))
1332     {
1333         pEntry = (tSendEntry *)RemoveHeadList(&DoneList);
1334         NDIS_SET_PACKET_STATUS(pEntry->packet, NDIS_STATUS_REQUEST_ABORTED);
1335         CompletePacket(pContext, pEntry->packet);
1336         NdisFreeMemory(pEntry, 0, 0);
1337     }
1338     DEBUG_EXIT_STATUS(0, n);
1339 }
1340 
1341 /**********************************************************
1342 Request to pause or resume data transmit
1343 if stopped, all the packets in internal queue are returned
1344 Parameters:
1345     context
1346     BOOLEAN bStop       1/0 - top or resume
1347 ***********************************************************/
1348 NDIS_STATUS ParaNdis5_StopSend(PARANDIS_ADAPTER *pContext, BOOLEAN bStop, ONPAUSECOMPLETEPROC Callback)
1349 {
1350     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1351     if (bStop)
1352     {
1353         LIST_ENTRY DoneList;
1354         tSendEntry *pEntry;
1355         DEBUG_ENTRY(0);
1356         ParaNdis_DebugHistory(pContext, hopInternalSendPause, NULL, 1, 0, 0);
1357         InitializeListHead(&DoneList);
1358         NdisAcquireSpinLock(&pContext->SendLock);
1359         if (IsListEmpty(&pContext->TxWaitingList))
1360         {
1361             pContext->SendState = srsDisabled;
1362             while (!IsListEmpty(&pContext->SendQueue))
1363             {
1364                 pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue);
1365                 InsertTailList(&DoneList, &pEntry->list);
1366             }
1367             ParaNdis_DebugHistory(pContext, hopInternalSendPause, NULL, 0, 0, 0);
1368         }
1369         else
1370         {
1371             pContext->SendState = srsPausing;
1372             pContext->SendPauseCompletionProc = Callback;
1373             status = NDIS_STATUS_PENDING;
1374             while (!IsListEmpty(&pContext->SendQueue))
1375             {
1376                 pEntry = (tSendEntry *)RemoveHeadList(&pContext->SendQueue);
1377                 pEntry->flags |= SEND_ENTRY_FLAG_READY;
1378                 InsertTailList(&pContext->TxWaitingList, &pEntry->list);
1379             }
1380         }
1381 
1382         NdisReleaseSpinLock(&pContext->SendLock);
1383         while (!IsListEmpty(&DoneList))
1384         {
1385             pEntry = (tSendEntry *)RemoveHeadList(&DoneList);
1386             NDIS_SET_PACKET_STATUS(pEntry->packet, NDIS_STATUS_REQUEST_ABORTED);
1387             CompletePacket(pContext, pEntry->packet);
1388             NdisFreeMemory(pEntry, 0, 0);
1389         }
1390     }
1391     else
1392     {
1393         pContext->SendState = srsEnabled;
1394         ParaNdis_DebugHistory(pContext, hopInternalSendResume, NULL, 0, 0, 0);
1395     }
1396     return status;
1397 }
1398 
1399 /**********************************************************
1400 Pause or resume receive operation:
1401 Parameters:
1402     context
1403     BOOLEAN bStop                       1/0 - pause or resume
1404     ONPAUSECOMPLETEPROC Callback        callback to call, if not completed immediately
1405 
1406 Return value:
1407     SUCCESS, if there is no RX packets under NDIS management
1408     PENDING, if we need to wait until NDIS returns us packets
1409 ***********************************************************/
1410 NDIS_STATUS ParaNdis5_StopReceive(
1411     PARANDIS_ADAPTER *pContext,
1412     BOOLEAN bStop,
1413     ONPAUSECOMPLETEPROC Callback
1414     )
1415 {
1416     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
1417     if (bStop)
1418     {
1419         ParaNdis_DebugHistory(pContext, hopInternalReceivePause, NULL, 1, 0, 0);
1420         NdisAcquireSpinLock(&pContext->ReceiveLock);
1421         if (IsListEmpty(&pContext->NetReceiveBuffersWaiting))
1422         {
1423             pContext->ReceiveState = srsDisabled;
1424             ParaNdis_DebugHistory(pContext, hopInternalReceivePause, NULL, 0, 0, 0);
1425         }
1426         else
1427         {
1428             pContext->ReceiveState = srsPausing;
1429             pContext->ReceivePauseCompletionProc = Callback;
1430             status = NDIS_STATUS_PENDING;
1431         }
1432         NdisReleaseSpinLock(&pContext->ReceiveLock);
1433     }
1434     else
1435     {
1436         pContext->ReceiveState = srsEnabled;
1437         ParaNdis_DebugHistory(pContext, hopInternalReceiveResume, NULL, 0, 0, 0);
1438     }
1439     return status;
1440 }
1441 
1442 /*************************************************************
1443 Required NDIS procedure, spawns regular (Common) DPC processing
1444 *************************************************************/
1445 VOID NTAPI ParaNdis5_HandleDPC(IN NDIS_HANDLE MiniportAdapterContext)
1446 {
1447     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
1448     ULONG requiresProcessing;
1449     BOOLEAN unused;
1450     DEBUG_ENTRY(7);
1451     // we do not need the timer, as DPC will do all the job
1452     // this is not a problem if the timer procedure is already running,
1453     // we need to do our job anyway
1454     NdisCancelTimer(&pContext->DPCPostProcessTimer, &unused);
1455     requiresProcessing = ParaNdis_DPCWorkBody(pContext, PARANDIS_UNLIMITED_PACKETS_TO_INDICATE);
1456     if (requiresProcessing)
1457     {
1458         // we need to request additional DPC
1459         InterlockedOr(&pContext->InterruptStatus, requiresProcessing);
1460         NdisSetTimer(&pContext->DPCPostProcessTimer, 10);
1461     }
1462 }
1463 
1464 BOOLEAN ParaNdis_SynchronizeWithInterrupt(
1465     PARANDIS_ADAPTER *pContext,
1466     ULONG messageId,
1467     tSynchronizedProcedure procedure,
1468     PVOID parameter)
1469 {
1470     tSynchronizedContext SyncContext;
1471     SyncContext.pContext  = pContext;
1472     SyncContext.Parameter = parameter;
1473     return NdisMSynchronizeWithInterrupt(&pContext->Interrupt, procedure, &SyncContext);
1474 }
1475