< Querying Bluetooth Device Example | Winsock2 Supported Protocols Main | Winsock 2 & Bluetooth APIs >


 

 

Winsock 2: Other Supported Protocols 4 Part 15

 

 

What do we have in this chapter 4 part 15?

  1. Publishing a Service

  2. SDP Records

  3. Registering a Bluetooth Service Steps and Example

 

Publishing a Service

 

The other side of service discovery is service publication. Bluetooth applications that want to provide a service to other applications must do more than simply create a Bluetooth socket, bind the socket, and call accept as would an IrDA service. In addition to the socket work, the service must publish the details of the service through the SDP API. The actual publication of a service is actually quite simple. All that’s necessary is to call WSASetService(), which is prototyped as:

 

INT WSASetService (LPWSAQUERYSET lpqsRegInfo,

WSAESETSERVICEOP essoperation,

DWORD dwControlFlags);

 

The three parameters are a pointer to a WSAQUERYSET structure; a service operation flag, which needs to be set to RNRSERVICE_REGISTER; and a dwControlFlags parameter set to 0.

If only registration were that simple. The problem isn’t calling the function; it’s composing the SDP data that’s placed in the WSAQUERYSET structure. The dwNameSpace field should be set to NS_BTH. And, as with the discovery process, the blobs are involved. The blob used in setting the service is a BTHNS_SETBLOB structure defined as:

 

typedef struct _BTHNS_SETBLOB {

ULONG* pRecordHandle;

ULONG fSecurity;

ULONG fOptions;

ULONG ulRecordLength;

UCHAR pRecord[1];

} BTHNS_SETBLOB, *PBTHNS_SETBLOB;

 

The first parameter points to a ULONG that will receive a handle for the SDP record being created. The fSecurity and fOptions fields (available in Windows CE) are reserved and should be set to 0. The ulRecord-Length parameter should be set to the length of the SDP record to publish, whereas pRecord is the starting byte of the byte array that is the SDP record to publish.

The following code demonstrates publishing an SDP record. The routine is passed an SDP record and its size. It then initializes the proper structures and calls WSASetService() to publish the record.

 

int PublishRecord (HWND hWnd, PBYTE pSDPRec, int nRecSize, ULONG *pRecord)

{

            BTHNS_SETBLOB *pSetBlob;

            ULONG ulSdpVersion = BTH_SDP_VERSION;

            int rc;

 

            // Zero out the record handle that will be returned by the call

            *pRecord = 0;

 

            // Allocate and init the SetBlob

            pSetBlob = (BTHNS_SETBLOB *)LocalAlloc (LPTR, sizeof (BTHNS_SETBLOB) + nRecSize);

 

            if (!pSetBlob)

                        return -1;

            pSetBlob->pRecordHandle = pRecord;

            pSetBlob->pSdpVersion = &ulSdpVersion;

            pSetBlob->fSecurity = 0;

            pSetBlob->fOptions = 0;

            pSetBlob->ulRecordLength = nRecSize;

            memcpy (pSetBlob->pRecord, pSDPRec, nRecSize);

 

            // Init the container blob

            BLOB blob;

            blob.cbSize = sizeof(BTHNS_SETBLOB) + SDP_RECORD_SIZE - 1;

            blob.pBlobData = (PBYTE) pSetBlob;

 

            // Init the WSAQuerySet struct

            WSAQUERYSET Service;

            memset (&Service, 0, sizeof(Service));

            Service.dwSize = sizeof(Service);

            Service.lpBlob = &blob;

            Service.dwNameSpace = NS_BTH;

 

            // Publish the service

            rc = WSASetService(&Service, RNRSERVICE_REGISTER, 0);

            if (rc == SOCKET_ERROR)

                        rc = GetLastError();

            // Clean up

            LocalFree ((PBYTE)pSetBlob);

            return rc;

 }

 

When the application no longer wants to support the service, it needs to remove the record from the SDP database. Removing the record is accomplished by using WSASetService(), specifying the record handle of the service and the flag RNRSERVICE_DELETE. The record handle is passed in the BTHNS_SETBLOB structure. The other fields of this structure are ignored. The following code shows a routine that unregisters a service.

 

int UnpublishRecord (ULONG hRecord)

