USB filter driver for supporting USB chargers

Write a filter driver that supports detection of chargers if the function controller uses the in-box Synopsys and ChipIdea drivers. If you are writing a client driver for a proprietary function controller, charger/attach detection is integrated in the client driver by implementing EVT_UFX_DEVICE_PROPRIETARY_CHARGER_SET_PROPERTY, EVT_UFX_DEVICE_PROPRIETARY_CHARGER_RESET, and EVT_UFX_DEVICE_DETECT_PROPRIETARY_CHARGER.

The USB function stack allows the device, such as a phone or tablet, to charge when connected to a host and USB charger as defined by the USB Battery Charging (BC) 1.2 specification.

  • There are two types of ports that the device can use for charging. The device can charge from a dedicated charging port (DCP) on a charger that shipped with the device. Alternately, the device can from standard downstream ports or charging downstream ports when the device is connected to a PC. Both of those cases are compliant with the USB BC 1.2 specification.
  • Certain chargers do not follow the specification. USB function stack allows the device to charge from those proprietary USB chargers.

To support spec-compliant and proprietary chargers, these operations are required.

  • The device is able to detect when a USB host or charger is attached or detached.
  • The device is able to detect the different USB charging ports as defined by the BC 1.2 spec.
  • For USB chargers that are defined by the BC 1.2 spec, the device charges with the maximum amount of current allowed by the BC 1.2 spec.
  • The device is able to detect proprietary USB chargers.
  • For proprietary USB chargers, determine the maximum amount of current that the device can draw.
  • Notify the operating system about the USB port type that is connected.
  • Prevent the device from pulling current over USB in the OS, even if a USB Host is connected and the device has configured itself with the Host.

Those operations are handled by USB function class extension (UFX)/client driver pair and a filter driver that is loaded as a lower filter in the USB function device stack. The driver manage USB charging starting from USB port detection to notifying the battery stack when it can begin charging and the maximum amount of current the device can draw.

Here is an architectural representation of the device stacks.

USB charging

When a USB port is attached to the device, the client driver gets a notified either by the lower filter driver or an interrupt. At this time, the client driver performs port detection by communicating with the USB hardware and reports the port type to UFX. Alternately, it can request the filter driver. In that case, the filter driver coordinates with the USB hardware to perform USB port detection and returns the detected port type to the client driver and the client driver passes it to UFX.

Based on the port type, UFX determines the maximum amount of current that the device can draw and sends that information to the Charging Aggregation Driver (CAD). CAD validates the information. If the current is valid, CAD sends a request to the battery class driver to start charging up to the specified maximum current. The battery class driver forwards the charging request to the battery miniclass driver for processing. If the charging request specified that a proprietary charger was attached and the battery miniclass handles proprietary chargers, the miniclass driver can attempt to charge with a maximum current that it determines is appropriate. Otherwise, the battery miniclass can only charge up to the maximum current that is specified by CAD.

Attach device interface

If USB lower filter driver that is responsible for detecting a charger must publish the USBFN_INTERFACE_ATTACH device interface. During that process it also registers its callback functions that are invoked by the UFX/client driver pair.

After calling WdfDeviceCreate, the driver performs these steps:

Mt188012.wedge(en-us,VS.85).gifTo publish support for attach and detach events, the filter driver must perform these steps:

  1. To support attach and detach interfaces, populate a USBFN_INTERFACE_ATTACH structures. Set pointers to its implementation of these callback functions.

    Callback functionDescription

    USBFN_GET_ATTACH_ACTION

    Gets invoked when charger is attached to the port.

    USBFN_GET_ATTACH_ACTION_ABORT

    Abort an attach-detect operation.

    USBFN_SET_DEVICE_STATE

    Sets the device state and operating bus speed.

     

  2. Initialize a WDF_QUERY_INTERFACE_CONFIG structure by calling WDF_QUERY_INTERFACE_CONFIG_INIT. In this call, pass the populated USBFN_INTERFACE_ATTACH structure and GUID_USBFN_INTERFACE_ATTACH as the InterfaceType value. The GUID is defined in usbfnattach.h.
  3. Call WdfDeviceAddQueryInterface and pass the initialized WDF_QUERY_INTERFACE_CONFIG structure.

If the lower filter driver exposes a USBFN_INTERFACE_ATTACH bus interface, the UFX/client driver pair does not perform its standard software-based port detection. Instead it will call the filter driver's interface’s USBFN_GET_ATTACH_ACTION callback function. The function provides information about the type of port detected and an attach action.

Note  In software based port detection, the client driver performs USB port detection by communicating with the USB hardware. Once the USB port type has been detected, the client driver returns the port type to UFX.
 

