xref: /reactos/drivers/network/dd/dc21x4/send.c (revision 9cfd8dd9)
1 /*
2  * PROJECT:     ReactOS DC21x4 Driver
3  * LICENSE:     GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:     Packet sending
5  * COPYRIGHT:   Copyright 2023 Dmitry Borisov <di.sean@protonmail.com>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "dc21x4.h"
11 
12 #include <debug.h>
13 
14 /* FUNCTIONS ******************************************************************/
15 
16 static
17 VOID
18 DcTransmitPacket(
19     _In_ PDC21X4_ADAPTER Adapter,
20     _In_ PDC_TCB Tcb,
21     _In_ PSCATTER_GATHER_LIST SgList)
22 {
23     PDC_TBD Tbd, NextTbd, FirstTbd, LastTbd;
24     ULONG i, TbdStatus;
25 
26     TbdStatus = 0;
27     Tbd = Adapter->CurrentTbd;
28 
29     for (i = 0; i < SgList->NumberOfElements; ++i)
30     {
31         ULONG Address, Length;
32 
33         LastTbd = Tbd;
34 
35         /* Not owned by NIC */
36         ASSERT(!(((i % 2) == 0) && (Tbd->Status & DC_TBD_STATUS_OWNED)));
37 
38         Tbd->Status = TbdStatus;
39 
40         /* 32-bit DMA */
41         ASSERT(SgList->Elements[i].Address.HighPart == 0);
42 
43         Address = SgList->Elements[i].Address.LowPart;
44         Length = SgList->Elements[i].Length;
45 
46         /* Two data buffers per descriptor */
47         if ((i % 2) == 0)
48         {
49             Tbd->Control &= DC_TBD_CONTROL_END_OF_RING;
50 
51             Tbd->Control |= Length;
52             Tbd->Address1 = Address;
53 
54             NextTbd = DC_NEXT_TBD(Adapter, Tbd);
55         }
56         else
57         {
58             Tbd->Control |= Length << DC_TBD_CONTROL_LENGTH_2_SHIFT;
59             Tbd->Address2 = Address;
60 
61             Tbd = NextTbd;
62             TbdStatus = DC_TBD_STATUS_OWNED;
63         }
64     }
65 
66     /* Enable IRQ on last element */
67     LastTbd->Control |= DC_TBD_CONTROL_LAST_FRAGMENT | DC_TBD_CONTROL_REQUEST_INTERRUPT;
68 
69     Tcb->Tbd = LastTbd;
70 
71     FirstTbd = Adapter->CurrentTbd;
72     Adapter->CurrentTbd = NextTbd;
73 
74     /* Not owned by NIC */
75     ASSERT(!(FirstTbd->Status & DC_TBD_STATUS_OWNED));
76 
77     FirstTbd->Control |= DC_TBD_CONTROL_FIRST_FRAGMENT;
78     DC_WRITE_BARRIER();
79     FirstTbd->Status = DC_TBD_STATUS_OWNED;
80 }
81 
82 static
83 BOOLEAN
84 DcCopyPacket(
85     _In_ PDC21X4_ADAPTER Adapter,
86     _In_ PNDIS_PACKET Packet,
87     _In_ PDC_COALESCE_BUFFER Buffer)
88 {
89     PNDIS_BUFFER CurrentBuffer;
90     PVOID Address;
91     UINT CurrentLength, PacketLength;
92     PUCHAR Destination;
93 
94     NdisGetFirstBufferFromPacketSafe(Packet,
95                                      &CurrentBuffer,
96                                      &Address,
97                                      &CurrentLength,
98                                      &PacketLength,
99                                      HighPagePriority);
100     if (!Address)
101         return FALSE;
102 
103     Destination = Buffer->VirtualAddress;
104 
105     while (TRUE)
106     {
107         NdisMoveMemory(Destination, Address, CurrentLength);
108         Destination += CurrentLength;
109 
110         NdisGetNextBuffer(CurrentBuffer, &CurrentBuffer);
111         if (!CurrentBuffer)
112             break;
113 
114         NdisQueryBufferSafe(CurrentBuffer,
115                             &Address,
116                             &CurrentLength,
117                             HighPagePriority);
118         if (!Address)
119             return FALSE;
120     }
121 
122     return TRUE;
123 }
124 
125 static
126 NDIS_STATUS
127 DcSendPacket(
128     _In_ NDIS_HANDLE MiniportAdapterContext,
129     _In_ PNDIS_PACKET Packet)
130 {
131     PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
132     PSCATTER_GATHER_LIST SgList;
133     PDC_TCB Tcb;
134     ULONG SlotsUsed;
135 
136     if (!Adapter->TcbSlots)
137         return NDIS_STATUS_RESOURCES;
138 
139     SgList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, ScatterGatherListPacketInfo);
140 
141     if (SgList->NumberOfElements > DC_FRAGMENTATION_THRESHOLD)
142     {
143         PDC_COALESCE_BUFFER CoalesceBuffer;
144         UINT PacketLength;
145 
146         if (!Adapter->TbdSlots || !Adapter->SendBufferList.Next)
147         {
148             return NDIS_STATUS_RESOURCES;
149         }
150 
151         NdisQueryPacketLength(Packet, &PacketLength);
152 
153         CoalesceBuffer = (PDC_COALESCE_BUFFER)PopEntryList(&Adapter->SendBufferList);
154 
155         if (!DcCopyPacket(Adapter, Packet, CoalesceBuffer))
156         {
157             PushEntryList(&Adapter->SendBufferList, &CoalesceBuffer->ListEntry);
158             return NDIS_STATUS_RESOURCES;
159         }
160 
161         SgList = &Adapter->LocalSgList;
162         SgList->Elements[0].Address.LowPart = CoalesceBuffer->PhysicalAddress;
163         SgList->Elements[0].Length = PacketLength;
164         SgList->NumberOfElements = 1;
165         SlotsUsed = 1;
166 
167         Tcb = Adapter->CurrentTcb;
168         Tcb->SlotsUsed = 1;
169         Tcb->Buffer = CoalesceBuffer;
170     }
171     else
172     {
173         /* We use two data buffers per descriptor */
174         SlotsUsed = (SgList->NumberOfElements + 1) / 2;
175 
176         if (SlotsUsed > Adapter->TbdSlots)
177             return NDIS_STATUS_RESOURCES;
178 
179         Tcb = Adapter->CurrentTcb;
180         Tcb->SlotsUsed = SlotsUsed;
181         Tcb->Buffer = NULL;
182     }
183 
184     --Adapter->TcbSlots;
185 
186     Adapter->CurrentTcb = DC_NEXT_TCB(Adapter, Tcb);
187 
188     Tcb->Packet = Packet;
189 
190     ASSERT(Adapter->TbdSlots >= Tcb->SlotsUsed);
191     Adapter->TbdSlots -= SlotsUsed;
192 
193     DcTransmitPacket(Adapter, Tcb, SgList);
194 
195     DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL);
196 
197     return NDIS_STATUS_PENDING;
198 }
199 
200 VOID
201 DcProcessPendingPackets(
202     _In_ PDC21X4_ADAPTER Adapter)
203 {
204     PLIST_ENTRY Entry;
205     NDIS_STATUS Status;
206     PNDIS_PACKET Packet;
207 
208     ASSERT(!IsListEmpty(&Adapter->SendQueueList));
209 
210     do
211     {
212         Entry = RemoveHeadList(&Adapter->SendQueueList);
213 
214         Packet = DC_PACKET_FROM_LIST_ENTRY(Entry);
215 
216         Status = DcSendPacket(Adapter, Packet);
217         if (Status == NDIS_STATUS_RESOURCES)
218         {
219             InsertHeadList(&Adapter->SendQueueList, DC_LIST_ENTRY_FROM_PACKET(Packet));
220             break;
221         }
222     }
223     while (!IsListEmpty(&Adapter->SendQueueList));
224 }
225 
226 VOID
227 NTAPI
228 DcSendPackets(
229     _In_ NDIS_HANDLE MiniportAdapterContext,
230     _In_ PPNDIS_PACKET PacketArray,
231     _In_ UINT NumberOfPackets)
232 {
233     PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
234     NDIS_STATUS Status;
235     ULONG i;
236 
237     NdisAcquireSpinLock(&Adapter->SendLock);
238 
239     if (!(Adapter->Flags & DC_ACTIVE))
240     {
241         NdisReleaseSpinLock(&Adapter->SendLock);
242 
243         for (i = 0; i < NumberOfPackets; ++i)
244         {
245             NdisMSendComplete(Adapter->AdapterHandle,
246                               PacketArray[i],
247                               NDIS_STATUS_NOT_ACCEPTED);
248         }
249 
250         return;
251     }
252 
253     TRACE("Send packets %u\n", NumberOfPackets);
254 
255     for (i = 0; i < NumberOfPackets; ++i)
256     {
257         PNDIS_PACKET Packet = PacketArray[i];
258 
259         Status = DcSendPacket(Adapter, Packet);
260         if (Status == NDIS_STATUS_RESOURCES)
261         {
262             InsertTailList(&Adapter->SendQueueList, DC_LIST_ENTRY_FROM_PACKET(Packet));
263         }
264     }
265 
266     NdisReleaseSpinLock(&Adapter->SendLock);
267 }
268 
269 VOID
270 NTAPI
271 DcCancelSendPackets(
272     _In_ NDIS_HANDLE MiniportAdapterContext,
273     _In_ PVOID CancelId)
274 {
275     PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext;
276     LIST_ENTRY DoneList;
277     PLIST_ENTRY Entry, NextEntry;
278 
279     TRACE("Called\n");
280 
281     InitializeListHead(&DoneList);
282 
283     NdisAcquireSpinLock(&Adapter->SendLock);
284 
285     NextEntry = Adapter->SendQueueList.Flink;
286     while (NextEntry != &Adapter->SendQueueList)
287     {
288         PNDIS_PACKET Packet;
289 
290         Entry = NextEntry;
291         NextEntry = NextEntry->Flink;
292 
293         Packet = DC_PACKET_FROM_LIST_ENTRY(Entry);
294 
295         if (NDIS_GET_PACKET_CANCEL_ID(Packet) == CancelId)
296         {
297             RemoveEntryList(DC_LIST_ENTRY_FROM_PACKET(Packet));
298 
299             InsertTailList(&DoneList, DC_LIST_ENTRY_FROM_PACKET(Packet));
300         }
301     }
302 
303     NdisReleaseSpinLock(&Adapter->SendLock);
304 
305     while (!IsListEmpty(&DoneList))
306     {
307         Entry = RemoveHeadList(&DoneList);
308 
309         NdisMSendComplete(Adapter->AdapterHandle,
310                           DC_PACKET_FROM_LIST_ENTRY(Entry),
311                           NDIS_STATUS_REQUEST_ABORTED);
312     }
313 }
314