{

            ULONG ulSdpVersion = BTH_SDP_VERSION;

            int rc;

            BTHNS_SETBLOB SetBlob;

            BLOB blob;

            WSAQUERYSET Service;

 

            memset (&SetBlob, 0, sizeof (SetBlob));

            SetBlob.pRecordHandle = &hRecord;

            SetBlob.pSdpVersion = &ulSdpVersion;

 

            // Init the container blob

            blob.cbSize = sizeof(BTHNS_SETBLOB);

            blob.pBlobData = (PBYTE) &SetBlob;

 

            // Init the WSAQuerySet struct

            memset (&Service, 0, sizeof(Service));

            Service.dwSize = sizeof(Service);

            Service.lpBlob = &blob;

            Service.dwNameSpace = NS_BTH;

 

            // Unpublish the service

            rc = WSASetService(&Service, RNRSERVICE_DELETE, 0);

            return rc;

}

 

 

 

 

SDP Records

 

The format of the SDP information that’s published is so complex that Windows CE provides a special COM control to construct and de-construct SDP records. Even with the control, parsing SDP records isn’t easy. The first problem is knowing what is required in the SDP record. The information in the SDP record is defined by the Bluetooth specification, and a complete explanation of this data far exceeds the space available in this book for such an explanation.

As a shortcut, many Bluetooth applications compose a generic record, either hand-assembling the record or using an example tool named BthNsCreate (available in the Windows CE SDK) that’s provided in the Platform Builder. These hand-generated records are saved as a byte array in the application. The known offsets where the GUID and the RFCOMM channel are stored are known and are updated in the array at run time. The record is then published using WSASetService(), as shown earlier.

The following code shows a routine that uses a canned SDP record with the GUID of the service and the channel stuffed into the appropriate places in the record.

 

int RegisterService(HWND hWnd, GUID *pguid, byte bChannel, ULONG *pRecord)

{

            // SDP dummy record

            // GUID goes at offset 8

            // Channel goes in last byte of record.

            static BYTE bSDPRecord[ ] = {

                        0x35, 0x27, 0x09, 0x00, 0x01, 0x35, 0x11, 0x1C, 0x00, 0x00, 0x00,

                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

                        0x00, 0x00, 0x09, 0x00, 0x04, 0x35, 0x0C, 0x35, 0x03, 0x19, 0x01,

                        0x00, 0x35, 0x05, 0x19, 0x00, 0x03, 0x08, 0x00};

 

                        // Translate guid into net byte order for SDP record

                        GUID *p = (GUID *)&bSDPRecord[8];

                        p->Data1 = htonl (pguid->Data1); p->Data2 = htons (pguid->Data2);

                        p->Data3 = htons (pguid->Data3);

                        memcpy (p->Data4, pguid->Data4, sizeof (pguid->Data4));

 

                        // Copy channel value into record

                        bSDPRecord[sizeof (bSDPRecord)-1] = bChannel;

                        return PublishRecord (hWnd, bSDPRecord, sizeof (bSDPRecord), pRecord);

}

 

Registering a Bluetooth Service Steps and Example

 

To publish a Bluetooth service record, the application must register the service. Microsoft Windows CE ships a tool, Bthnscreate that you can use to generate a blob that represents the SDP record. You can then register the record by using the standard Winsock function, WSASetService(). Before you register a Bluetooth service, you must have the following information:

 

1.      You have a record file that contains the SDP data. SDP data must be placed within a SEQUENCE...END block. Each block contains a series of [type] [value] pairs. The following code example shows a sample record file.

 

;  Simple data file

1    SEQUENCE

     ; Sequences contain one or more elements.

     UUID16 30000

     UUID32 4000000

     UUID128 12345678-ABCD-AF12-8800-12345678EF12

     END

119  UINT16  19

 

 

For more information about the elements of a record file, see SDP Search and Record Generator Sample.

 

2.      You have the Bthnscreate executable in the release directory on your device. If not, then build this tool by using the source code for Bthnscreate.

 

To register a Bluetooth service by using Winsock:

 

  1. To retrieve the binary service data, run bthnscreate.exe from the command prompt on your target Bluetooth device. Use the -record option and specify the record file as an argument to the tool. Type the following command:

 

bthnscreate -record <record filename>

 

Bthnscreate generates an SDP record in raw binary format. The following example shows the output for the sample record file.

 

const int cSdpRecord = 0x00000043

 

BYTE rgbSdpRecord[] = {

0x35, 0x41, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19,

0x11, 0x06, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35,

0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00,

0x03, 0x08, 0x0a, 0x35, 0x03, 0x19, 0x00, 0x08,

0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65, 0x6e,

0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, 0x00,

0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x06,

0x09, 0x01, 0x00, 0x09, 0x01, 0x00, 0x25, 0x03,

0x46, 0x54, 0x50

};

 

  1. Prepare the caller application by providing Winsock-related data such as the version and implementation details. This data can be retrieved by calling the WSAStartup() function as the following example shows.

 

