APIs for Setting Up/Tearing Down the Extension Layer

All Bluetooth stack layers, from HCI and up, follow the same guidelines for exporting interfaces and connecting to lower-level stacks. The templates for these interfaces are depicted in the following table.

**Note   **The terms LAYER_ and layer_ are generic placeholders 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 table shows the APIs that are provided as reference for facilitating the setting up and tearing down of the extension layer.

Function syntax Description Return value
int
layer_InitializeOnce
(void);
This function is called only once during driver load. It can be called from DllMain and cannot contain anything beyond initialization of synchronization primitives or I/O. Ignored.
int
layer_UninitializeOnce
(void);
This function is called during driver unload. It must be called when the driver has already entered a shutdown state. It can be called from DllMain and can only contain un-initialization of synchronization primitives created in the layer_InitializeOnce function. It is only called once, and the driver is unloaded immediately after that. Ignored.
int
layer_CreateDriverInstance
(void);
This function is used to initialize the stack layer. It is called after layer_InitializeOnce and can count on synchronization primitives being initialized. It can be called out of sequence with layer_CloseDriverInstance and must maintain state and protect against duplicate calls.

This call must start the stack layer, which includes connecting to the bottom layer, preparing for connection requests from top layers, and initializing hardware, if appropriate. The stack layer must be in an operational state for this function to return a success.

If the stack layer is operational, ERROR_SUCCESS is returned. If an error occurs, the appropriate error status from Winerror.h is returned.
int
layer_CloseDriverInstance
(void);
This function is used to un-initialize the stack layer. It is called after layer_InitializeOnce and can count on synchronization primitives being initialized. It can be called out of sequence with layer_CloseDriverInstance and must maintain state and protect against duplicate calls.

This call must stop the stack layer, which includes closing all connections, freeing the interface to the lower layer, notifying the upper layers of its shutdown, destroying all threads, and freeing everything allocated by layer_CreateDriverInstance.

If the stack layer is operational, ERROR_SUCCESS is returned. If an error occurs, the appropriate error status from Winerror.h is returned.

Interfacing between layers is achieved through callback tables. Each layer consumes two callback tables from each upper layer and provides each upper layer with one interface table. An inter-layer interface consists of calls, callbacks, and events.

On registration, the upper layer establishes a connection with the lower layer by supplying a table of events (LAYER_EVENT_INDICATION) and command completion callbacks (LAYER_CALLBACKS). A function table is received back for the lower-level interface (LAYER_INTERFACE).

int LAYER_EstablishDeviceConect(
   void*                       pUserContext,     /*IN*/
   filters for the incomming connection,         /*IN*/
   LAYER_EVENT_INDICATION*     pInd,             /*IN*/
   LAYER_CALLBACKS*            pCall,            /*IN*/
   LAYER_INTERFACE*            pInt,             /*OUT*/
   int*                        pcDataHeaders,    /*OUT*/
   int*                        pcDataTrailers,   /*OUT*/
   HANDLE*                     phDeviceContext   /*OUT*/
);

When the upper layer issues a call to the lower layer, it calls a function pointer in the LAYER_INTERFACE table. If the call immediately fails, such as for incorrect parameters and/or low memory conditions, the function immediately 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 abort the call by calling the LAYER_INTERFACE::layer_AbortCall function. The call can also be aborted by an underlying system due to communication loss, timeout, or hardware removal. If such a condition occurs, the LAYER_CALLBACKS::layer_CallAborted function is called with this cookie.

If the lower layer completes the call successfully, meaning it did not abort, the lower layer calls one of the callbacks provided by the upper layer. Every _In command from LAYER_INTERFACE has a corresponding _Out command in LAYER_CALLBACKS.

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.

Command Example

This example requests the RFCOMM layer to establish a connection to the channel on device *pba. The hDeviceContext parameter is the lower layer's handle that was returned by LAYER_EstablishDeviceContext. The pCallContext parameter is a pointer to the cookie that is used by the response callback.

typedef int (*RFCOMM_ConnectRequest_In)( 
    HANDLE              hDeviceContext, 
    void*               pCallContext, 
    BD_ADDR*            pba, 
    unsigned char       channel
);

Callback Example

