summaryrefslogtreecommitdiff
path: root/bluetooth/serialhcibus/driver.h
blob: 2aaf72a0ea3e4b0e5e095811e035ed22c978d20a (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
/*++

Copyright (c) Microsoft Corporation All Rights Reserved

Module Name:

    driver.h

Abstract:

    This module contains the common private declarations for
    for the Serial HCI bus driver.

Environment:

    kernel mode only

--*/

#ifndef DRIVER_H
#define DRIVER_H

#include <ntddk.h>
#include <wdf.h>

#define NTSTRSAFE_LIB
#include <ntstrsafe.h>

#define INITGUID
#include <guiddef.h>
#include <wdmguid.h>
#include <ntddser.h>    // Constants and types for access Serial device

#include <BthXDDI.h>    // BT Extensible Transport DDI

#include "device.h"     // Device specific
#include "io.h"         // Read pump
#include "debugdef.h"   // WPP trace
#include "public.h"     // Share between driver and application

#ifdef DEFINE_GUID

//
// Container ID for internally connected device
//
DEFINE_GUID(GUID_CONTAINERID_INTERNALLY_CONNECTED_DEVICE,
        0x00000000, 0x0000, 0x0000, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff);

//{00000000-0000-0000-ffff-ffffffffffff}

#endif // #ifdef DEFINE_GUID

//
// Define HCI event code
//
#ifndef CommandComplete
#define CommandComplete 0x0e
#endif
#ifndef CommandStatus
#define CommandStatus   0x0f
#endif

#define POOLTAG_UARTHCIBUSSAMPLE  'SBHS'  // 'S'erial 'H'ci 'B'us 'S'ample

//
// An ID used to uniquely identify Bluetooth function from other function
// of this multifunction device.
//
#define BLUETOOTH_FUNC_IDS  0x1001


//
// Device's idle state capability
//
typedef enum _IDLE_CAP_STATE {
    IdleCapActiveOnly   = 1,   // Support active only (cannot idle)
    IdleCapCanWake      = 2,   // Can enter D2 (idle) and remote wake to save power while in idle state.
    IdleCapCanTurnOff   = 3    // Can enter D3 (off) and not remote wake to save max power while device is off.
} IDLE_CAP_STATE;

//
// Reset recovery support capability. Multiple implementations are provided to choose from and
// customize as appropriate.
//
typedef enum _RESET_RECOVERY_SUPPORT_TYPE {
    ResetRecoveryTypeNone                       = 0,    // Reset-recovery is not implemented in the driver. This may be
                                                        // used if the stack does not support reset-recovery at all, or
                                                        // if ACPI firmware already implements reset-recovery in the context
                                                        // of the child device stack (i.e., the stack that loads on the
                                                        // PDO created by this driver). In that case, the bus driver does
                                                        // not need any additional code to support reset-recovery. However,
                                                        // it does need to support GUID_REENUMERATE_SELF_INTERFACE_STANDARD,
                                                        // which is most easily achieved by using WDF dynamic-enumeration
                                                        // for creating PDOs.

    ResetRecoveryTypeParentResetInterface       = 1,    // Delegate to GUID_DEVICE_RESET_INTERFACE_STANDARD
                                                        // support in the parent stack. The parent stack, i.e., the
                                                        // stack on which this driver loads as the FDO, must have
                                                        // support for GUID_DEVICE_RESET_INTERFACE_STANDARD. This is
                                                        // typically provided by the ACPI bus driver, if the ACPI device
                                                        // supports D3Cold (_PR3) or a power resource for reset (_PRR +
                                                        // _RST). See the documentation of
                                                        // GUID_DEVICE_RESET_INTERFACE_STANDARD for more information.

    ResetRecoveryTypeDriverImplemented          = 2,    // Reset recovery is implemented in the driver.
                                                        // Hardware-specific techniques are used to power-cycle the controller.

} RESET_RECOVERY_TYPE;

//
// Driver author may choose whichever reset-recovery strategy works best for their platform.
//
#define SUPPORTED_RESET_RECOVERY_TYPE ResetRecoveryTypeDriverImplemented

#ifdef DYNAMIC_ENUM
//
// The goal of the identification and address description abstractions is that enough
// information is stored for a discovered device so that when it appears on the bus,
// the framework (with the help of the driver writer) can determine if it is a new or
// existing device.  The identification and address descriptions are opaque structures
// to the framework, they are private to the driver writer.  The only thing the framework
// knows about these descriptions is what their size is.
// The identification contains the bus specific information required to recognize
// an instance of a device on its the bus.  The identification information usually
// contains device IDs along with any serial or slot numbers.
// For some buses (like USB and PCI), the identification of the device is sufficient to
// address the device on the bus; in these instances there is no need for a separate
// address description.  Once reported, the identification description remains static
// for the lifetime of the device.  For example, the identification description that the
// PCI bus driver would use for a child would contain the vendor ID, device ID,
// subsystem ID, revision, and class for the device. This sample uses only identification
// description.
// On other busses (like 1394 and auto LUN SCSI), the device is assigned a dynamic
// address by the hardware (which may reassigned and updated periodically); in these
// instances the driver will use the address description to encapsulate this dynamic piece
// of data.    For example in a 1394 driver, the address description would contain the
// device's current generation count while the identification description would contain
// vendor name, model name, unit spec ID, and unit software version.
//
typedef struct _PDO_IDENTIFICATION_DESCRIPTION
{
    WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; // should contain this header

    //
    // Unique serail number of the device on the bus
    //
    ULONG SerialNo;

    size_t CchHardwareIds;

    _Field_size_bytes_(CchHardwareIds) PWCHAR HardwareIds;

} PDO_IDENTIFICATION_DESCRIPTION, *PPDO_IDENTIFICATION_DESCRIPTION;
#endif  // #ifdef DYNAMIC_ENUM

typedef struct _UART_READ_CONTEXT *PUART_READ_CONTEXT;

//
// Bus driver's FDO (Function Device Object) extension structure used to maintain device
// properties and state.
//

typedef struct _FDO_EXTENSION
{
    WDFWAITLOCK ChildLock;

    //
    // Radio On/Off state
    //
    BOOLEAN IsRadioEnabled;

    //
    // WDF Device handle
    //
    WDFDEVICE WdfDevice;

    //
    // Serial port IO Target where we send IOCTL/READ/WRITE reuquest to
    //
    WDFIOTARGET IoTargetSerial;

    //
    // (optional) GPIO IO Target to enable serial bus device
    //
    WDFIOTARGET IoTargetGPIO;

    //
    // Bluetooth child dev node (PDO) capabilities
    //
    BTHX_CAPABILITIES  BthXCaps;

    //
    // Indicator if UART is properly initialize; may require re-inialization
    // when tranistion from exiting D0 to resume D0.
    //
    BOOLEAN DeviceInitialized;

    //
    // Cached UART controller connection IDs
    //
    LARGE_INTEGER UARTConnectionId;

    //
    // Cached I2C controller connection IDs
    //
    LARGE_INTEGER I2CConnectionId;

    //
    // Cached GPIO controller connection IDs
    //
    LARGE_INTEGER GPIOConnectionId;

    //
    // Preallocate WDF Requests for synchronous operation like serial port settings
    //
    WDFREQUEST  RequestIoctlSync;

    //
    // Preallocate WDF Requests to wait on serial error event
    //
    WDFREQUEST  RequestWaitOnError;

    //
    // Data return from serial event wait mask IOCTL
    //
    ULONG   SerErrorMask;

    //
    // WDM memory use for Wait Mask event
    //
    WDFMEMORY   WaitMaskMemory;

    //
    // Set if a hardware error (e.g. data overrun in UART FIFO) is detected
    //
    BOOLEAN    HardwareErrorDetected;

    //
    // Indication the state of the read pump (TRUE = active)
    //
    BOOLEAN     ReadPumpRunning;

    //
    // Track number of out-of-sync error that has been detected
    //
    ULONG   OutOfSyncErrorCount;

    //
    // Locks for synchronization for list and queue
    //
    WDFSPINLOCK  QueueAccessLock;

    //
    // Track next packet read (one and only one)
    //
    UART_READ_CONTEXT ReadContext;

    //
    // Preallocated local WDF requested and memory object that is reused to
    // implement read pump
    //
    WDFREQUEST  ReadRequest;
    WDFMEMORY   ReadMemory;
    UCHAR       ReadBuffer[MAX_H4_HCI_PACKET_SIZE];

#if DBG
    //
    // Track last completed HCI packet
    //
    UCHAR       LastPacket[MAX_H4_HCI_PACKET_SIZE];
    ULONG       LastPacketLength;
#endif
    //
    // WDF Queue for HCI event Request and total number of such request recevied
    //
    WDFQUEUE    ReadEventQueue;
    LONG        EventQueueCount;

    //
    // List to store (prefetched) incoming HCI events and number of entries
    //
    LIST_ENTRY  ReadEventList;
    LONG        EventListCount;

    //
    // WDF Queue for HCI read data Request and total number of such request recevied
    //
    WDFQUEUE    ReadDataQueue;
    LONG        DataQueueCount;

    //
    // List to store (prefetched) incoming HCI data and number of entries
    //
    LIST_ENTRY  ReadDataList;
    LONG        DataListCount;

    //
    // Counts used to track HCI requests received and completed for various packet types
    //
    LONG       CntCommandReq;          // Track total number of HCI command Requests
    LONG       CntCommandCompleted;    // Number of HCI Command completed

    LONG       CntEventReq;            // Track total number of HCI Event Requests
    LONG       CntEventCompleted;      // Number of HCI Command completed

    LONG       CntWriteDataReq;        // Track total number of HCI Write Data requests
    LONG       CntWriteDataCompleted;  // Number of HCI (write) Data completed

    LONG       CntReadDataReq;         // Track total number of HCI Read Data Requests
    LONG       CntReadDataCompleted;   // Number of HCI (Read) Data completed
} FDO_EXTENSION, *PFDO_EXTENSION;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_EXTENSION, FdoGetExtension)

