summaryrefslogtreecommitdiff
path: root/general/PLX9x5x/sys/Pci9656.c
blob: 7fc333490ff50bbfde2b14982712f676f91ba953 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
/*++
Copyright (c) Microsoft Corporation.  All rights reserved.

    THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
    KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
    IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
    PURPOSE.

Module Name:

    Pci9656.c

Abstract:

    This is a generic WDF sample driver for PLx PCI9656RDK-Lite reference
    adapter. It illustrates how to use the WDF DmaObject to perform
    Scatter/Gather DMA operations.

Environment:

    Kernel mode

--*/

#include "precomp.h"
//
// The trace message header (.tmh) file must be included in a source file
// before any WPP macro calls and after defining a WPP_CONTROL_GUIDS
// macro (defined in toaster.h). During the compilation, WPP scans the source
// files for DoTraceMessage() calls and builds a .tmh file which stores a unique
// data GUID for each message, the text resource string for each message,
// and the data types of the variables passed in for each message.  This file
// is automatically generated by the WPP preprocessor.
//
#include "Pci9656.tmh"


#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, PLxEvtDeviceAdd)
#pragma alloc_text (PAGE, PlxEvtDeviceCleanup)
#pragma alloc_text (PAGE, PLxEvtDevicePrepareHardware)
#pragma alloc_text (PAGE, PLxEvtDeviceReleaseHardware)
#pragma alloc_text (PAGE, PLxEvtDeviceD0Exit)
#pragma alloc_text (PAGE, PlxEvtDriverContextCleanup)
#pragma alloc_text (PAGE, PLxSetIdleAndWakeSettings)
#endif


NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING RegistryPath
    )