typedef int (*RFCOMM_ConnectRequest_Out)( 
    void*               pCallContext, 
    int                 iError, 
    HANDLE              hConnection
);

Event Example

This event indicates that a connection request has come from a device with the address of *pba on port psm. The channel ID assigned for this connection, if the upper layer chooses to accept it, is CID.

typedef int (*L2CA_ConnectInd) (
    void*               pUserContext,
    BD_ADDR*            pba,
    unsigned short      cid,
    unsigned char       id,
    unsigned short      psm
);

**Note   **Not all event and callbacks must be provided, and not all interface functions must be implemented. Passing NULL in the event/callback/interface table indicates an unimplemented function. Lower layers of the stack MUST accept any level of implementation, including all NULLs, in events and callbacks. Upper layers MUST check for sufficiency of the lower-layer implementation.

The pUserContext argument for all callback calls is the same cookie that is passed to LAYER_EstablishDeviceContext as its first argument.

The *pcDataHfeaders and *pcDataTrailers parameters are used to indicate how many bytes the upper layer must pre-allocate for lower-layer headers in the packet.

The upper layer might want to restrict incoming connections that are passed by the lower layer. For example, RFCOMM is only interested in connections on PSM 3, and SDP on PSM 1. They are not interested in connections on any other PSM (protocol/service multiplexer). The SCO stack is only interested in incoming SCO, but not ACL connections. Therefore, every stack maintains one and only one general client who can accept all incoming connections, and multiple stacks that accept connections based on some parameter, such as address or connection type. These parameters are different for different layers and do not intersect for multiple clients of the same layer.

Each incoming connection is checked to match restrictions against existing clients with connection restrictions. If no matching client exists, the generic upper-layer handler is executed.

When the upper layer is ready to shutdown, it calls the LAYER_CloseDeviceConnection function. Calls that were placed by this layer are aborted and the connections, owned by this layer, are closed.

int LAYER_CloseDeviceContext (
HANDLE   hDeviceContext
);

L2CAP

Upper layers of stack may install itself on top of L2CAP using this function.

int L2CAP_EstablishDeviceContext(
   void*                    *pUserContext,      /* IN */
   unsigned short           psm,               /* IN */
   L2CAP_EVENT_INDICATION*  pInd,              /* IN */
   L2CAP_CALLBACKS*         pCall,             /* IN */
   L2CAP_INTERFACE*         pInt,              /* OUT */
   int*                     pcDataHeaders,     /* OUT */
   int*                     pcDataTrailers,    /* OUT */
   HANDLE*                  phDeviceContext    /* OUT */
);

PSM is 0 for single listen-to-all stack, and a number for clients with fixed PSMs; SDP and RFCOMM for example.

Only connection-oriented L2CAP channels are implemented. CLT-related functions are stubbed with NULL. L2CAP accepts all negotiation parameters, but passes it to the user without attempting to enforce or even store it. See the description of isochronous channels above.

See the Bt_hci.h file for complete specifications of the L2CAP interface.

RFCOMM

RFCOMM clients can listen only on a particular channel by passing a non-zero channel argument to the following function:

int RFCOMM_EstablishDeviceContext(
   void*                     *pUserContext,      /* IN */
   unsigned char            channel,            /* IN */
   RFCOMM_EVENT_INDICATION*  pInd,              /* IN */
   RFCOMM_CALLBACKS*         pCall,             /* IN */
   RFCOMM_INTERFACE*         pInt,              /* OUT */
   int*                      pcDataHeaders,     /* OUT */
   int*                      pcDataTrailers,    /* OUT */
   HANDLE                   *phDeviceContext    /* OUT */
);

One and only one general-purpose stack (channel=0) can be installed.

RFCOMM passes negotiated parameters to the top level of the stack, but neither enforces nor stores them. The same is true for model commands and statuses that are used by a port emulation entity.

RFCOMM uses StackEvent to pass flow control status to every client. It will also enforce aggregate, but not connection-based flow control.

See the Bt_ddi.h file for complete specifications of the RFCOMM interface.

See Also

Interface References | Enhancing the Bluetooth Stack

Last updated on Wednesday, April 13, 2005

© 2005 Microsoft Corporation. All rights reserved.