1 /*
2  * This file contains driver-related part of NDIS5.X adapter driver.
3  *
4  * Copyright (c) 2008-2017 Red Hat, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met :
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and / or other materials provided with the distribution.
14  * 3. Neither the names of the copyright holders nor the names of their contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 #include "ParaNdis5.h"
30 
31 //#define NO_XP_POWER_MANAGEMENT
32 
33 #ifdef WPP_EVENT_TRACING
34 #include "ParaNdis5-Driver.tmh"
35 #endif
36 
37 static NDIS_HANDLE      DriverHandle;
38 static ULONG            gID = 0;
39 
40 /******************************************************
41 Unload handler, only responsibility is cleanup WPP
42 *******************************************************/
43 static VOID NTAPI ParaVirtualNICUnload(IN  PDRIVER_OBJECT  pDriverObject)
44 {
45     DEBUG_ENTRY(0);
46     ParaNdis_DebugCleanup(pDriverObject);
47 }
48 
49 /*************************************************************
50 Required NDIS function
51 Responsible to put the adapter to known (initial) hardware state
52 
53 Do not call any NDIS functions
54 *************************************************************/
55 static VOID NTAPI ParaNdis5_Shutdown(IN NDIS_HANDLE MiniportAdapterContext)
56 {
57     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
58     ParaNdis_OnShutdown(pContext);
59 }
60 
61 /******************************************************
62 Required NDIS procedure
63 Allocates and initializes adapter context
64 Finally sets send and receive to Enabled state and reports connect
65 Returns:
66 NDIS_STATUS SUCCESS or some error code
67 *******************************************************/
68 static NDIS_STATUS NTAPI ParaNdis5_Initialize(OUT PNDIS_STATUS OpenErrorStatus,
69                                     OUT PUINT SelectedMediumIndex,
70                                     IN PNDIS_MEDIUM MediumArray,
71                                     IN UINT MediumArraySize,
72                                     IN NDIS_HANDLE MiniportAdapterHandle,
73                                     IN NDIS_HANDLE WrapperConfigurationContext)
74 {
75     NDIS_STATUS  status = NDIS_STATUS_UNSUPPORTED_MEDIA;
76     PARANDIS_ADAPTER *pContext = NULL;
77     UINT i;
78     for(i = 0; i < MediumArraySize; ++i)
79     {
80         if(MediumArray[i] == NdisMedium802_3)
81         {
82             *SelectedMediumIndex = i;
83             status = NDIS_STATUS_SUCCESS;
84             break;
85         }
86     }
87 
88     if (status == NDIS_STATUS_SUCCESS)
89     {
90         pContext =
91             (PARANDIS_ADAPTER *)ParaNdis_AllocateMemory(NULL, sizeof(PARANDIS_ADAPTER));
92         if (!pContext)
93         {
94             status = NDIS_STATUS_RESOURCES;
95         }
96     }
97 
98     if (status == NDIS_STATUS_SUCCESS)
99     {
100         PVOID pResourceList = &status;
101         UINT  uSize = 0;
102         NdisZeroMemory(pContext, sizeof(PARANDIS_ADAPTER));
103         pContext->ulUniqueID = NdisInterlockedIncrement(&gID);
104         pContext->DriverHandle = DriverHandle;
105         pContext->MiniportHandle = MiniportAdapterHandle;
106         pContext->WrapperConfigurationHandle = WrapperConfigurationContext;
107         NdisMQueryAdapterResources(&status, WrapperConfigurationContext, pResourceList, &uSize);
108         if (uSize > 0)
109             pResourceList = ParaNdis_AllocateMemory(MiniportAdapterHandle, uSize);
110         else
111             pResourceList = NULL;
112         if (!pResourceList)
113             status = uSize > 0 ? NDIS_STATUS_RESOURCES : NDIS_STATUS_FAILURE;
114         else
115         {
116             ULONG attributes;
117             attributes = NDIS_ATTRIBUTE_DESERIALIZE | NDIS_ATTRIBUTE_BUS_MASTER;
118             // in XP SP2, if this flag is NOT set, the NDIS halts miniport
119             // upon transition to S1..S4.
120             // it seems that XP SP3 ignores it and always sends SET_POWER to D3
121 #ifndef NO_XP_POWER_MANAGEMENT
122             attributes |= NDIS_ATTRIBUTE_NO_HALT_ON_SUSPEND;
123 #endif
124             NdisMSetAttributesEx(
125                 MiniportAdapterHandle,
126                 pContext,
127                 0,
128                 attributes,
129                 NdisInterfacePci);
130             NdisMQueryAdapterResources(&status, WrapperConfigurationContext, pResourceList, &uSize);
131             status = ParaNdis_InitializeContext(pContext, (PNDIS_RESOURCE_LIST)pResourceList);
132             NdisFreeMemory(pResourceList, 0, 0);
133         }
134     }
135 
136     if (status == NDIS_STATUS_SUCCESS)
137     {
138         status = ParaNdis_FinishInitialization(pContext);
139         if (status == NDIS_STATUS_SUCCESS)
140         {
141             ParaNdis_DebugRegisterMiniport(pContext, TRUE);
142             ParaNdis_IndicateConnect(pContext, FALSE, TRUE);
143             ParaNdis5_StopSend(pContext, FALSE, NULL);
144             ParaNdis5_StopReceive(pContext, FALSE, NULL);
145             if (!pContext->ulMilliesToConnect)
146             {
147                 ParaNdis_ReportLinkStatus(pContext, FALSE);
148             }
149             else
150             {
151                 NdisSetTimer(&pContext->ConnectTimer, pContext->ulMilliesToConnect);
152             }
153         }
154         else
155         {
156             ParaNdis_CleanupContext(pContext);
157         }
158     }
159 
160     if (status != NDIS_STATUS_SUCCESS && pContext)
161     {
162         NdisFreeMemory(pContext, 0, 0);
163     }
164 
165     DEBUG_EXIT_STATUS(0, status);
166     return status;
167 }
168 
169 
170 /*************************************************************
171 Callback of delayed receive pause procedure upon reset request
172 *************************************************************/
173 static void OnReceiveStoppedOnReset(VOID *p)
174 {
175     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)p;
176     DEBUG_ENTRY(0);
177     NdisSetEvent(&pContext->ResetEvent);
178 }
179 
180 /*************************************************************
181 Callback of delayed send pause procedure upon reset request
182 *************************************************************/
183 static void OnSendStoppedOnReset(VOID *p)
184 {
185     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)p;
186     DEBUG_ENTRY(0);
187     NdisSetEvent(&pContext->ResetEvent);
188 }
189 
190 VOID ParaNdis_Suspend(PARANDIS_ADAPTER *pContext)
191 {
192     DEBUG_ENTRY(0);
193     NdisResetEvent(&pContext->ResetEvent);
194     if (NDIS_STATUS_PENDING != ParaNdis5_StopSend(pContext, TRUE, OnSendStoppedOnReset))
195     {
196         NdisSetEvent(&pContext->ResetEvent);
197     }
198     NdisWaitEvent(&pContext->ResetEvent, 0);
199     NdisResetEvent(&pContext->ResetEvent);
200     if (NDIS_STATUS_PENDING != ParaNdis5_StopReceive(pContext, TRUE, OnReceiveStoppedOnReset))
201     {
202         NdisSetEvent(&pContext->ResetEvent);
203     }
204     NdisWaitEvent(&pContext->ResetEvent, 0);
205     NdisResetEvent(&pContext->ResetEvent);
206     DEBUG_EXIT_STATUS(0, 0);
207 }
208 
209 VOID ParaNdis_Resume(PARANDIS_ADAPTER *pContext)
210 {
211     ParaNdis5_StopSend(pContext, FALSE, NULL);
212     ParaNdis5_StopReceive(pContext, FALSE, NULL);
213     DEBUG_EXIT_STATUS(0, 0);
214 }
215 
216 static void NTAPI OnResetWorkItem(NDIS_WORK_ITEM * pWorkItem, PVOID  Context)
217 {
218     tGeneralWorkItem *pwi = (tGeneralWorkItem *)pWorkItem;
219     PARANDIS_ADAPTER *pContext = pwi->pContext;
220     DEBUG_ENTRY(0);
221 
222     pContext->bResetInProgress = TRUE;
223     ParaNdis_IndicateConnect(pContext, FALSE, FALSE);
224     ParaNdis_Suspend(pContext);
225     ParaNdis_Resume(pContext);
226     pContext->bResetInProgress = FALSE;
227     ParaNdis_ReportLinkStatus(pContext, FALSE);
228 
229     NdisFreeMemory(pwi, 0, 0);
230     ParaNdis_DebugHistory(pContext, hopSysReset, NULL, 0, NDIS_STATUS_SUCCESS, 0);
231     NdisMResetComplete(pContext->MiniportHandle, NDIS_STATUS_SUCCESS, TRUE);
232 }
233 
234 
235 /*************************************************************
236 Required NDIS procedure
237 Called when some procedure (like OID handler) returns PENDING and
238 does not complete or when CheckForHang return TRUE
239 *************************************************************/
240 static NDIS_STATUS NTAPI ParaNdis5_Reset(
241     OUT PBOOLEAN AddressingReset,
242     IN NDIS_HANDLE MiniportAdapterContext)
243 {
244     NDIS_STATUS status;
245     tGeneralWorkItem *pwi;
246     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
247     DEBUG_ENTRY(0);
248     ParaNdis_DebugHistory(pContext, hopSysReset, NULL, 1, 0, 0);
249     status = NDIS_STATUS_FAILURE;
250     pwi = ParaNdis_AllocateMemory(pContext, sizeof(tGeneralWorkItem));
251     if (pwi)
252     {
253         pwi->pContext = pContext;
254         NdisInitializeWorkItem(&pwi->wi, OnResetWorkItem, pwi);
255         if (NdisScheduleWorkItem(&pwi->wi) == NDIS_STATUS_SUCCESS)
256         {
257             status = NDIS_STATUS_PENDING;
258         }
259         else
260         {
261             NdisFreeMemory(pwi, 0, 0);
262         }
263     }
264     if (status != NDIS_STATUS_PENDING)
265     {
266         ParaNdis_DebugHistory(pContext, hopSysReset, NULL, 0, status, 0);
267     }
268     return status;
269 }
270 
271 /*************************************************************
272 Callback of delayed receive pause procedure
273 *************************************************************/
274 static VOID OnReceiveStopped(VOID *p)
275 {
276     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)p;
277     DEBUG_ENTRY(0);
278     NdisSetEvent(&pContext->HaltEvent);
279 }
280 
281 /*************************************************************
282 Callback of delayed send pause procedure
283 *************************************************************/
284 static VOID OnSendStopped(VOID *p)
285 {
286     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)p;
287     DEBUG_ENTRY(0);
288     NdisSetEvent(&pContext->HaltEvent);
289 }
290 
291 static void WaitHaltEvent(PARANDIS_ADAPTER *pContext, const char *Reason)
292 {
293     UINT ms = 5000;
294     if (!NdisWaitEvent(&pContext->HaltEvent, 1))
295     {
296         while (!NdisWaitEvent(&pContext->HaltEvent, ms))
297         {
298             DPrintf(0, ("[%s]", __FUNCTION__));
299         }
300     }
301 }
302 
303 /*************************************************************
304 Required NDIS procedure
305 Stops TX and RX path and finished the function of adapter
306 *************************************************************/
307 static VOID NTAPI ParaNdis5_Halt(
308     IN NDIS_HANDLE MiniportAdapterContext)
309 {
310     NDIS_STATUS status = NDIS_STATUS_SUCCESS;
311     BOOLEAN bUnused;
312     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
313     DEBUG_ENTRY(0);
314 
315     ParaNdis_DebugHistory(pContext, hopHalt, NULL, 1, 0, 0);
316 
317     NdisCancelTimer(&pContext->ConnectTimer, &bUnused);
318     NdisResetEvent(&pContext->HaltEvent);
319     if (NDIS_STATUS_PENDING != ParaNdis5_StopSend(pContext, TRUE, OnSendStopped))
320         NdisSetEvent(&pContext->HaltEvent);
321     WaitHaltEvent(pContext, "Send");
322     NdisResetEvent(&pContext->HaltEvent);
323     if (NDIS_STATUS_PENDING != ParaNdis5_StopReceive(pContext, TRUE, OnReceiveStopped))
324         NdisSetEvent(&pContext->HaltEvent);
325     WaitHaltEvent(pContext, "Receive");
326     ParaNdis_CleanupContext(pContext);
327     NdisCancelTimer(&pContext->DPCPostProcessTimer, &bUnused);
328     ParaNdis_DebugHistory(pContext, hopHalt, NULL, 0, 0, 0);
329     ParaNdis_DebugRegisterMiniport(pContext, FALSE);
330     NdisFreeMemory(pContext, 0, 0);
331     DEBUG_EXIT_STATUS(0, status);
332 }
333 
334 
335 /*************************************************************
336 Called periodically (usually each 2 seconds)
337 *************************************************************/
338 static BOOLEAN NTAPI ParaNdis5_CheckForHang(IN NDIS_HANDLE MiniportAdapterContext)
339 {
340     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
341     DEBUG_ENTRY(8);
342     return ParaNdis_CheckForHang(pContext);
343 }
344 
345 /*************************************************************
346 Required NDIS procedure
347 Responsible for hardware interrupt handling
348 *************************************************************/
349 static VOID NTAPI ParaNdis5_MiniportISR(OUT PBOOLEAN InterruptRecognized,
350                                OUT PBOOLEAN QueueMiniportHandleInterrupt,
351                                IN NDIS_HANDLE  MiniportAdapterContext)
352 {
353     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
354     BOOLEAN b;
355     *QueueMiniportHandleInterrupt = FALSE;
356     b = ParaNdis_OnLegacyInterrupt(pContext, QueueMiniportHandleInterrupt);
357     *InterruptRecognized = b;
358     DEBUG_EXIT_STATUS(7, (ULONG)b);
359 }
360 
361 /*************************************************************
362 Parameters:
363 
364 Return value:
365 
366 *************************************************************/
367 VOID NTAPI ParaNdis5_PnPEventNotify(IN NDIS_HANDLE MiniportAdapterContext,
368                                   IN NDIS_DEVICE_PNP_EVENT PnPEvent,
369                                   IN PVOID InformationBuffer,
370                                   IN ULONG InformationBufferLength)
371 {
372     PARANDIS_ADAPTER *pContext = (PARANDIS_ADAPTER *)MiniportAdapterContext;
373     ParaNdis_OnPnPEvent(pContext, PnPEvent, InformationBuffer, InformationBufferLength);
374 }
375 
376 /*************************************************************
377 Driver's entry point
378 Parameters:
379     as usual
380 Return value:
381     SUCCESS or error code
382 *************************************************************/
383 NDIS_STATUS NTAPI DriverEntry(PVOID DriverObject,PVOID RegistryPath)
384 {
385     NDIS_STATUS status;
386     NDIS_MINIPORT_CHARACTERISTICS chars;
387     ParaNdis_DebugInitialize(DriverObject, RegistryPath);
388 
389     status = NDIS_STATUS_FAILURE;
390 
391     DEBUG_ENTRY(0);
392     _LogOutString(0, __DATE__ " " __TIME__);
393 
394     NdisMInitializeWrapper(&DriverHandle,
395                            DriverObject,
396                            RegistryPath,
397                            NULL
398                            );
399 
400     if (DriverHandle)
401     {
402         NdisZeroMemory(&chars, sizeof(chars));
403         //NDIS version of the miniport
404         chars.MajorNdisVersion          = NDIS_MINIPORT_MAJOR_VERSION;
405         chars.MinorNdisVersion          = NDIS_MINIPORT_MINOR_VERSION;
406         //Init and destruction
407         chars.InitializeHandler         = ParaNdis5_Initialize;
408         chars.HaltHandler               = ParaNdis5_Halt;
409 
410         //Interrupt and DPC handling
411         chars.HandleInterruptHandler    = ParaNdis5_HandleDPC;
412         chars.ISRHandler                = ParaNdis5_MiniportISR;
413 
414         //Packet transfer - send path and notification on the send packet
415         chars.SendPacketsHandler        = ParaNdis5_SendPackets;
416         chars.ReturnPacketHandler       = ParaNdis5_ReturnPacket;
417 
418         //OID set\get
419         chars.SetInformationHandler     = ParaNdis5_SetOID;
420         chars.QueryInformationHandler   = ParaNdis5_QueryOID;
421 
422         //Reset
423         chars.ResetHandler              = ParaNdis5_Reset;
424         chars.CheckForHangHandler       = ParaNdis5_CheckForHang; //optional
425 
426         chars.CancelSendPacketsHandler  = ParaNdis5_CancelSendPackets;
427         chars.PnPEventNotifyHandler     = ParaNdis5_PnPEventNotify;
428         chars.AdapterShutdownHandler    = ParaNdis5_Shutdown;
429 
430         status = NdisMRegisterMiniport(
431             DriverHandle,
432             &chars,
433             sizeof(chars));
434     }
435 
436     if (status == NDIS_STATUS_SUCCESS)
437     {
438         NdisMRegisterUnloadHandler(DriverHandle, ParaVirtualNICUnload);
439     }
440     else if (DriverHandle)
441     {
442         DPrintf(0, ("NdisMRegisterMiniport failed"));
443         NdisTerminateWrapper(DriverHandle, NULL);
444     }
445     else
446     {
447         DPrintf(0, ("NdisMInitializeWrapper failed"));
448     }
449 
450     DEBUG_EXIT_STATUS(status ? 0 : 4, status);
451     return status;
452 }
453