//
// Can send IO only if the device (UART) is in the initialized state.
//
#define IsDeviceInitialized(FdoExtension) (FdoExtension->DeviceInitialized)

#define ValidConnectionID(ConnectionId) (ConnectionId.QuadPart != 0)

//
// Bus driver's child PDO (Physical Device Object) extension structure used to maintain this
// PDO's device properties and state.
//

typedef struct _PDO_EXTENSION
{
    //
    // Back pointer to FDO_EXTENSION
    //
    PFDO_EXTENSION FdoExtension;

    //
    // Unique serial number of the device on the bus
    //
    ULONG SerialNo;

    //
    // Type of reset-recovery support implemented.
    //
    RESET_RECOVERY_TYPE ResetRecoveryType;

} PDO_EXTENSION, *PPDO_EXTENSION;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_EXTENSION, PdoGetExtension)

//
// Prototypes of functions
//

//
// Driver.c
//

EVT_WDF_OBJECT_CONTEXT_CLEANUP DriverCleanup;

VOID
DriverSetDeviceCallbackEvents(
    _In_  PWDFDEVICE_INIT  _DeviceInit
    );

EVT_WDF_DRIVER_DEVICE_ADD DriverDeviceAdd;

DRIVER_INITIALIZE DriverEntry;

//
// FDO.c
//