/*++

Routine Description:

    Driver initialization entry point.
    This entry point is called directly by the I/O system.

Arguments:

    DriverObject - pointer to the driver object

    RegistryPath - pointer to a unicode string representing the path,
                   to driver-specific key in the registry.

Return Value:

    NTSTATUS    - if the status value is not STATUS_SUCCESS,
                        the driver will get unloaded immediately.

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    WDF_DRIVER_CONFIG   config;
    WDF_OBJECT_ATTRIBUTES attributes;

    //
    // Initialize WDF WPP tracing.
    //
    WPP_INIT_TRACING( DriverObject, RegistryPath );

    //
    // TraceEvents function is mapped to DoTraceMessage provided by
    // WPP by using a directive in the sources file.
    //
    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,
                "Pci9656 Sample - Driver Framework Edition.");

    //
    // Initialize the Driver Config structure.
    //
    WDF_DRIVER_CONFIG_INIT( &config, PLxEvtDeviceAdd );

    //
    // Register a cleanup callback so that we can call WPP_CLEANUP when
    // the framework driver object is deleted during driver unload.
    //
    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
    attributes.EvtCleanupCallback = PlxEvtDriverContextCleanup;

    status = WdfDriverCreate( DriverObject,
                              RegistryPath,
                              &attributes,
                              &config,
                              WDF_NO_HANDLE);

    if (!NT_SUCCESS(status)) {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                    "WdfDriverCreate failed with status %!STATUS!", status);
        //
        // Cleanup tracing here because DriverContextCleanup will not be called
        // as we have failed to create WDFDRIVER object itself.
        // Please note that if your return failure from DriverEntry after the
        // WDFDRIVER object is created successfully, you don't have to
        // call WPP cleanup because in those cases DriverContextCleanup
        // will be executed when the framework deletes the DriverObject.
        //
        WPP_CLEANUP(DriverObject);
    }

    return status;
}


NTSTATUS
PLxEvtDeviceAdd(
    IN WDFDRIVER        Driver,
    IN PWDFDEVICE_INIT  DeviceInit
    )
/*++

Routine Description:

    EvtDeviceAdd is called by the framework in response to AddDevice
    call from the PnP manager. Here the driver should register all the
    PNP, power and Io callbacks, register interfaces and allocate other
    software resources required by the device. The driver can query
    any interfaces or get the config space information from the bus driver
    but cannot access hardware registers or initialize the device.

Arguments:

Return Value:

--*/
{
    NTSTATUS                   status = STATUS_SUCCESS;
    WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
    WDF_OBJECT_ATTRIBUTES       attributes;
    WDFDEVICE                   device;
    PDEVICE_EXTENSION           devExt = NULL;

    UNREFERENCED_PARAMETER( Driver );

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,  "--> PLxEvtDeviceAdd");

    PAGED_CODE();

    WdfDeviceInitSetIoType(DeviceInit, WdfDeviceIoDirect);

    //
    // Zero out the PnpPowerCallbacks structure.
    //
    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);

    //
    // Set Callbacks for any of the functions we are interested in.
    // If no callback is set, Framework will take the default action
    // by itself.
    //
    pnpPowerCallbacks.EvtDevicePrepareHardware = PLxEvtDevicePrepareHardware;
    pnpPowerCallbacks.EvtDeviceReleaseHardware = PLxEvtDeviceReleaseHardware;

    //
    // These two callbacks set up and tear down hardware state that must be
    // done every time the device moves in and out of the D0-working state.
    //
    pnpPowerCallbacks.EvtDeviceD0Entry         = PLxEvtDeviceD0Entry;
    pnpPowerCallbacks.EvtDeviceD0Exit          = PLxEvtDeviceD0Exit;

    //
    // Register the PnP Callbacks..
    //
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

    //
    // Initialize Fdo Attributes.
    //
    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_EXTENSION);
    //
    // By opting for SynchronizationScopeDevice, we tell the framework to
    // synchronize callbacks events of all the objects directly associated
    // with the device. In this driver, we will associate queues and
    // and DpcForIsr. By doing that we don't have to worrry about synchronizing
    // access to device-context by Io Events and DpcForIsr because they would
    // not concurrently ever. Framework will serialize them by using an
    // internal device-lock.
    //
    attributes.SynchronizationScope = WdfSynchronizationScopeDevice;

    //
    // This callback is invoked when the device is removed or the driver
    // is unloaded.
    //
    attributes.EvtCleanupCallback = PlxEvtDeviceCleanup;

    //
    // Create the device
    //
    status = WdfDeviceCreate( &DeviceInit, &attributes, &device );

    if (!NT_SUCCESS(status)) {
        //
        // Device Initialization failed.
        //
        TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                    "DeviceCreate failed %!STATUS!", status);
        return status;
    }

    //
    // Get the DeviceExtension and initialize it. PLxGetDeviceContext is an inline function
    // defined by WDF_DECLARE_CONTEXT_TYPE_WITH_NAME macro in the
    // private header file. This function will do the type checking and return
    // the device context. If you pass a wrong object a wrong object handle
    // it will return NULL and assert if run under framework verifier mode.
    //
    devExt = PLxGetDeviceContext(device);

    devExt->Device = device;

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "     AddDevice PDO (0x%p) FDO (0x%p), DevExt (0x%p)",
                WdfDeviceWdmGetPhysicalDevice(device),
                WdfDeviceWdmGetDeviceObject(device), devExt);

    //
    // Tell the Framework that this device will need an interface
    //
    // NOTE: See the note in Public.h concerning this GUID value.
    //
    status = WdfDeviceCreateDeviceInterface( device,
                                             (LPGUID) &GUID_PLX_INTERFACE,
                                             NULL );

    if (!NT_SUCCESS(status)) {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                    "<-- DeviceCreateDeviceInterface "
                    "failed %!STATUS!", status);
        return status;
    }

    //
    // Set the idle and wait-wake policy for this device.
    //
    status = PLxSetIdleAndWakeSettings(devExt);

    if (!NT_SUCCESS (status)) {
        //
        // NOTE: The attempt to set the Idle and Wake options
        //       is a best-effort try. Failure is probably due to
        //       the non-driver environmentals, such as the system,
        //       bus or OS indicating that Wake is not supported for
        //       this case.
        //       All that being said, it probably not desirable to
        //       return the failure code as it would cause the
        //       AddDevice to fail and Idle and Wake are probably not
        //       "must-have" options.
        //
        //       You must decide for your case whether Idle/Wake are
        //       "must-have" options...but my guess is probably not.
        //
#if 1
        status = STATUS_SUCCESS;
#else
        return status;
#endif
    }

    //
    // Initalize the Device Extension.
    //
    status = PLxInitializeDeviceExtension(devExt);

    if (!NT_SUCCESS(status)) {
        return status;
    }

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "<-- PLxEvtDeviceAdd %!STATUS!", status);

    return status;
}

