Implement the Bluetooth Stack Data Structures (Compact 7)
3/12/2014
An extension layer interfaces with a specific layer of the Bluetooth stack through a set of three primary data structures. Each data structure contains function pointers for commands, callbacks, or events. These structures are defined in Bt_ddi.h, the header file that contains the Bluetooth APIs that are used to create a Bluetooth stack extension layer. You can find this file at %_WINCEROOT%\\public\COMMON\sdk\inc.
The following table shows the data structures that you can use to create an extension layer for the Bluetooth stack. The name of each structure is preceded by the name of the layer that it applies to.
Layer | Structures |
---|---|
HCI |
HCI_INTERFACEHCI_CALLBACKSHCI_EVENT_INDICATION |
L2CAP |
L2CAP_INTERFACEL2CAP_CALLBACKSL2CAP_EVENT_INDICATION |
RFCOMM |
RFCOMM_INTERFACERFCOMM_CALLBACKSRFCOMM_EVENT_INDICATION |
Note
Because this article references sample code that demonstrates an L2CAP layer implementation, the topics that follow focus on L2CAP_INTERFACE, implementation detailsL2CAP_CALLBACKS, L2CAP_EVENT_INDICATION, L2CAP_EstablishDeviceContext, and L2CAP_CloseDeviceContext respectively.
Implementation
To ensure that your code is stable and recovers quickly from unexpected situations, you must do the following:
- Always provide error handling for all the stack operations because these operations can unexpectedly fail.
- Never call into upper or lower layers of the stack in a critical section because the command always completes on a different thread.
- Always process Layer_CallAborted to handle failures due to a command being terminated by an underlying system after communication loss, time-out, or hardware removal.
- Always process Layer_StackEvent to notify the extension layer about the events generated by the stack.
To implement the data structures that interface with the Bluetooth stack, and the programming elements that must be implemented for each step, you must perform the following tasks:
- Implement the layer interface commands in LAYER_INTERFACE.
- Implement callbacks in LAYER_CALLBACKS.
- Create event handlers in LAYER_EVENT_INDICATION.
- Establish a connection to the layer in Layer_EstablishDeviceContext.
- Close the connection to the layer in Layer_CloseDeviceContext.
The code examples in this topic show how to perform these tasks.
Note
LAYER_ and Layer_ are place holders for the name of the layer that the programming element applies to.
You do not have to provide all event and callbacks, or all interface functions. You can pass NULL in an event, callback, or interface table to indicate an unimplemented function. Lower layers of the stack accept any level of implementation, including all NULL values, in events and callbacks. Upper layers check for sufficiency of the lower-layer implementation.
Implement Commands
For the stack extension layer that you are extending, you must implement a set of commands that can be called by the layers above it in the Bluetooth protocol stack.
The function pointers to the commands that a layer supports are declared in a structure named LAYER_INTERFACE. For example, L2CAP_INTERFACE contains one entry point for each L2CAP-level Bluetooth command as defined by the L2CAP specification in the Bluetooth Core Specification. A call to the L2CAP_EstablishDeviceContext function returns a pointer a structure of type L2CAP_INTERFACE.
Note
For detailed information about the Bluetooth architecture for Compact 7, see the Bluetooth Architecture.
Each function pointer from the interface table takes a void* which is a cookie that uniquely identifies the call context to the caller layer. The upper layer can abort the call by calling the L2CAP_AbortCall function of the lower layer. The underlying system can also abort the call due to communication loss, time-out, card being removed or other reasons, in which case, the system calls L2CAP_CallAborted with this cookie.
When the layer issues a call to a command implemented by a lower layer, it calls a function pointer defined by LAYER_INTERFACE. If the call immediately fails, for example, because it has invalid parameters or low memory, the function returns a non-zero error value. If there are enough resources to schedule the command for execution, it is queued and the function returns ERROR_SUCCESS.If the lower layer successfully completes the call because the call was not aborted, the lower layer calls one of the callbacks that the upper layer provides. Every _In command from L2CAP_INTERFACE has a corresponding _Out command in L2CAP_CALLBACKS.
If the lower stack generates an event, an asynchronous event not directly (except in L2CAP) related to commands executed by upper layer, the stack passes it to the upper layer through LAYER_EVENT_INDICATION callbacks.
The following example code from the Bt_ddi.h header file shows the data structure that contains the L2CAP commands.
struct _L2CAP_INTERFACE {
L2CA_ConnectReq_In l2ca_ConnectReq_In; // Call to send a Connection Request
L2CA_ConnectResponse_In l2ca_ConnectResponse_In; // Call to send a Connection Response
L2CA_ConfigReq_In l2ca_ConfigReq_In; // Call to send a Configuration Request
L2CA_ConfigResponse_In l2ca_ConfigResponse_In; // Call to send a Configuration Response
L2CA_Disconnect_In l2ca_Disconnect_In; // Call to send a Disconnection Request
L2CA_DataDown_In l2ca_DataDown_In; // Call to send data
L2CA_GroupCreate_In l2ca_GroupCreate_In;
L2CA_GroupClose_In l2ca_GroupClose_In;
L2CA_GroupAddMember_In l2ca_GroupAddMember_In;
L2CA_GroupRemoveMember_In l2ca_GroupRemoveMember_In;
L2CA_GroupMembership_In l2ca_GroupMembership_In;
L2CA_Ping_In l2ca_Ping_In;
L2CA_GetInfo_In l2ca_GetInfo_In;
L2CA_DisableCLT_In l2ca_DisableCLT_In;
L2CA_EnableCLT_In l2ca_EnableCLT_In;
BT_LAYER_IO_CONTROL l2ca_ioctl;
BT_LAYER_ABORT_CALL l2ca_AbortCall;
};
The following example code from the Bt_ddi.h header files shows the declaration of the function pointer for the L2CA_ConnectReq_In command.
typedef int (*L2CA_ConnectReq_In) (HANDLE hDeviceContext, void *pCallContext, unsigned short psm, BD_ADDR *pba);
L2CA_ConnectRequest_In requests the L2CAP layer to establish a connection to the channel on the device specified by phone-book access. The hDeviceContext parameter is the handle to the lower layer that is returned by L2CAP_EstablishDeviceContext. The pCallContextparameter is a pointer to the cookie that is used by the response callback.
An example implementation for L2CAP_INTERFACE can be found in %WINCEROOT%\Public\Common\Oak\Drivers\Bluetooth\Sample\L2capdev\L2capdev.cxx.
Implement Callbacks
Every command that is implemented in a lower layer has a corresponding callback function that must be implemented by the extension layer. These callbacks are defined in a structure named LAYER_CALLBACKS. For example, L2CAP_CALLBACKS contains one entry point for each L2CAP-level Bluetooth command as defined by the L2CAP specification in the Bluetooth Core Specification. When you extend the L2CAP layer, the extension layer must implement callback functions for each command that the L2CAP layer supports.
Commands that terminate with the L2CAP_CommandComplete event have return parameters as part of _Out functions in L2CAP_CALLBACK. Commands that terminate with L2CAP_CommandStatus have a boilerplate completion routine that contains only status. If the status is success, the command terminates when the appropriate event is signaled.
The following example code from the Bt_ddi.h header files shows the data structure that contains the callbacks for the L2CAP layer.
struct _L2CAP_CALLBACKS {
L2CA_ConnectReq_Out l2ca_ConnectReq_Out; // Callback to process a received Connection Response packet
L2CA_ConnectResponse_Out l2ca_ConnectResponse_Out; // Callback after a Connection Response packet has been sent by HCI
L2CA_ConfigReq_Out l2ca_ConfigReq_Out; // Callback to process a received Configure Response packet
L2CA_ConfigResponse_Out l2ca_ConfigResponse_Out; // Callback after a Configuration Response packet has been sent by HCI
L2CA_Disconnect_Out l2ca_Disconnect_Out; // Callback to process a received Disconnect Response packet
L2CA_DataDown_Out l2ca_DataDown_Out; // Callback after a data packet has been transmitted by HCI (Basic mode)
// Callback after a data packet has been acked/timed out (Flow Control/Retransmission modes)
L2CA_GroupCreate_Out l2ca_GroupCreate_Out;
L2CA_GroupClose_Out l2ca_GroupClose_Out;
L2CA_GroupAddMember_Out l2ca_GroupAddMember_Out;
L2CA_GroupRemoveMember_Out l2ca_GroupRemoveMember_Out;
L2CA_GroupMembership_Out l2ca_GroupMembership_Out;
L2CA_Ping_Out l2ca_Ping_Out;
L2CA_GetInfo_Out l2ca_GetInfo_Out;
L2CA_DisableCLT_Out l2ca_DisableCLT_Out;
L2CA_EnableCLT_Out l2ca_EnableCLT_Out;
BT_LAYER_CALL_ABORTED l2ca_CallAborted;
L2CA_ConfigReqEx_Out l2ca_ConfigReqEx_Out; // Callback to process a received Configure Response packet, required to handle non-0 Flags field
};
The following example code from the bt_ddi header file displays the declaration of the function pointer for the L2CA_ConnectReq_Out callback.
typedef int (*L2CA_ConnectReq_Out) (void *pCallContext, unsigned short cid, unsigned short result, unsigned short status);
When the call to L2CAP_ConnectRequest_In completes, L2CAP calls L2CAP_ConnectRequest_Out implemented in the extension layer, by passing the call context and a handle to the established connection.
For each of the commands that the extension layer supports, declare function pointers to the corresponding callback functions in a structure named LAYER_CALLBACKS.
An example implementation for L2CAP_CALLBACKS can be found in %WINCEROOT%\Public\Common\Oak\Drivers\Bluetooth\Sample\L2capdev\L2capdev.cxx.
Create Event Handlers
Events are raised either because of an action by a peer Bluetooth device or by executing a command on a device. Every layer in the Bluetooth protocol stack matches events to commands that are currently in execution and returns the call context of the executing command in pCallContex. The command is completed only after the corresponding event is raised. If the raised event is not in response to an executed command, then pCallContext is NULL.
Note
For information about the pCallContext parameter, see the previous sections, Implement Commands and Implement Callbacks, in this topic.
Extension layers must provide event handlers for all events for which they require notification. For example, if the extension layer is extending the L2CAP layer, and it must be notified of an event that is raised by the L2CAP layer, the extension layer must implement that event handler. The events that can be raised a layer are declared in a structure named LAYER_EVENT_INDICATION. The events that the L2CAP layer raises are declared in a structure named L2CAP_EVENT_INDICATION, which contains the full set of Bluetooth L2CAP events as defined by the L2CAP specification in the Bluetooth Core Specification.
The following example code from the bt_ddi header file displays the data structure that contains the event handlers for the L2CAP layer.
struct _L2CAP_EVENT_INDICATION {
L2CA_ConnectInd l2ca_ConnectInd; // Callback to process a received Connection Request
L2CA_ConfigInd l2ca_ConfigInd; // Callback to process a received Configuration Request
L2CA_DisconnectInd l2ca_DisconnectInd;
L2CA_QoSViolationInd l2ca_QoSViolationInd;
L2CA_DataUpInd l2ca_DataUpInd;
BT_LAYER_STACK_EVENT_IND l2ca_StackEvent;
L2CA_ConfigIndEx l2ca_ConfigIndEx; // Callback to process a received Configuration Request (required when request Flags field is non-0)
};
The following example code from the bt_ddi header file displays the declaration of the function pointer for the L2CA_ConnectInd event.
typedef int (*L2CA_ConnectInd) (void *pUserContext, BD_ADDR *pba, unsigned short cid, unsigned char id, unsigned short psm);
This L2CAP event indicates that there is a connection request from a device with the address of pba on port psm. The channel ID assigned for this connection is passed in cid, if the upper layer chooses to accept it. The **pUserContext **argument is a cookie that stores the user context information.
An example implementation of L2CAP_EVENT_INDICATION can be found in %WINCEROOT%\Public\Common\Oak\Drivers\Bluetooth\Sample\L2capdev\L2capdev.cxx.
Establish Connection to the Layer
To connect to the Bluetooth protocol stack, you must register the extension layer with the layer that it extends. You register your extension layer by calling Layer_EstablishDeviceContext and passing the event and callback structures. This call returns a pointer to the interface structure, as well as a pointer to the device context.
Note
While this call prepares your extension layer for loading, it does not load the layer. You will load the layer later.
The following example code from the sample L2capdev is an excerpt from the l2capdev class constructor that shows how to initialize the extension layer callbacks and events for registration with the L2CAP layer. Registration is complete when you pass the initialized structures to Layer_EstablishDeviceContext.
L2CAP_INTERFACE l2cap_if;
memset (&l2cap_if, 0, sizeof(l2cap_if));
L2CAP_EVENT_INDICATION lei;
memset (&lei, 0, sizeof(lei));
lei.l2ca_ConfigIndEx = l2capdev_ConfigIndEx;
lei.l2ca_ConnectInd = l2capdev_ConnectInd;
lei.l2ca_DataUpInd = l2capdev_DataUpInd;
lei.l2ca_DisconnectInd = l2capdev_DisconnectInd;
lei.l2ca_StackEvent = l2capdev_lStackEvent;
L2CAP_CALLBACKS lc;
memset (&lc, 0, sizeof(lc));
lc.l2ca_CallAborted = l2capdev_lCallAborted;
lc.l2ca_ConfigReqEx_Out = l2capdev_ConfigReqEx_Out;
lc.l2ca_ConfigResponse_Out = l2capdev_ConfigResponse_Out;
lc.l2ca_ConnectReq_Out = l2capdev_ConnectReq_Out;
lc.l2ca_ConnectResponse_Out = l2capdev_ConnectResponse_Out;
lc.l2ca_DataDown_Out = l2capdev_DataDown_Out;
lc.l2ca_Ping_Out = l2capdev_Ping_Out;
lc.l2ca_GetInfo_Out = l2capdev_GetInfo_Out;
lc.l2ca_Disconnect_Out = l2capdev_Disconnect_Out;
When the extension layer makes a call to L2CAP_EstablishDeviceContext, it must pass pointers to L2CAP_EVENT_INDICATION and L2CAP_CALLBACKS structures. In addition, it must receive a pointer to an L2CAP_INTERFACE structure. The following example code from the sample L2capdev shows a call to L2CAP_EstablishDeviceContext in the class constructor.
L2CAP_EstablishDeviceContext (this, L2CAP_PSM_MULTIPLE, &lei, &lc, &l2cap_if, &cHeaders, &cTrailers, &hL2CAP)
Close Connection to the Layer
When you are ready to shut down the extension layer, call the L2CAP_CloseDeviceContext function. All calls that were placed by the extension layer are ended and the connections owned by the layer are closed.
The following example code from the sample L2capdev shows how to close the connection to the L2CAP layer.
L2CAP_CloseDeviceContext(hL2CAP);
See Also
Concepts
Bluetooth Stack Extension Layer
Implement Functions that Setup and Shutdown the Extension Layer
Create an Installable Stream Device Driver for the Extension Layer