xref: /reactos/drivers/network/dd/dc21x4/dc21x4.c (revision d41dec2e)
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