For example, when a charger is attached to a system, the system might issue a request to the UFX/client driver pair to determine the type of charger and the configuration properties. The driver pair invokes the lower filter driver's callback function. In that callback function the filter driver reports the charger identifier and the properties. The UFX/client driver pair can complete the request with that information and the system can use that information to configure the charger.

USB proprietary charger detection

During an attach operation, the lower filter can detect the port as a proprietary charger. If the lower filter wants to handle operations for that type of charger it must publish its support through the UFX_INTERFACE_PROPRIETARY_CHARGER device interface. During the publishing process, the driver also registers its implementation of callback functions that the UFX/client driver pair invokes when it gets request for the charger.

Note  If the filter driver returns UsbfnDetectProprietaryPort, it should also return a standard BC 1.2 port type (typically UsbFnDedicatedChargingPort) so that charging can start immediately, and the device can charge at a reduced current level if proprietary detection fails.
 

After calling WdfDeviceCreate, the driver performs these steps

Mt188012.wedge(en-us,VS.85).gifTo publish support for proprietary chargers, the filter driver must perform these steps:

  1. To support proprietary chargers, populate a UFX_INTERFACE_PROPRIETARY_CHARGER structure. Set pointers to its implementation of these callback functions.

    Callback functionDescription

    UFX_PROPRIETARY_CHARGER_DETECT

    Detects if a charger is attached and get details about the charger.

    UFX_PROPRIETARY_CHARGER_SET_PROPERTY

    Set a configurable property on the charger.

    UFX_PROPRIETARY_CHARGER_ABORT_OPERATION

    Aborts an attach-detect operation.

    UFX_PROPRIETARY_CHARGER_RESET_OPERATION

    Resets a charger operation.

     

  2. Initialize a WDF_QUERY_INTERFACE_CONFIG structure by calling WDF_QUERY_INTERFACE_CONFIG_INIT. In this call, pass the populated UFX_INTERFACE_PROPRIETARY_CHARGER structure and GUID_UFX_INTERFACE_PROPRIETARY_CHARGER as the InterfaceType value. The GUID is defined in ufxproprietarycharger.h.
  3. Call WdfDeviceAddQueryInterface and pass the initialized WDF_QUERY_INTERFACE_CONFIG structure.

Upon detection, the UFX/client driver pair invokes the UFX_PROPRIETARY_CHARGER_DETECT callback function. The lower filter driver assigns a charger ID: a GUID that can be used to identify a specific type of charger. If a specific charger is configurable, the driver must also define a list of supported properties and their possible value types. UFX passes the charger ID to CAD, which passes it to the battery miniclass driver supplied by the SoC vendor or OEM. If a specific charger is configurable, the battery miniclass driver determines the appropriate current and/or voltage levels for the charger based on the charger ID and can send an IOCTL_INTERNAL_CONFIGURE_CHARGER_PROPERTY request to UFX. UFX invokes the client driver's EVT_UFX_DEVICE_PROPRIETARY_CHARGER_SET_PROPERTY. The client driver can handle that request or invoke the lower filter driver's UFX_PROPRIETARY_CHARGER_SET_PROPERTY callback that configures the charger.


#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, UsbLowerFilter_EvtDriverDeviceAdd)
#pragma alloc_text (PAGE, UsbLowerFilter_CreateControlDevice)
#pragma alloc_text (PAGE, UsbLowerFilter_EvtIoDeviceControl)
#pragma alloc_text (PAGE, UsbLowerFilter_ProprietaryChargerDetect)
#pragma alloc_text (PAGE, UsbLowerFilter_ProprietaryChargerSetProperty)
#pragma alloc_text (PAGE, UsbLowerFilter_ProprietaryChargerAbortOperation)
#pragma alloc_text (PAGE, UsbLowerFilter_GetAttachAction)
#pragma alloc_text (PAGE, UsbLowerFilter_GetAttachActionAbortOperation)
#pragma alloc_text (PAGE, UsbLowerFilter_SetDeviceState)

#endif


_Must_inspect_result_
__drv_maxIRQL(PASSIVE_LEVEL)
NTSTATUS
UsbLowerFilter_EvtDriverDeviceAdd(
    __in WDFDRIVER Driver,
    __in PWDFDEVICE_INIT DeviceInit
    )