NTSTATUS
HlpInitializeFdoExtension(WDFDEVICE   _Device);

NTSTATUS
FdoWriteDeviceIO(_In_ WDFREQUEST        _RequestFromBthport,
                 _In_ WDFDEVICE         _Device,
                 _In_ PFDO_EXTENSION    _FdoExtension,
                 _In_ PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext);

NTSTATUS
FdoWriteToDeviceSync(_In_ WDFIOTARGET  _IoTargetSerial,
                     _In_ WDFREQUEST   _RequestWriteSync,
                     _In_ ULONG        _IoControlCode,
                     _In_opt_ ULONG    _InBufferSize,
                     _In_opt_ PVOID    _InBuffer,
                     _Out_ PULONG_PTR  _BytesWritten);

NTSTATUS
DeviceConfigWaitOnError(_In_ WDFIOTARGET    _IoTargetSerial,
                        _In_ WDFREQUEST     _RequestWaitOnError,
                        _In_ WDFMEMORY      _WaitMaskMemory,
                        _In_ PULONG         _ErrorResult,
                        _In_ PFDO_EXTENSION _FdoExtension);

NTSTATUS
HCIContextValidate(ULONG Index,
                   PBTHX_HCI_READ_WRITE_CONTEXT _HCIContext);

// Power policy events
EVT_WDF_DEVICE_ARM_WAKE_FROM_S0     FdoEvtDeviceArmWake;
EVT_WDF_DEVICE_DISARM_WAKE_FROM_S0  FdoEvtDeviceDisarmWake;

