Wednesday, October 29, 2008

Enabling Bluetooth Serial Port Profile (SSP) for serial port emulator in Windows Embedded CE 6.0

Normally SSP is used by Hands Free Profile (HFP), Head set profile (HSP) and LAN Access Profile (LAP) in windows Embedded CE 6.0. However for some applications, SSP will be needed directly. Unfortunately there is no direct way of enabling the SSP in Windows CE ( No SYSGEN Variable).
SPP will be accessed through socket programming and COM port emulator. Based on the application requirement, Socket programming will be used for multiple port access and COM port emulator will be used for single port access. To enable the SSP, Application has to export the SSP through the service discovery protocol (SDP).
Exporting an service using SDP protocol is not a straight forward task. All the services are created as binary a record that has to be exported. All the services has mentioned as a unique ID and in the form of GUID. For SSP the GUID is 00001101-0000-1000-8000-00805f9b34fb. Please see the bt_sdp.h in _(WINCEROOT)\public\common\sdk\inc directory for more Bluetooth services. Microsoft has provided the sample applications , which explains the implementation of SSP over socket programming and serial port emulator.SSP has been exported in the socket programming example. Unfortunately port emulation code does not having the SSP export over SDP. Followings are the ways to create and export the SSP record.
1) Creating the service records dynamically through source code.
Creating a SDP record is done by using the Component Object Model programming. Writing the source code to create a service record is bit difficult for naive users. To facilitate the naïve users, Microsoft has given a sample application to create the source code for service as well as the binary record for the corresponding services. This source code is available in the _(WINCEROOT)\PUBLIC\COMMON\SDK\SAMPLES\BLUETOOTH\BTHNSCREATE directory. You can add this created source code to your application to invoke the service.
2) Using the sample socket programming over SSP.
_(WINCEROOT)\PUBLIC\COMMON\SDK\SAMPLES\BLUETOOTH\SSA is the sample implementation for the socket programming over SSP. It is having the SSP export code.
_(WINCEROOT)\PUBLIC\COMMON\SDK\SAMPLES\BLUETOOTH\SCA is the sample implementation for serial port emulator.
a) Create a new project, copy the SCA application code for serial port emulator implementation.
b) Add the following code as a glopal.
#define SDP_RECORD_SIZE 0x0000003f
#undef WSAGetLastError
#define WSAGetLastError() GetLastError()

BYTE rgbSdpRecord[SDP_RECORD_SIZE] = {
0x35, 0x3d, 0x09, 0x00, 0x01, 0x35, 0x11, 0x1c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // service UUID goes here (+8)
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, // server channel goes here (+40)
0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65,
0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09,
0x01, 0x00, 0x25, 0x03, 0x53, 0x53, 0x41
};

This the Sdp record to be exported for the SSP. The size of the SSP GUID is 16 bytes and this will be added after 8 bytes of the SDP record. The Channel number will be added after 40 bytes of the SDP record.

c) Add the following code before calling the HANDLE h = RegisterBluetoothCOMPort (L"COM", index, &pp); function.

GUID serviceClassId;
ULONG recordHandle = 0;
WSADATA wsd;
if (WSAStartup (MAKEWORD(1,0), &wsd)) {
wprintf (L"Initialization of socket subsystem failed! Error = %d\n", WSAGetLastError ());
return 0;
}
GetGUID (_T("00001101-0000-1000-8000-00805f9b34fb"),&serviceClassId);
wprintf(L"Channel No=0x%x\n",pp.channel);
rgbSdpRecord[40] = (unsigned char)pp.channel;
GUID bigEndianGUID;
bigEndianGUID.Data1 = ((serviceClassId.Data1 << 24) & 0xff000000)
((serviceClassId.Data1 << 8) & 0x00ff0000)
((serviceClassId.Data1 >> 8) & 0x0000ff00)
((serviceClassId.Data1 >> 24) & 0x000000ff);
bigEndianGUID.Data2 = ((serviceClassId.Data2 << 8) & 0xff00)
((serviceClassId.Data2 >> 8) & 0x00ff);
bigEndianGUID.Data3 = ((serviceClassId.Data3 << 8) & 0xff00)
((serviceClassId.Data3 >> 8) & 0x00ff);
memcpy (bigEndianGUID.Data4, serviceClassId.Data4, sizeof(serviceClassId.Data4));
memcpy (rgbSdpRecord + 8, &bigEndianGUID, sizeof(bigEndianGUID));
struct {
BTHNS_SETBLOB b;
unsigned char uca[SDP_RECORD_SIZE];
} bigBlob;

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 blob;
blob.cbSize = sizeof(BTHNS_SETBLOB) + SDP_RECORD_SIZE - 1;
blob.pBlobData = (PBYTE) &bigBlob;
WSAQUERYSET Service;
memset (&Service, 0, sizeof(Service));
Service.dwSize = sizeof(Service);
Service.lpBlob = &blob;
Service.dwNameSpace = NS_BTH;
int iRet = WSASetService(&Service,RNRSERVICE_REGISTER,0);
if (iRet != ERROR_SUCCESS) {
wprintf (L"WSASetService failes with status %d\n", WSAGetLastError ());
return 0;
}
pp.uuidService.Data1=serviceClassId.Data1;
pp.uuidService.Data2=serviceClassId.Data2;
pp.uuidService.Data3=serviceClassId.Data3;
memcpy(pp.uuidService.Data4,serviceClassId.Data4,sizeof(serviceClassId.Data4));
// HANDLE h = RegisterBluetoothCOMPort (L"COM", index, &pp);
HANDLE h = RegisterDevice (L"COM", index, L"btd.dll", (DWORD)&pp);
wprintf (L"handle = 0x%08x\n", (int)h);

