1 /* 2 * PROJECT: ReactOS DC21x4 Driver 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Miniport driver entry 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 ULONG 17 DcEthernetCrc( 18 _In_reads_bytes_(Size) const VOID* Buffer, 19 _In_ ULONG Size) 20 { 21 ULONG i, j, Crc; 22 const UCHAR* Data = Buffer; 23 24 Crc = 0xFFFFFFFF; 25 for (i = 0; i < Size; ++i) 26 { 27 Crc ^= Data[i]; 28 for (j = 8; j > 0; j--) 29 { 30 /* CRC-32 polynomial little-endian */ 31 Crc = (Crc >> 1) ^ (-(LONG)(Crc & 1) & 0xEDB88320); 32 } 33 } 34 35 return Crc; 36 } 37 38 static 39 VOID 40 DcFlushTransmitQueue( 41 _In_ PDC21X4_ADAPTER Adapter) 42 { 43 LIST_ENTRY DoneList; 44 PLIST_ENTRY Entry; 45 PNDIS_PACKET Packet; 46 PDC_TCB Tcb; 47 48 InitializeListHead(&DoneList); 49 50 NdisAcquireSpinLock(&Adapter->SendLock); 51 52 /* Remove pending transmissions from the transmit ring */ 53 for (Tcb = Adapter->LastTcb; 54 Tcb != Adapter->CurrentTcb; 55 Tcb = DC_NEXT_TCB(Adapter, Tcb)) 56 { 57 Packet = Tcb->Packet; 58 59 if (!Packet) 60 continue; 61 62 InsertTailList(&DoneList, DC_LIST_ENTRY_FROM_PACKET(Packet)); 63 64 DC_RELEASE_TCB(Adapter, Tcb); 65 } 66 Adapter->CurrentTcb = Tcb; 67 68 /* Remove pending transmissions from the internal queue */ 69 while (!IsListEmpty(&Adapter->SendQueueList)) 70 { 71 Entry = RemoveHeadList(&Adapter->SendQueueList); 72 73 InsertTailList(&DoneList, Entry); 74 } 75 76 NdisReleaseSpinLock(&Adapter->SendLock); 77 78 while (!IsListEmpty(&DoneList)) 79 { 80 Entry = RemoveHeadList(&DoneList); 81 82 NdisMSendComplete(Adapter->AdapterHandle, 83 DC_PACKET_FROM_LIST_ENTRY(Entry), 84 NDIS_STATUS_FAILURE); 85 } 86 } 87 88 static 89 VOID 90 DcStopReceivePath( 91 _In_ PDC21X4_ADAPTER Adapter) 92 { 93 BOOLEAN RxStopped; 94 95 ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL); 96 97 #if DBG 98 NdisAcquireSpinLock(&Adapter->ReceiveLock); 99 if (Adapter->RcbFree != Adapter->RcbCount) 100 { 101 INFO("RX packets: %u/%u\n", Adapter->RcbFree, Adapter->RcbCount); 102 } 103 NdisReleaseSpinLock(&Adapter->ReceiveLock); 104 #endif 105 106 while (TRUE) 107 { 108 NdisAcquireSpinLock(&Adapter->ReceiveLock); 109 110 RxStopped = (Adapter->RcbFree == Adapter->RcbCount); 111 112 NdisReleaseSpinLock(&Adapter->ReceiveLock); 113 114 if (RxStopped) 115 break; 116 117 NdisMSleep(10); 118 } 119 } 120 121 DECLSPEC_NOINLINE /* Called from pageable code */ 122 VOID 123 DcStopAdapter( 124 _In_ PDC21X4_ADAPTER Adapter, 125 _In_ BOOLEAN WaitForPackets) 126 { 127 BOOLEAN TimerCancelled; 128 129 /* Attempt to disable interrupts to complete more quickly */ 130 DC_WRITE(Adapter, DcCsr7_IrqMask, 0); 131 132 /* Prevent DPCs from executing and stop accepting incoming packets */ 133 NdisAcquireSpinLock(&Adapter->SendLock); 134 Adapter->Flags &= ~DC_ACTIVE; 135 NdisReleaseSpinLock(&Adapter->SendLock); 136 137 NdisMCancelTimer(&Adapter->MediaMonitorTimer, &TimerCancelled); 138 139 /* Wait for any DPCs to complete */ 140 KeFlushQueuedDpcs(); 141 142 /* Disable interrupts */ 143 DC_WRITE(Adapter, DcCsr7_IrqMask, 0); 144 145 /* Wait for completion of TX/RX and stop the DMA engine inside the NIC */ 146 DcStopTxRxProcess(Adapter); 147 Adapter->OpMode &= ~(DC_OPMODE_RX_ENABLE | DC_OPMODE_TX_ENABLE); 148 149 DcFlushTransmitQueue(Adapter); 150 151 /* Wait for the packets to be returned to the driver */ 152 if (WaitForPackets) 153 { 154 DcStopReceivePath(Adapter); 155 } 156 157 /* Make sure there is no pending OID request */ 158 if (Adapter->OidPending) 159 { 160 NdisMSetInformationComplete(Adapter->AdapterHandle, NDIS_STATUS_SUCCESS); 161 162 Adapter->OidPending = FALSE; 163 } 164 } 165 166 CODE_SEG("PAGE") 167 VOID 168 DcStartAdapter( 169 _In_ PDC21X4_ADAPTER Adapter) 170 { 171 PAGED_CODE(); 172 173 /* Enable interrupts */ 174 _InterlockedExchange((PLONG)&Adapter->CurrentInterruptMask, Adapter->InterruptMask); 175 DC_WRITE(Adapter, DcCsr7_IrqMask, Adapter->InterruptMask); 176 177 Adapter->Flags |= DC_ACTIVE; 178 179 /* Start the RX process */ 180 Adapter->OpMode |= DC_OPMODE_RX_ENABLE; 181 DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); 182 183 /* Start the media monitor, wait the selected media to become ready */ 184 NdisMSetTimer(&Adapter->MediaMonitorTimer, 2400); 185 } 186 187 CODE_SEG("PAGE") 188 VOID 189 NTAPI 190 DcResetWorker( 191 _In_ PNDIS_WORK_ITEM WorkItem, 192 _In_opt_ PVOID Context) 193 { 194 PDC21X4_ADAPTER Adapter = Context; 195 NDIS_STATUS Status; 196 ULONG InterruptStatus; 197 LONG ResetReason; 198 199 UNREFERENCED_PARAMETER(WorkItem); 200 201 PAGED_CODE(); 202 203 Status = NDIS_STATUS_SUCCESS; 204 205 /* Check if the device is present */ 206 InterruptStatus = DC_READ(Adapter, DcCsr5_Status); 207 if (InterruptStatus == 0xFFFFFFFF) 208 { 209 ERR("Hardware is gone...\n"); 210 211 /* Remove this adapter */ 212 NdisMRemoveMiniport(Adapter->AdapterHandle); 213 214 Status = NDIS_STATUS_HARD_ERRORS; 215 goto Done; 216 } 217 218 DcStopAdapter(Adapter, FALSE); 219 220 if (Adapter->LinkUp) 221 { 222 Adapter->LinkUp = FALSE; 223 224 NdisMIndicateStatus(Adapter->AdapterHandle, 225 NDIS_STATUS_MEDIA_DISCONNECT, 226 NULL, 227 0); 228 NdisMIndicateStatusComplete(Adapter->AdapterHandle); 229 } 230 231 DcSetupAdapter(Adapter); 232 233 DcStartAdapter(Adapter); 234 235 Done: 236 ResetReason = _InterlockedExchange(&Adapter->ResetLock, 0); 237 238 /* Complete the pending reset request */ 239 if (ResetReason == 1) 240 { 241 NdisMResetComplete(Adapter->AdapterHandle, Status, FALSE); 242 } 243 } 244 245 VOID 246 NTAPI 247 DcTransmitTimeoutRecoveryWorker( 248 _In_ PNDIS_WORK_ITEM WorkItem, 249 _In_opt_ PVOID Context) 250 { 251 PDC21X4_ADAPTER Adapter = Context; 252 253 UNREFERENCED_PARAMETER(WorkItem); 254 255 NdisAcquireSpinLock(&Adapter->ModeLock); 256 257 DcStopTxRxProcess(Adapter); 258 DC_WRITE(Adapter, DcCsr6_OpMode, Adapter->OpMode); 259 260 NdisDprAcquireSpinLock(&Adapter->SendLock); 261 262 DC_WRITE(Adapter, DcCsr1_TxPoll, DC_TX_POLL_DOORBELL); 263 264 NdisDprReleaseSpinLock(&Adapter->SendLock); 265 266 NdisReleaseSpinLock(&Adapter->ModeLock); 267 } 268 269 static 270 BOOLEAN 271 NTAPI 272 DcCheckForHang( 273 _In_ NDIS_HANDLE MiniportAdapterContext) 274 { 275 PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; 276 ULONG TcbCompleted; 277 BOOLEAN TxHang = FALSE; 278 279 if (!(Adapter->Flags & DC_ACTIVE)) 280 return FALSE; 281 282 NdisDprAcquireSpinLock(&Adapter->SendLock); 283 284 if (Adapter->TcbSlots != (DC_TRANSMIT_BLOCKS - DC_TCB_RESERVE)) 285 { 286 TcbCompleted = Adapter->TcbCompleted; 287 TxHang = (TcbCompleted == Adapter->LastTcbCompleted); 288 Adapter->LastTcbCompleted = TcbCompleted; 289 } 290 291 NdisDprReleaseSpinLock(&Adapter->SendLock); 292 293 if (TxHang) 294 { 295 WARN("Transmit timeout, CSR12 %08lx, CSR5 %08lx\n", 296 DC_READ(Adapter, DcCsr12_SiaStatus), 297 DC_READ(Adapter, DcCsr5_Status)); 298 299 NdisScheduleWorkItem(&Adapter->TxRecoveryWorkItem); 300 } 301 302 return FALSE; 303 } 304 305 static 306 NDIS_STATUS 307 NTAPI 308 DcReset( 309 _Out_ PBOOLEAN AddressingReset, 310 _In_ NDIS_HANDLE MiniportAdapterContext) 311 { 312 PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; 313 314 WARN("Called\n"); 315 316 if (_InterlockedCompareExchange(&Adapter->ResetLock, 1, 0)) 317 { 318 return NDIS_STATUS_RESET_IN_PROGRESS; 319 } 320 321 NdisScheduleWorkItem(&Adapter->ResetWorkItem); 322 323 return NDIS_STATUS_PENDING; 324 } 325 326 static 327 CODE_SEG("PAGE") 328 VOID 329 NTAPI 330 DcHalt( 331 _In_ NDIS_HANDLE MiniportAdapterContext) 332 { 333 PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; 334 335 PAGED_CODE(); 336 337 INFO("Called\n"); 338 339 DcStopAdapter(Adapter, TRUE); 340 341 DcDisableHw(Adapter); 342 343 DcFreeAdapter(Adapter); 344 } 345 346 static 347 VOID 348 NTAPI 349 DcShutdown( 350 _In_ NDIS_HANDLE MiniportAdapterContext) 351 { 352 PDC21X4_ADAPTER Adapter = (PDC21X4_ADAPTER)MiniportAdapterContext; 353 354 INFO("Called\n"); 355 356 DcDisableHw(Adapter); 357 } 358 359 CODE_SEG("INIT") 360 NTSTATUS 361 NTAPI 362 DriverEntry( 363 _In_ PDRIVER_OBJECT DriverObject, 364 _In_ PUNICODE_STRING RegistryPath) 365 { 366 NDIS_HANDLE WrapperHandle; 367 NDIS_STATUS Status; 368 NDIS_MINIPORT_CHARACTERISTICS Characteristics = { 0 }; 369 370 INFO("Called\n"); 371 372 NdisMInitializeWrapper(&WrapperHandle, DriverObject, RegistryPath, NULL); 373 if (!WrapperHandle) 374 return NDIS_STATUS_FAILURE; 375 376 Characteristics.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION; 377 Characteristics.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION; 378 Characteristics.CheckForHangHandler = DcCheckForHang; 379 Characteristics.HaltHandler = DcHalt; 380 Characteristics.HandleInterruptHandler = DcHandleInterrupt; 381 Characteristics.InitializeHandler = DcInitialize; 382 Characteristics.ISRHandler = DcIsr; 383 Characteristics.QueryInformationHandler = DcQueryInformation; 384 Characteristics.ResetHandler = DcReset; 385 Characteristics.SetInformationHandler = DcSetInformation; 386 Characteristics.ReturnPacketHandler = DcReturnPacket; 387 Characteristics.SendPacketsHandler = DcSendPackets; 388 Characteristics.CancelSendPacketsHandler = DcCancelSendPackets; 389 Characteristics.AdapterShutdownHandler = DcShutdown; 390 391 Status = NdisMRegisterMiniport(WrapperHandle, &Characteristics, sizeof(Characteristics)); 392 if (Status != NDIS_STATUS_SUCCESS) 393 { 394 NdisTerminateWrapper(WrapperHandle, NULL); 395 return Status; 396 } 397 398 InitializeListHead(&SRompAdapterList); 399 400 return NDIS_STATUS_SUCCESS; 401 } 402