EVT_WDF_DEVICE_ARM_WAKE_FROM_SX     FdoEvtDeviceArmWake;
EVT_WDF_DEVICE_DISARM_WAKE_FROM_SX  FdoEvtDeviceDisarmWake;

// PnP events
EVT_WDF_DEVICE_PREPARE_HARDWARE  FdoDevPrepareHardware;
EVT_WDF_DEVICE_RELEASE_HARDWARE  FdoDevReleaseHardware;

// Power events
EVT_WDF_DEVICE_D0_ENTRY          FdoDevD0Entry;
EVT_WDF_DEVICE_D0_EXIT           FdoDevD0Exit;

EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT  FdoDevSelfManagedIoInit;
EVT_WDF_DEVICE_SELF_MANAGED_IO_CLEANUP  FdoDevSelfManagedIoCleanup;

// Queue
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL FdoIoQuDeviceControl;

// PDO creation

#ifdef DYNAMIC_ENUM

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
PdoResetHandlerDynamic(_In_ PVOID              _InterfaceContext,
                       _In_ DEVICE_RESET_TYPE  _ResetType,
                       _In_ ULONG              _Flags,
                       _In_opt_ PVOID          _ResetParameters);

EVT_WDF_CHILD_LIST_CREATE_DEVICE FdoEvtDeviceListCreatePdo;

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
PdoCreateDynamic(_In_ WDFDEVICE       Device,
                 _In_ PWDFDEVICE_INIT DeviceInit,
                 _In_ PWCHAR          HardwareIds,
                 _In_ ULONG           SerialNo);

NTSTATUS
FdoCreateOneChildDeviceDynamic(_In_ WDFDEVICE  _Device,
                               _In_ PWCHAR     _HardwareIds,
                               _In_ size_t     _CchHardwareIds,
                               _In_ ULONG      _SerialNo);
#endif

EVT_WDF_DEVICE_DISABLE_WAKE_AT_BUS PdoDevDisableWakeAtBus;
EVT_WDF_DEVICE_ENABLE_WAKE_AT_BUS  PdoDevEnableWakeAtBus;

NTSTATUS
FdoCreateOneChildDevice(_In_ WDFDEVICE  _Device,
                        _In_ PWCHAR     _HardwareIds,
                        _In_ ULONG      _SerialNo);

NTSTATUS
FdoCreateAllChildren(_In_ WDFDEVICE _Device);

NTSTATUS
FdoRemoveOneChildDevice(WDFDEVICE   _Device,
                        ULONG       _SerialNo);

NTSTATUS
FdoFindConnectResources(_In_ WDFDEVICE    _Device,
                        _In_ WDFCMRESLIST _ResourcesRaw,
                        _In_ WDFCMRESLIST _ResourcesTranslated);

//
// Pdo.c
//

