We have new L2CAP support in the code repository for L2CAP, currently only for the Broadcom/Widcomm stack. This can be used for your custom application that uses L2CAP, and for some Bluetooth Profiles, for instance HDP (Health Devices) etc.

For the HID Profile most Bluetooth stacks have built-in ‘host’ support for HID and thus connect directly with the HID device and pass the data directly to Windows’ HID support.  However some devices have incomplete HID support and thus the protocol stack can’t connect to them, so this L2CAP support can be used to connect directly.  It also could be used if one wants to create a HID Device.  Depending on the stack, there might be conflicts between the built-int HID services and your implementation however.

API

The API to this is currently two new classes: L2CapClient and L2CapListener which each have a subset of the member on the exixting BluetoothClient and BluetoothListener classes — they remain for RFCOMM.  However the data stream returned by L2CapClient is subclass of System.IO.Stream rather than a subclass of NetworkStream.  This is because whereas RFCOMM presents a stream of data instead L2CAP transfers packets so should use socket type SOCK_SEQPACKET rather that SOCK_STREAM, and NetworkStream does not support SOCK_SEQPACKET.  See more on data transfer below.

At the moment both client and listener are lacking SDP support.  Client given only a Service Class Id will not lookup a port (L2CAP PSM — Protocol Service Multiplexer), and Listener will not add a SDP record.  We should be able to add support for both in the next week or two.

Unfortunately, being Widcomm, updates to the C++ mapping layer are required for this new functionality.  These have been added to the 32feetWidcomm.dll source in the repository and the respective DLL need to be recompiled.  I can provide access to a built copy based on demand.

Update: I hadn’t checked in one change for Listener so it wasn’t working until today (January 2nd).  Client SDP is working now too.

Update: Listener SDP is working now too, repository revision 82726.

Example

Listener

---- L2CapListen ---- local PSM (optional)>
BluetoothEndPoint..cctor _isBlueZ: False
L2CapIf_AssignPsmValue ret: True <- psm: 0=0x0
L2CapIf_GetPsm PSM: 22271=0x56FF
L2CapIf_Register ret :True
Server GetScn returned port: 22271 True; False,False-> NONE
Listening on 000000000000:22271
WidcommRfcommPort.Create'd: 12E5988
WidcommL2CapClient.NativeMethods.L2CapConn_OpenServer ret: SUCCESS=0x00000000
OpenServer ret: SUCCESS=0x00000000
StartOneNewListenerPort 12E5988. Started 1 new port(s).
Listening on PSM: 22271 = 0x56FF
BeginAccept Enqueued
00:39:46.5000000 HandleEvent: CONNECTED=0x00000200=CONNECTED
12E5988: CONNECTED (New) port: InTheHand.Net.Bluetooth.Widcomm.L2CapPort CONNECTED 12E5988; m_state: New; m_arConnect (set), IsCompleted: False. PortAccepted Dequeued a caller WidcommRfcommPort.Create'd: 12E59E8 WidcommL2CapClient.NativeMethods.L2CapConn_OpenServer ret: SUCCESS=0x00000000 OpenServer ret: SUCCESS=0x00000000
StartOneNewListenerPort 12E59E8. Started 1 new port(s).
Accepted! :-)
---- CLEAN EXIT ----

Client

---- L2CapConnect ----
remote address>00:1A:92:AB:47:F5
remote PSM>22261
WidcommRfcommPort.Create'd: 12E5A48
BeginFillInPortState
BeginFillInPort, has port -> Completed Syncronously
L2CapIf_AssignPsmValue ret: True <- psm: 22261=0x56F5
L2CapIf_GetPsm PSM: 22261=0x56F5
L2CapIf_Register ret :True
WidcommL2CapClient.NativeMethods.L2CapConn_OpenClient ret: SUCCESS=0x00000000
OpenClient ret: SUCCESS=0x00000000
Connect called
---- CLEAN EXIT ----

Data access

As a SOCK_SEQPACKET the packet boundaries from the L2CAP protocol should be maintained when reading from the socket/stream, we do that currently, so one Read will not return data from more than one packet, it will return when the end of a packet is reached (or it has filled the caller buffer).  However the semantics of SEQPACKET with simple recv are also that if a portion of a packet is read then the rest of the data from that packet should be discarded.  We do not do that currently, remaining parts of a packet will be returned in the subsequent reads.  We will look at changing that sometime.