/*++

Routine Description:

    EvtDriverDeviceAdd is called by the framework in response to AddDevice
    call from the PnP manager. We create and initialize a device object to
    represent a new instance of the device.

Arguments:

    Driver - Handle to a framework driver object created in DriverEntry

    DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.

Return Value:

    NTSTATUS

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;

    WDF_PNPPOWER_EVENT_CALLBACKS PnpCallbacks;
    WDF_OBJECT_ATTRIBUTES Attributes = {0};
    WDFDEVICE Device = NULL;
    PPDCP_CONTEXT PdcpContext = NULL;

    WDF_QUERY_INTERFACE_CONFIG InterfaceConfig;
    UFX_INTERFACE_PROPRIETARY_CHARGER PdcpInterface;
    USBFN_INTERFACE_ATTACH UsbfnAttach;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(Driver);


    //
    // Specify that this is a filter driver
    //

    WdfFdoInitSetFilter(DeviceInit);


    //
    // Register for PnP notifications
    //

    WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&PnpCallbacks);
    PnpCallbacks.EvtDeviceD0Entry = UsbLowerFilter_EvtDeviceD0Entry;
    PnpCallbacks.EvtDeviceD0Exit = UsbLowerFilter_EvtDeviceD0Exit;
    WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &PnpCallbacks);


    //
    // Initialize a context area
    //

    WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
        &Attributes,
        PDCP_CONTEXT);


    //
    // Create the device object
    //

    Status = WdfDeviceCreate(&DeviceInit, &Attributes, &Device);
    if (!NT_SUCCESS(Status))
    {
        goto End;
    }

   PdcpContext = DeviceGetUsbLowerFilterContext(Device);

    PdcpContext->PdcpChargerAttached = TRUE;
    PdcpContext->PdcpChargerDetected = FALSE;
    PdcpContext->RejectNextRequest = FALSE;
    PdcpContext->MaxVoltageInmV = 9000;
    PdcpContext->CurrentVoltageInmV = 5000;
    PdcpContext->DetectionDelayInms = 2000;
    PdcpContext->CurrentState = PowerDeviceD0;
    PdcpContext->LastIdleState = PowerDeviceD0;
    PdcpContext->ConfigurationDelayInms = 100;

    //
    // Create our control device
    //
    Status = UsbLowerFilter_CreateControlDevice(Driver, PdcpContext);
    if (!NT_SUCCESS(Status))
    {
        goto End;
    }


    //
    // Publish that we support Proprietary Charging
    //
    PdcpInterface.InterfaceHeader.Size = sizeof(PdcpInterface);
    PdcpInterface.InterfaceHeader.Version = 1;
    PdcpInterface.InterfaceHeader.Context = (PVOID)Device;
    PdcpInterface.InterfaceHeader.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
    PdcpInterface.InterfaceHeader.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
    PdcpInterface.ProprietaryChargerDetect = UsbLowerFilter_ProprietaryChargerDetect;
    PdcpInterface.ProprietaryChargerSetProperty = UsbLowerFilter_ProprietaryChargerSetProperty;
    PdcpInterface.ProprietaryChargerAbortOperation = UsbLowerFilter_ProprietaryChargerAbortOperation;

    WDF_QUERY_INTERFACE_CONFIG_INIT(
        &InterfaceConfig,
        (PINTERFACE)&PdcpInterface,
        &GUID_UFX_INTERFACE_PROPRIETARY_CHARGER,
        NULL);

    Status = WdfDeviceAddQueryInterface(Device, &InterfaceConfig);
    if (!NT_SUCCESS(Status))
    {
        goto End;
    }

    //
    // Create the abort event for charger
    //
    KeInitializeEvent(&PdcpContext->AbortOperation, SynchronizationEvent, FALSE);

    //
    // Publish that we support the USBFN Attach interface
    //
    UsbfnAttach.InterfaceHeader.Size = sizeof(UsbfnAttach);
    UsbfnAttach.InterfaceHeader.Version = 1;
    UsbfnAttach.InterfaceHeader.Context = (PVOID)Device;
    UsbfnAttach.InterfaceHeader.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
    UsbfnAttach.InterfaceHeader.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
    UsbfnAttach.GetAttachAction = UsbLowerFilter_GetAttachAction;
    UsbfnAttach.GetAttachActionAbortOperation = UsbLowerFilter_GetAttachActionAbortOperation;
    UsbfnAttach.SetDeviceState = UsbLowerFilter_SetDeviceState;

    WDF_QUERY_INTERFACE_CONFIG_INIT(
        &InterfaceConfig,
        (PINTERFACE)&UsbfnAttach,
        &GUID_USBFN_INTERFACE_ATTACH,
        NULL);

    Status = WdfDeviceAddQueryInterface(Device, &InterfaceConfig);
    if (!NT_SUCCESS(Status))
    {
        goto End;
    }

    PdcpContext->CurrentDeviceState = UsbfnDeviceStateDefault;
    PdcpContext->BusSpeed = UsbfnBusSpeedHigh;
    PdcpContext->CurrentAttachAction = UsbfnPortDetected;
    PdcpContext->CurrentPortType = UsbfnStandardDownstreamPort;

    //
    // Create the abort event for attach operation
    //
    KeInitializeEvent(&PdcpContext->AbortAttachOperation, SynchronizationEvent, FALSE);

End:
    return Status;
}

 

 

Send comments about this topic to Microsoft

Show: