Implementing Commands, Callbacks, and Events Handlers for an Extension Layer (Windows CE 5.0)
Interfacing between layers is achieved through callback tables. Each layer consumes two callback tables from each protocol layer and provides each layer with one interface table. An intermediate layer interface consists of commands, callbacks, and events.
All Bluetooth stack layers, from HCI and up, follow the same guidelines for exporting interfaces and connecting to lower-level stacks.
Note The terms LAYER_ and layer_ are generic placeholders and are used in this section instead of real stack layer prefixes such as HCI_, L2CAP_, SDP_, RFCOMM_, hci_, l2cap_, sdp_, rfcomm_ prefixes that are part of real names.
The following list describes some stack extension layer design considerations:
- 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 within 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 due to communication loss, timeout, or hardware removal.
- Always process layer_StackEvent to notify the extension layer about the events generated by the stack. For more information about these events, see IO Control and Other Shared Functions.
To implement the extension layer commands, callbacks, and event handlers
- Create commands that the extension layer will support.
Each layer implements 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, the commands that the RFCOMM layer supports are declared in a structure named RFCOMM_INTERFACE.
A call to the Layer_EstablishDeviceContext function returns a pointer to a structure of type LAYER_INTERFACE.
The following code example shows the function pointer declaration for the RFCOMM_ConnectRequest_In command.
typedef int (*RFCOMM_ConnectRequest_In)( HANDLE hDeviceContext, void* pCallContext, BD_ADDR* pba, unsigned char channel );
RFCOMM_ConnectRequest_In requests the RFCOMM layer to establish a connection to the channel on the device specified by pba. The hDeviceContext parameter is the handle to the lower layer that was returned by Layer_EstablishDeviceContext. The pCallContext parameter is a pointer to the cookie that is used by the response callback.
- Implement callback functions for each command supported by the layer that is being extended.
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, the callbacks for the RFCOMM layer are defines in a structure named RFCOMM_CALLBACKS.
When you extend the RFCOMM layer, the extension layer must implement callback functions for each command that the RFCOMM layer supports. The following code example shows the function pointer declaration for the callback, RFCOMM_ConnectRequest_Out of the RFCOMM_ConnectRequest_In command.
typedef int (*RFCOMM_ConnectRequest_Out)( void* pCallContext, int iError, HANDLE hConnection );
When the call to RFCOMM_ConnectRequest_In completes, RFCOMM calls RFCOMM_ConnectRequest_Out implemented in the extension layer, by passing the call context and a handle to the established connection. If an error occurs, then the error code is returned in iError.
For each of the commands that the extension layer supports, declare function pointers to the corresponding callback functions in a structure named LAYER_CALLBACKS, where LAYER is the name of the extension layer.
- Create event handlers for the events that the extended layer raises.
Events are raised either as a result 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.
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 needs to 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.
The following code example shows the function pointer declaration for the event L2CA_ConnectInd.
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.Note Not all event and callbacks must be provided, and not all interface functions must be implemented. Passing NULL in the event, the callback, or the interface table indicates an unimplemented function. Lower layers of the stack must accept any level of implementation, including all NULL values, in events and callbacks. Upper layers must check for sufficiency of the lower-layer implementation.
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 invalid parameters or low memory conditions for example, 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.
Each function pointer from the interface table takes a void* cookie that uniquely identifies the call context to the calling layer.
The upper layer can terminate the call by calling the LAYER_INTERFACE::layer_AbortCall function of the lower layer. The call can also be terminated by an underlying system due to communication loss, timeout, or hardware removal. If such a condition occurs, layer_CallAborted is called with this cookie.
If the lower layer completes the call successfully, the lower layer calls one of the callbacks provided by the upper layer.
If the lower stack generates events that are asynchronous and not directly related to commands executed by an upper layer, they are passed to the upper layer through LAYER_EVENT_INDICATION callbacks.
Send Feedback on this topic to the authors