_Use_decl_annotations_
VOID
PlxEvtDeviceCleanup(
    _In_ WDFOBJECT Device
    )
/*++

Routine Description:

    Invoked before the device object is deleted.

Arguments:

    Device - A handle to the WDFDEVICE

Return Value:

     None

--*/
{
    PDEVICE_EXTENSION devExt;

    PAGED_CODE();

    devExt = PLxGetDeviceContext((WDFDEVICE)Device);

    PlxCleanupDeviceExtension(devExt);
}

NTSTATUS
PLxEvtDevicePrepareHardware (
    WDFDEVICE       Device,
    WDFCMRESLIST   Resources,
    WDFCMRESLIST   ResourcesTranslated
    )
/*++

Routine Description:

    Performs whatever initialization is needed to setup the device, setting up
    a DMA channel or mapping any I/O port resources.  This will only be called
    as a device starts or restarts, not every time the device moves into the D0
    state.  Consequently, most hardware initialization belongs elsewhere.

Arguments:

    Device - A handle to the WDFDEVICE

    Resources - The raw PnP resources associated with the device.  Most of the
        time, these aren't useful for a PCI device.

    ResourcesTranslated - The translated PnP resources associated with the
        device.  This is what is important to a PCI device.

Return Value:

    NT status code - failure will result in the device stack being torn down

--*/
{
    NTSTATUS            status = STATUS_SUCCESS;
    PDEVICE_EXTENSION   devExt;

    UNREFERENCED_PARAMETER(Resources);

    PAGED_CODE();

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "--> PLxEvtDevicePrepareHardware");

    devExt = PLxGetDeviceContext(Device);

    status = PLxPrepareHardware(devExt, ResourcesTranslated);
    if (!NT_SUCCESS (status)){
        return status;
    }


    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "<-- PLxEvtDevicePrepareHardware, status %!STATUS!", status);

    return status;
}

NTSTATUS
PLxEvtDeviceReleaseHardware(
    IN  WDFDEVICE Device,
    IN  WDFCMRESLIST ResourcesTranslated
    )
/*++

Routine Description:

    Unmap the resources that were mapped in PLxEvtDevicePrepareHardware.
    This will only be called when the device stopped for resource rebalance,
    surprise-removed or query-removed.

Arguments:

    Device - A handle to the WDFDEVICE

    ResourcesTranslated - The translated PnP resources associated with the
        device.  This is what is important to a PCI device.

Return Value:

    NT status code - failure will result in the device stack being torn down

--*/
{
    PDEVICE_EXTENSION   devExt;
    NTSTATUS status = STATUS_SUCCESS;

    UNREFERENCED_PARAMETER(ResourcesTranslated);

    PAGED_CODE();

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "--> PLxEvtDeviceReleaseHardware");

    devExt = PLxGetDeviceContext(Device);

    if (devExt->RegsBase) {
        MmUnmapIoSpace(devExt->RegsBase, devExt->RegsLength);
        devExt->RegsBase = NULL;
    }

    if (devExt->SRAMBase) {
        MmUnmapIoSpace(devExt->SRAMBase, devExt->SRAMLength);
        devExt->SRAMBase = NULL;
    }

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP,
                "<-- PLxEvtDeviceReleaseHardware");

    return status;
}


NTSTATUS
PLxEvtDeviceD0Entry(
    IN  WDFDEVICE Device,
    IN  WDF_POWER_DEVICE_STATE PreviousState
    )
/*++

Routine Description:

    This routine prepares the device for use.  It is called whenever the device
    enters the D0 state, which happens when the device is started, when it is
    restarted, and when it has been powered off.

    Note that interrupts will not be enabled at the time that this is called.
    They will be enabled after this callback completes.

    This function is not marked pageable because this function is in the
    device power up path. When a function is marked pagable and the code
    section is paged out, it will generate a page fault which could impact
    the fast resume behavior because the client driver will have to wait
    until the system drivers can service this page fault.

Arguments:

    Device  - The handle to the WDF device object

    PreviousState - The state the device was in before this callback was invoked.

Return Value:

    NTSTATUS

    Success implies that the device can be used.

    Failure will result in the    device stack being torn down.

--*/
{
    PDEVICE_EXTENSION   devExt;
    NTSTATUS            status;

    UNREFERENCED_PARAMETER(PreviousState);

    devExt = PLxGetDeviceContext(Device);

    status = PLxInitWrite( devExt );
    if (NT_SUCCESS(status)) {

        status = PLxInitRead( devExt );

    }

    return status;
}