WSADATA wsd;

WSAStartup (MAKEWORD(2,2), &wsd);

 

  1. Define the SDP record size, the server channel offset, and create an array to store the binary SDP data returned by the Bthnscreate tool in step 1.

 

a.             Define the SDP record size SDP_RECORD_SIZE and set the record size returned in step 1. The following example code shows how to define the record size.

 

#define SDP_RECORD_SIZE 0x00000043

 

b.             Identify the byte that represents the channel in the record file, and define the server channel offset as the index of this byte in the array. For example, for the previous sample, the offset is 32. The following example code defines the server offset.

 

#define SDP_CHANNEL_OFFSET 32

 

c.             Declare a BYTE array, rgbSdpRecord and initialize it with the output values generated by Bthnscreate as the following example shows.

 

static const BYTE rgbSdpRecord[] = {

            0x35, 0x41, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19,

            0x11, 0x06, 0x09, 0x00, 0x04, 0x35, 0x11, 0x35,

            0x03, 0x19, 0x01, 0x00, 0x35, 0x05, 0x19, 0x00,

            0x03, 0x08, 0x0a, 0x35, 0x03, 0x19, 0x00, 0x08,

            //Server offset

            0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65, 0x6e,

            0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09, 0x00,

            0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x06,

            0x09, 0x01, 0x00, 0x09, 0x01, 0x00, 0x25, 0x03,

            0x46, 0x54, 0x50

            };

 

 

 

 

  1. To store information about the service record, create and configure a BLOB structure as the following example code shows.

 

struct

{

            BTHNS_SETBLOB   b;

            unsigned char   uca[SDP_RECORD_SIZE];

} bigBlob;

 

BLOB blob;

 

ULONG ulSdpVersion = BTH_SDP_VERSION;

bigBlob.b.pRecordHandle   = &recordHandle;

bigBlob.b.pSdpVersion     = &ulSdpVersion;

bigBlob.b.fSecurity       = 0;

bigBlob.b.fOptions        = 0;

bigBlob.b.ulRecordLength  = SDP_RECORD_SIZE;

memcpy (bigBlob.b.pRecord, rgbSdpRecord, SDP_RECORD_SIZE);

 

blob.cbSize    = sizeof(BTHNS_SETBLOB) + SDP_RECORD_SIZE - 1;

blob.pBlobData = (PBYTE) &bigBlob;

 

In the preceding example, the structure bigblob declares a BTHNS_SETBLOB variable that stores the service record and information about the record such as the SDP version and size of the record. For BTHNS_SETBLOB, set the following members:

 

a.             pRecordHandle: Set to the address of a ULONG variable that contains the record handle. For new records, this handle should be set to zero (0). During registration, SDP assigns a value to this handle and returns it in an out parameter for WSASetService.

b.             ulRecordLength: Set to SDP_RECORD_SIZE as defined in step 2.

c.             pSdpVersion: Set to the address of a ULONG variable that is assigned the value, BTH_SDP_VERSION, that defines the SDP version and is declared in the header file bthapi.h.

d.             pRecord: Points to the buffer that contains the contents of the BYTE array rgbSdpRecord as defined in step 2.

 

The variable, blob, declared as BLOB type, references bigblob variable and specifies the total size for the record and data.

 

  1. Create a WSAQUERYSET and configure it to register a new Bluetooth service by setting the dwNameSpace to NS_BTH. Specify the service to register by setting the lpBlob to the address of the BLOB variable created in step 3. The following example code shows how to set this variable.

 

WSAQUERYSET Service;

memset (&Service, 0, sizeof(Service));

Service.dwSize = sizeof(Service);

Service.lpBlob = &blob;

Service.dwNameSpace = NS_BTH;

 

  1. To register the new Bluetooth service, call the WSASetService function by specifying the service to register in the lpqsRegInfo parameter and passing the RNRSERVICE_REGISTER flag in the essoperation parameter. The following example code shows how to call WSASetService.

 

int iRet = BthNsSetService (&Service, RNRSERVICE_REGISTER,0);

 

To terminate the use of Winsock services, call the WSACleanup() function. Call WSACleanup() for every successful call to WSAStartup() made by an application.

 

Example on how to search SDP and record generator (for platform Builder, Microsoft Windows CE 5.0) can be found at SDP Search and Record Generator Sample.

 

 


< Querying Bluetooth Device Example | Winsock2 Supported Protocols Main | Winsock 2 & Bluetooth APIs >