Writing an MBBCx client driver
Warning
The sequence diagrams in this topic are for illustration purposes only. They are not public contracts and are subject to change in the future.
INF files for MBBCx client drivers
INF files for MBBCx client drivers are the same as other NetAdapterCx client drivers. For more information, see INF files for NetAdapterCx client drivers.
Follow the Universal guidance to ensure that the INF files meet the Universal requirements.
Initialize the device
In addition to those tasks required by NetAdapterCx for NetAdapter device initialization, an MBB client driver must also perform the following tasks in its EvtDriverDeviceAdd callback function:
Call MBB_DEVICE_CONFIG_INIT after calling NetDeviceInitConfig but before calling WdfDeviceCreate, referencing the same WDFDEVICE_INIT object passed in by the framework.
Call MbbDeviceInitialize to register MBB device-specific callback functions using an initialized MBB_DEVICE_CONFIG structure and the WDFDEVICE object obtained from WdfDeviceCreate.
The following example demonstrates how to initialize the MBB device. Error handling has been left out for clarity.
status = NetDeviceInitConfig(deviceInit);
status = MbbDeviceInitConfig(deviceInit);
// Set up other callbacks such as Pnp and Power policy
status = WdfDeviceCreate(&deviceInit, &deviceAttributes, &wdfDevice);
MBB_DEVICE_CONFIG mbbDeviceConfig;
MBB_DEVICE_CONFIG_INIT(&mbbDeviceConfig,
EvtMbbDeviceSendMbimFragment,
EvtMbbDeviceReceiveMbimFragment,
EvtMbbDeviceSendServiceSessionData,
EvtMbbDeviceCreateAdapter);
status = MbbDeviceInitialize(wdfDevice, &mbbDeviceConfig);
Unlike other types of NetAdapterCx drivers, MBB client drivers must not create the NETADAPTER object from within the EvtDriverDeviceAdd callback function. Instead, it will be instructed by MBBCx to do so later.
Next, the client driver must call MbbDeviceSetMbimParameters, typically in the EvtDevicePrepareHardware callback function that follows.
This message flow diagram illustrates the initialization process.
This message flow diagram illustrates the initialization process.
Handling MBIM control messages
MBBCx uses the standard MBIM control commands defined in MBIM specification Rev 1.0, sections 8, 9, and 10, for the control plane. Commands and responses are exchanged through a set of callback functions provided by the client driver and APIs provided by MBBCx. MBBCx mimics the operational model of an MBIM device, as defined in MBIM specification Rev 1.0, section 5.3, by using these function calls:
- MBBCx sends an MBIM command message to the client driver by invoking its EvtMbbDeviceSendMbimFragment callback function. The client driver asynchronously completes this send request by calling MbbRequestComplete.
- The client driver signals availability of the result by calling MbbDeviceResponseAvailable.
- MBBCx fetches the MBIM response message from the client driver by invoking its EvtMbbDeviceReceiveMbimFragment callback function. The client driver asynchronously completes this get-response request by calling MbbRequestCompleteWithInformation.
- The MBB client driver may notify MBBCx of an unsolicited device event by calling MbbDeviceResponseAvailable. MBBCx then retrieves the information from the client driver similarly to how it fetches MBIM response messages.
The following diagram illustrates MBBCx-client driver message exchange flow.
Synchronization of MBIM control messages
The MBBCx framework always serializes calls into the client driver's EvtMbbDeviceSendMbimFragment and EvtMbbDeviceReceiveMbimFragment callback functions. No new calls will be made by the framework until the client driver calls either MbbRequestComplete or MbbRequestCompleteWithInformation.
While a client driver is guaranteed not to receive overlapped EvtMbbDeviceSendMbimFragment or EvtMbbDeviceReceiveMbimFragment callbacks, it may receive multiple calls to them in succession before the response for a previous command is available from the device.
If the device is not in the D0 state, the MBBCx framework will first bring the device to D0 (in other words, it calls EvtDeviceD0Entry) before it calls EvtMbbDeviceSendMbimFragment or EvtMbbDeviceReceiveMbimFragment. The MBBCx framework also guarantees that it will keep the device in the D0 state, meaning it will not call EvtDeviceD0Exit, until the client calls MbbRequestComplete or MbbRequestCompleteWithInformation.
Creating the NetAdapter interface for the PDP context/EPS bearer
Before establishing a data session, MBBCx will instruct the client driver to create a NETADAPTER object, and it will be used by MBBCx to represent the network interface for the data session activated. This is accomplished by MBBCx calling into the client driver's EvtMbbDeviceCreateAdapter callback function.
In the implementation of the EvtMbbDeviceCreateAdapter callback function, the MBBCx client driver must first perform the same tasks required for creating a NETADAPTER object as any NetAdapterCx client driver. Additionally, it must also perform the following additional tasks:
Call MbbAdapterInitialize on the NETADAPTER object created by NetAdapterCreate.
After calling MbbAdapterinitialize, call MbbAdapterGetSessionId to retreive the data session ID for which MBBCx intends to use this NETADAPTER object. For example, if the returned value is 0, it means MBBCx will use this NETADAPTER interface for the data session established by the primary PDP context/default EPS bearer.
We recommend that MBBCx client drivers keep an internal mapping between the created NETADAPTER object and the returned SessionId. This helps track the data session-to-NETADAPTER object relationship, which is especially useful when multiple PDP contexts/EPS bearers have been activated.
Before returning from EvtMbbDeviceCreateAdapter, client drivers must start the adapter by calling NetAdapterStart. Optionally, they can also set the adapter's capabilities by calling one or more of these functions before the call to NetAdapterStart:
MBBCx invokes this callback function at least once, so there is always one NETADPATER object for the primary PDP context/default EPS bearer. If multiple PDP contexts/EPS bearers are activated, MBBCx might invoke this callback function more times, once for every data session to be established. There must be a one-to-one relationship between the network interface represented by the NETADAPTER object and a data session, as shown in the following diagram.
The following example shows how to create a NETADAPTER object for a data session. Note that error handling and code required for setting up adapter capabilities is left out for brevity and clarity.
NTSTATUS
EvtMbbDeviceCreateAdapter(
WDFDEVICE Device,
PNETADAPTER_INIT AdapterInit
)
{
// Get the client driver defined per-device context
PMY_DEVICE_CONTEXT deviceContext = MyGetDeviceContext(Device);
// Set up the client driver defined per-adapter context
WDF_OBJECT_ATTRIBUTES adapterAttributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&adapterAttributes,
MY_NETADAPTER_CONTEXT);
// Create the NETADAPTER object
NETADAPTER netAdapter;
NTSTATUS status = NetAdapterCreate(AdapterInit,
&adapterAttributes,
&netAdapter);
// Initialize the adapter for MBB
status = MbbAdapterInitialize(netAdapter);
// Retrieve the Session ID and use an array to store
// the session <-> NETADAPTER object mapping
ULONG sessionId;
PMY_NETADAPTER_CONTEXT netAdapterContext = MyGetNetAdapterContext(netAdapter);
netAdapterContext->NetAdapter = netAdapter;
sessionId = MbbAdapterGetSessionId(netAdapter);
netAdapterContext->SessionId = sessionId;
deviceContext->Sessions[sessionId].NetAdapterContext = netAdapterContext;
//
// Optional: set adapter capabilities
//
...
NetAdapterSetDatapathCapabilities(netAdapter,
&txCapabilities,
&rxCapabilities);
...
NetAdapterSetLinkLayerCapabilities(netAdapter,
&linkLayerCapabilities);
...
NetAdapterSetLinkLayerMtuSize(netAdapter,
MY_MAX_PACKET_SIZE - ETHERNET_HEADER_LENGTH);
//
// Required: start the adapter
//
status = NetAdapterStart(netAdapter);
return status;
}
For a code example of setting datapath capabilities, see Network data buffer management.
MBBCx guarantees that it calls EvtMbbDeviceCreateAdapter before requesting MBIM_CID_CONNECT with the same session ID. The following flow diagram shows the interactions between the client driver and the class extension in creating the NETADAPTER object.
The flow for creating the NETADAPTER object for the primary PDP context/default EPS bearer is initiated by MBBCx when EvtDevicePrepareHardware has successfully finished.
The flow for creating the NETADAPTER object for the secondary PDP context/dedicated EPS bearer is triggered by WwanSvc whenever on-demand connections are requested by applications.
Lifetime of the NETADAPTER object
The NETADAPTER object created by the client driver will be automatically destroyed by MBBCx when it's no longer in use. For example, this happens after additional PDP context/EPS bearers are deactivated. MBBCx client drivers must not call WdfObjectDelete on the NETADAPTER objects they create.
If a client driver needs to clean up context data tied to a NETADAPTER object, it should provide an EvtDestroyCallback function in the object attributes structure when calling NetAdapterCreate.
Power management of the MBB device
For power management, client drivers should use the NETPOWERSETTINGS object like other types of NetAdapterCx client drivers.
Handling device service sessions
When an application sends DSS data down to the modem device, MBBCx invokes the client driver's EvtMbbDeviceSendServiceSessionData callback function. The client driver should then send the data asynchronously to the device and call MbbDeviceSendDeviceServiceSessionDataComplete once the send has completed, so MBBCx then can free the memory allocated for the data.
Conversely, the client driver calls MbbDeviceReceiveDeviceServiceSessionData to pass any data up to the application through MBBCx.
Windows Drivers Compliant requirements
- Comply with DCH Design Principles
- Follow the Driver Package Isolation principles
- Follow API Layering Requirements
- Certify with the Windows Hardware Compatibility Program Certification Process using the Hardware Lab Kit