xref: /reactos/drivers/network/dd/rtl8139/ndis.c (revision 34593d93)
1 /*
2  * ReactOS Realtek 8139 Driver
3  *
4  * Copyright (C) 2013 Cameron Gutman
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  *
20  */
21 
22 #include "nic.h"
23 
24 #define NDEBUG
25 #include <debug.h>
26 
27 ULONG DebugTraceLevel = MIN_TRACE;
28 
29 NDIS_STATUS
30 NTAPI
MiniportReset(OUT PBOOLEAN AddressingReset,IN NDIS_HANDLE MiniportAdapterContext)31 MiniportReset (
32     OUT PBOOLEAN AddressingReset,
33     IN NDIS_HANDLE MiniportAdapterContext
34     )
35 {
36     *AddressingReset = FALSE;
37     return NDIS_STATUS_FAILURE;
38 }
39 
40 NDIS_STATUS
41 NTAPI
MiniportSend(IN NDIS_HANDLE MiniportAdapterContext,IN PNDIS_PACKET Packet,IN UINT Flags)42 MiniportSend (
43     IN NDIS_HANDLE MiniportAdapterContext,
44     IN PNDIS_PACKET Packet,
45     IN UINT Flags
46     )
47 {
48     PRTL_ADAPTER adapter = (PRTL_ADAPTER)MiniportAdapterContext;
49     NDIS_STATUS status;
50     PSCATTER_GATHER_LIST sgList = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet,
51                                     ScatterGatherListPacketInfo);
52     ULONG transmitLength;
53     ULONG transmitBuffer;
54     PNDIS_BUFFER firstBuffer;
55     PVOID firstBufferVa;
56     UINT firstBufferLength, totalBufferLength;
57     PUCHAR runtBuffer;
58 
59     ASSERT(sgList != NULL);
60 
61     ASSERT(sgList->NumberOfElements == 1);
62     ASSERT(sgList->Elements[0].Address.HighPart == 0);
63     ASSERT((sgList->Elements[0].Address.LowPart & 3) == 0);
64     ASSERT(sgList->Elements[0].Length <= MAXIMUM_FRAME_SIZE);
65 
66     NDIS_DbgPrint(MAX_TRACE, ("Sending %d byte packet\n", sgList->Elements[0].Length));
67 
68     NdisAcquireSpinLock(&adapter->Lock);
69 
70     if (adapter->TxFull)
71     {
72         NDIS_DbgPrint(MIN_TRACE, ("All TX descriptors are full\n"));
73         NdisReleaseSpinLock(&adapter->Lock);
74         return NDIS_STATUS_RESOURCES;
75     }
76 
77     NDIS_DbgPrint(MAX_TRACE, ("Sending packet on TX desc %d\n", adapter->CurrentTxDesc));
78 
79     //
80     // If this is a runt, we need to pad it manually for the RTL8139
81     //
82     if (sgList->Elements[0].Length < MINIMUM_FRAME_SIZE)
83     {
84         transmitLength = MINIMUM_FRAME_SIZE;
85         transmitBuffer = adapter->RuntTxBuffersPa.LowPart +
86                   (MINIMUM_FRAME_SIZE * adapter->CurrentTxDesc);
87 
88         NdisGetFirstBufferFromPacketSafe(Packet,
89                                          &firstBuffer,
90                                          &firstBufferVa,
91                                          &firstBufferLength,
92                                          &totalBufferLength,
93                                          NormalPagePriority);
94         if (firstBufferVa == NULL)
95         {
96             NDIS_DbgPrint(MIN_TRACE, ("Unable to get buffer from packet\n"));
97             NdisReleaseSpinLock(&adapter->Lock);
98             return NDIS_STATUS_RESOURCES;
99         }
100 
101         ASSERT(firstBufferLength == totalBufferLength);
102 
103         runtBuffer = adapter->RuntTxBuffers + (MINIMUM_FRAME_SIZE * adapter->CurrentTxDesc);
104         RtlCopyMemory(runtBuffer, firstBufferVa, firstBufferLength);
105         RtlFillMemory(runtBuffer + firstBufferLength, MINIMUM_FRAME_SIZE - firstBufferLength, 0x00);
106     }
107     else
108     {
109         transmitLength = sgList->Elements[0].Length;
110         transmitBuffer = sgList->Elements[0].Address.LowPart;
111     }
112 
113     status = NICTransmitPacket(adapter, adapter->CurrentTxDesc, transmitBuffer, transmitLength);
114     if (status != NDIS_STATUS_SUCCESS)
115     {
116         NDIS_DbgPrint(MIN_TRACE, ("Transmit packet failed\n"));
117         NdisReleaseSpinLock(&adapter->Lock);
118         return status;
119     }
120 
121     adapter->CurrentTxDesc++;
122     adapter->CurrentTxDesc %= TX_DESC_COUNT;
123 
124     if (adapter->CurrentTxDesc == adapter->DirtyTxDesc)
125     {
126         NDIS_DbgPrint(MID_TRACE, ("All TX descriptors are full now\n"));
127         adapter->TxFull = TRUE;
128     }
129 
130     NdisReleaseSpinLock(&adapter->Lock);
131 
132     return NDIS_STATUS_SUCCESS;
133 }
134 
135 VOID
136 NTAPI
MiniportHalt(IN NDIS_HANDLE MiniportAdapterContext)137 MiniportHalt (
138     IN NDIS_HANDLE MiniportAdapterContext
139     )
140 {
141     PRTL_ADAPTER adapter = (PRTL_ADAPTER)MiniportAdapterContext;
142 
143     ASSERT(adapter != NULL);
144 
145     //
146     // Interrupts need to stop first
147     //
148     if (adapter->InterruptRegistered != FALSE)
149     {
150         NdisMDeregisterInterrupt(&adapter->Interrupt);
151     }
152 
153     //
154     // If we have a mapped IO port range, we can talk to the NIC
155     //
156     if (adapter->IoBase != NULL)
157     {
158         if (adapter->ReceiveBuffer != NULL)
159         {
160             //
161             // Disassociate our shared buffer before freeing it to avoid
162             // NIC-induced memory corruption
163             //
164             NICRemoveReceiveBuffer(adapter);
165 
166             NdisMFreeSharedMemory(adapter->MiniportAdapterHandle,
167                                   adapter->ReceiveBufferLength,
168                                   FALSE,
169                                   adapter->ReceiveBuffer,
170                                   adapter->ReceiveBufferPa);
171         }
172 
173         if (adapter->RuntTxBuffers != NULL)
174         {
175             NdisMFreeSharedMemory(adapter->MiniportAdapterHandle,
176                                   MINIMUM_FRAME_SIZE * TX_DESC_COUNT,
177                                   FALSE,
178                                   adapter->RuntTxBuffers,
179                                   adapter->RuntTxBuffersPa);
180         }
181 
182         //
183         // Unregister the IO range
184         //
185         NdisMDeregisterIoPortRange(adapter->MiniportAdapterHandle,
186                                    adapter->IoRangeStart,
187                                    adapter->IoRangeLength,
188                                    adapter->IoBase);
189     }
190 
191     //
192     // Destroy the adapter context
193     //
194     NdisFreeMemory(adapter, sizeof(*adapter), 0);
195 }
196 
197 NDIS_STATUS
198 NTAPI
MiniportInitialize(OUT PNDIS_STATUS OpenErrorStatus,OUT PUINT SelectedMediumIndex,IN PNDIS_MEDIUM MediumArray,IN UINT MediumArraySize,IN NDIS_HANDLE MiniportAdapterHandle,IN NDIS_HANDLE WrapperConfigurationContext)199 MiniportInitialize (
200     OUT PNDIS_STATUS OpenErrorStatus,
201     OUT PUINT SelectedMediumIndex,
202     IN PNDIS_MEDIUM MediumArray,
203     IN UINT MediumArraySize,
204     IN NDIS_HANDLE MiniportAdapterHandle,
205     IN NDIS_HANDLE WrapperConfigurationContext
206     )
207 {
208     PRTL_ADAPTER adapter;
209     NDIS_STATUS status;
210     UINT i;
211     PNDIS_RESOURCE_LIST resourceList;
212     UINT resourceListSize;
213 
214     //
215     // Make sure the medium is supported
216     //
217     for (i = 0; i < MediumArraySize; i++)
218     {
219         if (MediumArray[i] == NdisMedium802_3)
220         {
221             *SelectedMediumIndex = i;
222             break;
223         }
224     }
225 
226     if (i == MediumArraySize)
227     {
228         NDIS_DbgPrint(MIN_TRACE, ("802.3 medium was not found in the medium array\n"));
229         return NDIS_STATUS_UNSUPPORTED_MEDIA;
230     }
231 
232     //
233     // Allocate our adapter context
234     //
235     status = NdisAllocateMemoryWithTag((PVOID*)&adapter,
236                                        sizeof(*adapter),
237                                        ADAPTER_TAG);
238     if (status != NDIS_STATUS_SUCCESS)
239     {
240         NDIS_DbgPrint(MIN_TRACE, ("Failed to allocate adapter context\n"));
241         return NDIS_STATUS_RESOURCES;
242     }
243 
244     RtlZeroMemory(adapter, sizeof(*adapter));
245     adapter->MiniportAdapterHandle = MiniportAdapterHandle;
246     NdisAllocateSpinLock(&adapter->Lock);
247 
248     //
249     // Notify NDIS of some characteristics of our NIC
250     //
251     NdisMSetAttributesEx(MiniportAdapterHandle,
252                          adapter,
253                          0,
254                          NDIS_ATTRIBUTE_BUS_MASTER,
255                          NdisInterfacePci);
256 
257     //
258     // Get our resources for IRQ and IO base information
259     //
260     resourceList = NULL;
261     resourceListSize = 0;
262     NdisMQueryAdapterResources(&status,
263                                WrapperConfigurationContext,
264                                resourceList,
265                                &resourceListSize);
266     if (status != NDIS_STATUS_RESOURCES)
267     {
268         NDIS_DbgPrint(MIN_TRACE, ("Unexpected failure of NdisMQueryAdapterResources #1\n"));
269         status = NDIS_STATUS_FAILURE;
270         goto Cleanup;
271     }
272 
273     status = NdisAllocateMemoryWithTag((PVOID*)&resourceList,
274                                 resourceListSize,
275                                 RESOURCE_LIST_TAG);
276     if (status != NDIS_STATUS_SUCCESS)
277     {
278         NDIS_DbgPrint(MIN_TRACE, ("Failed to allocate resource list\n"));
279         goto Cleanup;
280     }
281 
282     NdisMQueryAdapterResources(&status,
283                                WrapperConfigurationContext,
284                                resourceList,
285                                &resourceListSize);
286     if (status != NDIS_STATUS_SUCCESS)
287     {
288         NDIS_DbgPrint(MIN_TRACE, ("Unexpected failure of NdisMQueryAdapterResources #2\n"));
289         goto Cleanup;
290     }
291 
292     ASSERT(resourceList->Version == 1);
293     ASSERT(resourceList->Revision == 1);
294 
295     for (i = 0; i < resourceList->Count; i++)
296     {
297         switch (resourceList->PartialDescriptors[i].Type)
298         {
299             case CmResourceTypePort:
300                 ASSERT(adapter->IoRangeStart == 0);
301 
302                 ASSERT(resourceList->PartialDescriptors[i].u.Port.Start.HighPart == 0);
303 
304                 adapter->IoRangeStart = resourceList->PartialDescriptors[i].u.Port.Start.LowPart;
305                 adapter->IoRangeLength = resourceList->PartialDescriptors[i].u.Port.Length;
306 
307                 NDIS_DbgPrint(MID_TRACE, ("I/O port range is %p to %p\n",
308                               adapter->IoRangeStart, adapter->IoRangeStart + adapter->IoRangeLength));
309                 break;
310 
311             case CmResourceTypeInterrupt:
312                 ASSERT(adapter->InterruptVector == 0);
313                 ASSERT(adapter->InterruptLevel == 0);
314 
315                 adapter->InterruptVector = resourceList->PartialDescriptors[i].u.Interrupt.Vector;
316                 adapter->InterruptLevel = resourceList->PartialDescriptors[i].u.Interrupt.Level;
317                 adapter->InterruptShared = (resourceList->PartialDescriptors[i].ShareDisposition == CmResourceShareShared);
318                 adapter->InterruptFlags = resourceList->PartialDescriptors[i].Flags;
319 
320                 NDIS_DbgPrint(MID_TRACE, ("IRQ vector is %d\n", adapter->InterruptVector));
321                 break;
322 
323             default:
324                 NDIS_DbgPrint(MIN_TRACE, ("Unrecognized resource type: 0x%x\n", resourceList->PartialDescriptors[i].Type));
325                 break;
326         }
327     }
328 
329     NdisFreeMemory(resourceList, resourceListSize, 0);
330     resourceList = NULL;
331 
332     if (adapter->IoRangeStart == 0 || adapter->InterruptVector == 0)
333     {
334         NDIS_DbgPrint(MIN_TRACE, ("Adapter didn't receive enough resources\n"));
335         goto Cleanup;
336     }
337 
338     //
339     // Allocate the DMA resources
340     //
341     status = NdisMInitializeScatterGatherDma(MiniportAdapterHandle,
342                                              FALSE, // RTL8139 only supports 32-bit addresses
343                                              MAXIMUM_FRAME_SIZE);
344     if (status != NDIS_STATUS_SUCCESS)
345     {
346         NDIS_DbgPrint(MIN_TRACE, ("Unable to configure DMA\n"));
347         goto Cleanup;
348     }
349 
350     adapter->ReceiveBufferLength = FULL_RECEIVE_BUFFER_SIZE;
351     NdisMAllocateSharedMemory(MiniportAdapterHandle,
352                               adapter->ReceiveBufferLength,
353                               FALSE,
354                               (PVOID*)&adapter->ReceiveBuffer,
355                               &adapter->ReceiveBufferPa);
356     if (adapter->ReceiveBuffer == NULL)
357     {
358         NDIS_DbgPrint(MIN_TRACE, ("Unable to allocate receive buffer\n"));
359         status = NDIS_STATUS_RESOURCES;
360         goto Cleanup;
361     }
362 
363     NdisMAllocateSharedMemory(MiniportAdapterHandle,
364                               MINIMUM_FRAME_SIZE * TX_DESC_COUNT,
365                               FALSE,
366                               (PVOID*)&adapter->RuntTxBuffers,
367                               &adapter->RuntTxBuffersPa);
368     if (adapter->RuntTxBuffers == NULL)
369     {
370         NDIS_DbgPrint(MIN_TRACE, ("Unable to allocate runt TX buffer\n"));
371         status = NDIS_STATUS_RESOURCES;
372         goto Cleanup;
373     }
374 
375     //
376     // Register the I/O port range and configure the NIC
377     //
378     status = NdisMRegisterIoPortRange((PVOID*)&adapter->IoBase,
379                                       MiniportAdapterHandle,
380                                       adapter->IoRangeStart,
381                                       adapter->IoRangeLength);
382     if (status != NDIS_STATUS_SUCCESS)
383     {
384         NDIS_DbgPrint(MIN_TRACE, ("Unable to register IO port range (0x%x)\n", status));
385         goto Cleanup;
386     }
387 
388     //
389     // Adapter setup
390     //
391     status = NICPowerOn(adapter);
392     if (status != NDIS_STATUS_SUCCESS)
393     {
394         NDIS_DbgPrint(MIN_TRACE, ("Unable to power on NIC (0x%x)\n", status));
395         goto Cleanup;
396     }
397 
398     status = NICSoftReset(adapter);
399     if (status != NDIS_STATUS_SUCCESS)
400     {
401         NDIS_DbgPrint(MIN_TRACE, ("Unable to reset the NIC (0x%x)\n", status));
402         goto Cleanup;
403     }
404 
405     status = NICGetPermanentMacAddress(adapter, adapter->PermanentMacAddress);
406     if (status != NDIS_STATUS_SUCCESS)
407     {
408         NDIS_DbgPrint(MIN_TRACE, ("Unable to get the fixed MAC address (0x%x)\n", status));
409         goto Cleanup;
410     }
411 
412     RtlCopyMemory(adapter->CurrentMacAddress, adapter->PermanentMacAddress, IEEE_802_ADDR_LENGTH);
413 
414     //
415     // Update link state and speed
416     //
417     NICUpdateLinkStatus(adapter);
418 
419     status = NICRegisterReceiveBuffer(adapter);
420     if (status != NDIS_STATUS_SUCCESS)
421     {
422         NDIS_DbgPrint(MIN_TRACE, ("Unable to setup receive buffer (0x%x)\n", status));
423         goto Cleanup;
424     }
425 
426     //
427     // We're ready to handle interrupts now
428     //
429     status = NdisMRegisterInterrupt(&adapter->Interrupt,
430                                     MiniportAdapterHandle,
431                                     adapter->InterruptVector,
432                                     adapter->InterruptLevel,
433                                     TRUE, // We always want ISR calls
434                                     adapter->InterruptShared,
435                                     (adapter->InterruptFlags & CM_RESOURCE_INTERRUPT_LATCHED) ?
436                                         NdisInterruptLatched : NdisInterruptLevelSensitive);
437     if (status != NDIS_STATUS_SUCCESS)
438     {
439         NDIS_DbgPrint(MIN_TRACE, ("Unable to register interrupt (0x%x)\n", status));
440         goto Cleanup;
441     }
442 
443     adapter->InterruptRegistered = TRUE;
444 
445     //
446     // Enable interrupts on the NIC
447     //
448     adapter->InterruptMask = DEFAULT_INTERRUPT_MASK;
449     status = NICApplyInterruptMask(adapter);
450     if (status != NDIS_STATUS_SUCCESS)
451     {
452         NDIS_DbgPrint(MIN_TRACE, ("Unable to apply interrupt mask (0x%x)\n", status));
453         goto Cleanup;
454     }
455 
456     //
457     // Turn on TX and RX now
458     //
459     status = NICEnableTxRx(adapter);
460     if (status != NDIS_STATUS_SUCCESS)
461     {
462         NDIS_DbgPrint(MIN_TRACE, ("Unable to enable TX and RX (0x%x)\n", status));
463         goto Cleanup;
464     }
465 
466     return NDIS_STATUS_SUCCESS;
467 
468 Cleanup:
469     if (resourceList != NULL)
470     {
471         NdisFreeMemory(resourceList, resourceListSize, 0);
472     }
473     if (adapter != NULL)
474     {
475         MiniportHalt(adapter);
476     }
477 
478     return status;
479 }
480 
481 NTSTATUS
482 NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)483 DriverEntry (
484     IN PDRIVER_OBJECT DriverObject,
485     IN PUNICODE_STRING RegistryPath
486     )
487 {
488     NDIS_HANDLE wrapperHandle;
489     NDIS_MINIPORT_CHARACTERISTICS characteristics;
490     NDIS_STATUS status;
491 
492     RtlZeroMemory(&characteristics, sizeof(characteristics));
493     characteristics.MajorNdisVersion = NDIS_MINIPORT_MAJOR_VERSION;
494     characteristics.MinorNdisVersion = NDIS_MINIPORT_MINOR_VERSION;
495     characteristics.CheckForHangHandler = NULL;
496     characteristics.DisableInterruptHandler = NULL;
497     characteristics.EnableInterruptHandler = NULL;
498     characteristics.HaltHandler = MiniportHalt;
499     characteristics.HandleInterruptHandler = MiniportHandleInterrupt;
500     characteristics.InitializeHandler = MiniportInitialize;
501     characteristics.ISRHandler = MiniportISR;
502     characteristics.QueryInformationHandler = MiniportQueryInformation;
503     characteristics.ReconfigureHandler = NULL;
504     characteristics.ResetHandler = MiniportReset;
505     characteristics.SendHandler = MiniportSend;
506     characteristics.SetInformationHandler = MiniportSetInformation;
507     characteristics.TransferDataHandler = NULL;
508     characteristics.ReturnPacketHandler = NULL;
509     characteristics.SendPacketsHandler = NULL;
510     characteristics.AllocateCompleteHandler = NULL;
511 
512     NdisMInitializeWrapper(&wrapperHandle, DriverObject, RegistryPath, NULL);
513     if (!wrapperHandle)
514     {
515         return NDIS_STATUS_FAILURE;
516     }
517 
518     status = NdisMRegisterMiniport(wrapperHandle, &characteristics, sizeof(characteristics));
519     if (status != NDIS_STATUS_SUCCESS)
520     {
521         NdisTerminateWrapper(wrapperHandle, 0);
522         return NDIS_STATUS_FAILURE;
523     }
524 
525     return NDIS_STATUS_SUCCESS;
526 }
527