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
DcTransmitPacket(_In_ PDC21X4_ADAPTER Adapter,_In_ PDC_TCB Tcb,_In_ PSCATTER_GATHER_LIST SgList)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
DcCopyPacket(_In_ PDC21X4_ADAPTER Adapter,_In_ PNDIS_PACKET Packet,_In_ PDC_COALESCE_BUFFER Buffer)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
DcSendPacket(_In_ NDIS_HANDLE MiniportAdapterContext,_In_ PNDIS_PACKET Packet)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
DcProcessPendingPackets(_In_ PDC21X4_ADAPTER Adapter)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
DcSendPackets(_In_ NDIS_HANDLE MiniportAdapterContext,_In_ PPNDIS_PACKET PacketArray,_In_ UINT NumberOfPackets)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
DcCancelSendPackets(_In_ NDIS_HANDLE MiniportAdapterContext,_In_ PVOID CancelId)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