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