NTSTATUS
PLxEvtDeviceD0Exit(
    IN  WDFDEVICE Device,
    IN  WDF_POWER_DEVICE_STATE TargetState
    )
/*++

Routine Description:

    This routine undoes anything done in PLxEvtDeviceD0Entry.  It is called
    whenever the device leaves the D0 state, which happens when the device
    is stopped, when it is removed, and when it is powered off.

    The device is still in D0 when this callback is invoked, which means that
    the driver can still touch hardware in this routine.

    Note that interrupts have already been disabled by the time that this
    callback is invoked.

Arguments:

    Device  - The handle to the WDF device object

    TargetState - The state the device will go to when this callback completes.

Return Value:

    Success implies that the device can be used.  Failure will result in the
    device stack being torn down.

--*/
{
    PDEVICE_EXTENSION   devExt;

    PAGED_CODE();

    devExt = PLxGetDeviceContext(Device);

    switch (TargetState) {
    case WdfPowerDeviceD1:
    case WdfPowerDeviceD2:
    case WdfPowerDeviceD3:

        //
        // Fill in any code to save hardware state here.
        //

        //
        // Fill in any code to put the device in a low-power state here.
        //
        break;

    case WdfPowerDevicePrepareForHibernation:

        //
        // Fill in any code to save hardware state here.  Do not put in any
        // code to shut the device off.  If this device cannot support being
        // in the paging path (or being a parent or grandparent of a paging
        // path device) then this whole case can be deleted.
        //

        break;

    case WdfPowerDeviceD3Final:
    default:

        //
        // Reset the hardware, as we're shutting down for the last time.
        //
        PLxShutdown(devExt);
        break;
    }

    return STATUS_SUCCESS;
}


_Use_decl_annotations_
VOID
PlxEvtDriverContextCleanup(
    WDFOBJECT Driver
    )
/*++
Routine Description:

    Free all the resources allocated in DriverEntry.

Arguments:

    Driver - handle to a WDF Driver object.

Return Value:

    VOID.

--*/
{
    PAGED_CODE ();

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_INIT,
                    "PlxEvtDriverContextCleanup: enter");

    WPP_CLEANUP( WdfDriverWdmGetDriverObject( Driver ) );

}



NTSTATUS
PLxSetIdleAndWakeSettings(
        IN PDEVICE_EXTENSION FdoData
    )
/*++
Routine Description:

    Called by EvtDeviceAdd to set the idle and wait-wake policy. Registering this policy
    causes Power Management Tab to show up in the device manager. By default these
    options are enabled and the user is provided control to change the settings.

Return Value:

    NTSTATUS - Failure status is returned if the device is not capable of suspending
    or wait-waking the machine by an external event. Framework checks the
    capability information reported by the bus driver to decide whether the device is
    capable of waking the machine.

--*/
{
    WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
    WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
    NTSTATUS    status = STATUS_SUCCESS;

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "--> PLxSetIdleAndWakeSettings");

    PAGED_CODE();

    //
    // Init the idle policy structure.
    //
    WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings, IdleCanWakeFromS0);
    idleSettings.IdleTimeout = 10000; // 10-sec

    status = WdfDeviceAssignS0IdleSettings(FdoData->Device, &idleSettings);
    if ( !NT_SUCCESS(status)) {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                    "DeviceSetPowerPolicyS0IdlePolicy failed %!STATUS!", status);
        return status;
    }

    //
    // Init wait-wake policy structure.
    //
    WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);

    status = WdfDeviceAssignSxWakeSettings(FdoData->Device, &wakeSettings);
    if (!NT_SUCCESS(status)) {
        TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
                    "DeviceAssignSxWakeSettings failed %!STATUS!", status);
        return status;
    }

    TraceEvents(TRACE_LEVEL_INFORMATION, DBG_PNP, "<-- PLxSetIdleAndWakeSettings");

    return status;
}