EVT_WDF_DEVICE_PREPARE_HARDWARE  PdoDevPrepareHardware;
EVT_WDF_DEVICE_RELEASE_HARDWARE  PdoDevReleaseHardware;

EVT_WDF_DEVICE_D0_ENTRY         PdoDevD0Entry;
EVT_WDF_DEVICE_D0_EXIT          PdoDevD0Exit;

EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL PdoIoQuDeviceControl;

_IRQL_requires_max_(PASSIVE_LEVEL)
NTSTATUS
PdoResetHandler(_In_ PVOID              _InterfaceContext,
                _In_ DEVICE_RESET_TYPE  _ResetType,
                _In_ ULONG              _Flags,
                _In_opt_ PVOID          _ResetParameters);

NTSTATUS
PdoCreate(_In_ WDFDEVICE  _Device,
          _In_ PWSTR      _HardwareIds,
          _In_ ULONG      _SerialNo);

VOID
PdoDevDisableWakeAtBus(_In_ WDFDEVICE _Device);

NTSTATUS
PdoDevEnableWakeAtBus(_In_ WDFDEVICE          _Device,
                      _In_ SYSTEM_POWER_STATE _PowerState);

//
// Define in io.c
//
NTSTATUS ReadResourcesAllocate(_In_ WDFDEVICE _Device);
VOID ReadResourcesFree(_In_ WDFDEVICE _Device);

NTSTATUS
HLP_AllocateResourceForWrite(_In_ WDFDEVICE   _Device,
                             _In_ WDFIOTARGET _IoTargetSerial,
                             _Out_ WDFREQUEST *_pRequest);

VOID
HLP_FreeResourceForWrite(PUART_WRITE_CONTEXT _TransferContext);

EVT_WDF_REQUEST_CANCEL CB_RequestFromBthportCancel;

EVT_WDF_REQUEST_COMPLETION_ROUTINE CR_WriteDeviceIO;

NTSTATUS
ReadRequestComplete(_In_ PFDO_EXTENSION    _FdoExtension,
                    _In_ UCHAR             _Type,
                    _In_ ULONG             _PacketLength,
                    _In_reads_bytes_opt_(_PacketLength) PUCHAR _Packet,
                    _Inout_  WDFQUEUE      _Queue,
                    _Inout_  PLONG         _QueueCount,
                    _Inout_  PLIST_ENTRY   _ListHead,
                    _Inout_  PLONG         _ListCount);

EVT_WDF_REQUEST_COMPLETION_ROUTINE ReadH4PacketCompletionRoutine;

NTSTATUS
ReadH4Packet(_In_ PUART_READ_CONTEXT _ReadContext,
             _In_  WDFREQUEST        _WdfRequest,
             _In_  WDFMEMORY         _WdfMemory,
             _Pre_notnull_ _Pre_writable_byte_size_(_BufferLen) PVOID _Buffer,
             _In_  ULONG             _BufferLen);

//
// Device.c
//

VOID
DeviceQueryDeviceParameters(_In_ WDFDRIVER  _Driver);

BOOLEAN
DeviceInitialize(_In_ PFDO_EXTENSION _FdoExtension,
                 _In_ WDFIOTARGET    _IoTargetSerial,
                 _In_ WDFREQUEST     _RequestSync,
                 _In_ BOOLEAN        _ResetUart);

NTSTATUS
DeviceEnableWakeControl(_In_ WDFDEVICE          _Device,
                        _In_ SYSTEM_POWER_STATE _PowerState);
void
DeviceDisableWakeControl(WDFDEVICE _Device);

NTSTATUS
DeviceEnable(_In_ WDFDEVICE _Device,
             _In_ BOOLEAN   _Enabled);

NTSTATUS
DevicePowerOn(_In_ WDFDEVICE _Device);

NTSTATUS
DevicePowerOff(_In_ WDFDEVICE _Device);

VOID
DeviceDoPLDR(_In_ WDFDEVICE _Fdo);

#endif