if (h == NULL) {
wprintf (L"GetLastError = %d\n", GetLastError ());
return 0;
}
WCHAR szComPort[30];
wsprintf (szComPort, L"COM%d:", index);
HANDLE hCommPort = CreateFile (szComPort, GENERIC_READ GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hCommPort == INVALID_HANDLE_VALUE) {
wprintf (L"Failed to open %s. Error = %d\n", szComPort, GetLastError ());
// DeregisterBluetoothCOMPort (h);
DeregisterDevice(h);
return 0;
}

To export the SSP, the only way you have to use the Winsock APIs. WSAStartup() and WSASetservice() are used for registering the SSP service. You can use RegisterDevice instead of RegisterBluetoothCOMPort provided you have to mention the DLL name as an argument. Please see the above code.
Sample source code is published in codeplex. Here is the link to download.
https://bluetoothvcom.codeplex.com/downloads/get/781470

About Me:
I am currently working with e-con systems India which offers product design services, BSP development, Device driver development on Windows CE. we are specialized in camera drivers.

1 comment:

Anonymous said...

I had better luck with this by using the the 16-bit Serial Port Profile UUID instead of full 128-bit UUID, and also adding a Profile Descriptor List into the SDP record. This was on Windows Mobile 6. My revised SDP record was:

BYTE rgbSdpRecord[SDP_RECORD_SIZE] = {
0x35, 0x44, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19,
0x00, 0x00, // service UUID goes here (+8)
0x09, 0x00, 0x04, 0x35, 0x0c, 0x35, 0x03, 0x19,
0x01, 0x00, 0x35, 0x05, 0x19, 0x00, 0x03, 0x08,
0x00, // server channel goes here (+26)
0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65,
0x6e, 0x09, 0x00, 0x6a, 0x09, 0x01, 0x00, 0x09,
0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11,
0x01, 0x09, 0x01, 0x00, 0x09, 0x01, 0x00, 0x25,
0x0B, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6C, 0x20,
0x50, 0x6F, 0x72, 0x74
};

And I replaced the code:

GUID bigEndianGUID;
bigEndianGUID.Data1 = ((serviceClassId.Data1 << 24) & 0xff000000)
((serviceClassId.Data1 << 8) & 0x00ff0000)
((serviceClassId.Data1 >> 8) & 0x0000ff00)
((serviceClassId.Data1 >> 24) & 0x000000ff);
bigEndianGUID.Data2 = ((serviceClassId.Data2 << 8) & 0xff00)
((serviceClassId.Data2 >> 8) & 0x00ff);
bigEndianGUID.Data3 = ((serviceClassId.Data3 << 8) & 0xff00)
((serviceClassId.Data3 >> 8) & 0x00ff);
memcpy (bigEndianGUID.Data4, serviceClassId.Data4, sizeof(serviceClassId.Data4));
memcpy (rgbSdpRecord + 8, &bigEndianGUID, sizeof(bigEndianGUID));

with this:

rgbSdpRecord[8] = 0x11;
rgbSdpRecord[9] = 0x01;

I was able to get it working after making these changes.