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