Mostly as an aid memoire to myself, to help me remember how to find the version of radio (“controller” in official terminology IIRC) in the various Bluetooth stacks.

Microsoft on Windows XP, Windows 7 etc

Windows 7 Advanced tab of the Bluetooth Control Panel

Admin view of Windows XP Advanced tab of the Bluetooth Control Panel No version on Windows XP's non-admin view...

No version on Windows XP's non-admin view...

Widcomm/Broadcom

Widcomm (Windows XP at least) shows the version on the Hardware tab of its Control Panel.

BlueSoleil

BlueSoleil (Windows 7 at least) shows the version on the Hardware tab of its Control Panel.

BlueZ on Linux

Not from hcitool:

alan@foobar:~/> hcitool dev
Devices:
 hci0 00:15:83:B4:1B:FA
 hci1 00:80:98:24:4C:A4
alan@foobar:~/>

Yes from root hciconfig:

foobar:/home/alan # hciconfig hci0 version
hci0: Type: BR/EDR  Bus: USB
 BD Address: 00:15:83:B4:1B:FA  ACL MTU: 384:8  SCO MTU: 64:8
 HCI Version: 2.0 (0x3)  Revision: 0x7a6
 LMP Version: 2.0 (0x3)  Subversion: 0x7a6
 Manufacturer: Cambridge Silicon Radio (10)
foobar:/home/alan # hciconfig hci1 version
hci1: Type: BR/EDR  Bus: USB
 BD Address: 00:80:98:24:4C:A4  ACL MTU: 192:8  SCO MTU: 64:8
 HCI Version: 1.1 (0x1)  Revision: 0x20c
 LMP Version: 1.1 (0x1)  Subversion: 0x20c
 Manufacturer: Cambridge Silicon Radio (10)

foobar:/home/alan # hciconfig -a
hci0: Type: BR/EDR  Bus: USB
 BD Address: 00:15:83:B4:1B:FA  ACL MTU: 384:8  SCO MTU: 64:8
 UP RUNNING PSCAN
 RX bytes:1677 acl:0 sco:0 events:41 errors:0
 TX bytes:498 acl:0 sco:0 commands:41 errors:0
 Features: 0xff 0xff 0x8f 0xfe 0x9b 0xf9 0x00 0x80
 Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
 Link policy: RSWITCH HOLD SNIFF PARK
 Link mode: SLAVE ACCEPT
 Name: 'linux-0'
 Class: 0x4a0100
 Service Classes: Networking, Capturing, Telephony
 Device Class: Computer, Uncategorized
 HCI Version: 2.0 (0x3)  Revision: 0x7a6
 LMP Version: 2.0 (0x3)  Subversion: 0x7a6
 Manufacturer: Cambridge Silicon Radio (10)

hci1: Type: BR/EDR  Bus: USB
 BD Address: 00:80:98:24:4C:A4  ACL MTU: 192:8  SCO MTU: 64:8
 UP RUNNING PSCAN
 RX bytes:689 acl:0 sco:0 events:22 errors:0
 TX bytes:86 acl:0 sco:0 commands:22 errors:0
 Features: 0xff 0xff 0x0f 0x00 0x00 0x00 0x00 0x00
 Packet type: DM1 DM3 DM5 DH1 DH3 DH5 HV1 HV2 HV3
 Link policy:
 Link mode: SLAVE ACCEPT
 Name: 'ALANPC1a'
 Class: 0x4a0000
 Service Classes: Networking, Capturing, Telephony
 Device Class: Miscellaneous,
 HCI Version: 1.1 (0x1)  Revision: 0x20c
 LMP Version: 1.1 (0x1)  Subversion: 0x20c
 Manufacturer: Cambridge Silicon Radio (10)

foobar:/home/alan #

Version Numbers

The X.X e.g 1.1, 3.0, version numbers are actually represented by a simple integer, HCI and LMP strictly don’t use the same definitions but the two use the same numbering system currently and likely will do forever.  The current definitions are:

0 Bluetooth Core Specification 1.0b
1 Bluetooth Core Specification 1.1
2 Bluetooth Core Specification 1.2
3 Bluetooth Core Specification 2.0 + EDR
4 Bluetooth Core Specification 2.1 + EDR
5 Bluetooth Core Specification 3.0 + HS
6 Bluetooth Core Specification 4.0
7 – 255 Reserved

The definitions are available at https://www.bluetooth.org/Technical/AssignedNumbers/hci.htm and https://www.bluetooth.org/Technical/AssignedNumbers/link_manager.htm The Bluetooth specification documents have moved, unfortunately there’s a fault on the HCI document which requires Bluetooth log-in to access.  (http://bluetooth.com/Specification%20Documents/AssignedNumbersHostControllerInterface1.pdf and  http://bluetooth.com/Specification%20Documents/AssignedNumbersLinkManager1.pdf)

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.



We’ve implemented support to use BlueZ’s D-Bus ‘newly discovered device’ signal for our ‘live’ discovery feature. Use BluetoothComponent.DiscoverDevicesAsync as always.   It appears that a signal is only raised if the device wasn’t seen the last time discovery was run — I haven’t found whether this is per application or is controllable through their “session” features.  Maybe we need to store the old set each time, adding when we see the DeviceFound signal and removing from it when we see the DeviceDisappeared signal.

Radio.set_Mode is also now supported.  Note that once a radio is ‘PoweredOff’ I can’t find a way through the BlueZ control panels to re-enable it, we don’t load it either so I had to use a custome application to re-enable it.

Finally we’ve also had some feedback from the Mono team that they don’t want to support Bluetooth sockets specifically and have to pull in their header file, so we suggested having their socket layer stop blocking unknown address families and sockaddr types but instead pass them through. We submitted a new patch to them for this[1] and have updated out BluetoothEndPoint class to match[2].

[1] https://bugzilla.novell.com/show_bug.cgi?id=655153#c6

[2] http://32feet.codeplex.com/SourceControl/changeset/changes/82268

(Further to: https://32feetnetdev.wordpress.com/2010/12/05/further-support-for-bluez/ and https://32feetnetdev.wordpress.com/2010/11/24/bluez-support/)

The Microsoft Bluetooth stack on both desktop Windows and WM/CE uses sockets for RFCOMM communications, and they use socket options for both setting parameters and getting information.  We use two of the options to implement the BluetoothClient and BluetoothListener Authenticate and Encrypt properties, and another on WM/CE for the SetPin method.

As these options are present only on the Microsoft platforms and even vary between the two platforms they aren’t very useful for production use, but they can be useful for diagnostics etc.  We recently had a question in the forums and I knocked up a bit of code to run through all the options we’ve seen in MSDN and see what values they return.  The code to run through all the known option is in our code repository at codeplex in the XxxMenuTesting suite of apps (Forms-/Device-/ConsoleMenuTesting) and uses reflection to get the list of options.  I won’t reproduce that here due to its complexity, but a sample simple(!) call would be:

    System.Net.Sockets.Socket sock = ... ...;
    System.Net.Sockets.SocketOptionName option;
#if NETCF
    option = BluetoothSocketOptionName.GetMtu;
#else
    option = BluetoothSocketOptionName.XPMtu;
#endif
    var v = sock.GetSocketOption(BluetoothSocketOptionLevel.RFComm, option);
    var i = (int)v;
    console.WriteLine("Option MTU value: {0}=0x{0:X}", i);

Firstly the result from a WM device, when connected to Widcomm on Windows XP. We get fifteen successes there.

---- TryAllMsftSocketOptions ----
Option:             Authenticate           1 : error:  (10042)
Option:           XPAuthenticate  0x80000001 : error:  (10042)
Option:                  Encrypt           2 : error:  (10042)
Option:                   SetPin           3 : error:  (10042)
Option:                  SetLink           4 : error:  (10042)
Option:                  GetLink           5 : error:  (10022)
Option:                    XPMtu  0x80000007 : error:  (10042)
Option:                   SetMtu           6 : error:  (10042)
Option:                   GetMtu           7 : value: 127=0x7F
Option:             XPMtuMaximum  0x80000008 : error:  (10042)
Option:            SetMtuMaximum           8 : error:  (10042)
Option:            GetMtuMaximum           9 : value: 32767=0x7FFF
Option:             XPMtuMinimum  0x8000000A : error:  (10042)
Option:            SetMtuMinimum          10 : error:  (10042)
Option:            GetMtuMinimum          11 : value: 23=0x17
Option:              SetXOnLimit          12 : error:  (10042)
Option:              GetXOnLimit          13 : value: 800=0x320
Option:             SetXOffLimit          14 : error:  (10042)
Option:             GetXOffLimit          15 : value: 200=0xC8
Option:            SetSendBuffer          16 : error:  (10042)
Option:            GetSendBuffer          17 : value: 7168=0x1C00
Option:         SetReceiveBuffer          18 : error:  (10042)
Option:         GetReceiveBuffer          19 : value: 7168=0x1C00
Option:              GetV24Break          20 : error:  (10022)
Option:                   GetRls          21 : value: 0=0x0
Option:                  SendMsc          22 : error:  (10042)
Option:                  SendRls          23 : error:  (10042)
Option:              GetFlowType          24 : value: 1=0x1
Option:           SetPageTimeout          25 : error:  (10042)
Option:           GetPageTimeout          26 : value: 9472=0x2500
Option:                  SetScan          27 : error:  (10042)
Option:                  GetScan          28 : value: 3=0x3
Option:                   SetCod          29 : error:  (10042)
Option:                   GetCod          30 : value: 5898764=0x5A020C
Option:          GetLocalVersion          31 : error:  (10022)
Option:         GetRemoteVersion          32 : error:  (10022)
Option: GetAuthenticationEnabled          33 : value: 0=0x0
Option: SetAuthenticationEnabled          34 : error:  (10042)
Option:           ReadRemoteName          35 : error:  (10042)
Option:            GetLinkPolicy          36 : value: 15=0xF
Option:            SetLinkPolicy          37 : error:  (10042)
Option:            EnterHoldMode          38 : error:  (10042)
Option:           EnterSniffMode          39 : error:  (10042)
Option:            ExitSniffMode          40 : error:  (10042)
Option:            EnterParkMode          41 : error:  (10042)
Option:             ExitParkMode          42 : error:  (10042)
Option:                  GetMode          43 : value: 0=0x0
---- CLEAN EXIT ----

Secondly on Windows XP, when connected to a MSFT+WM6, we only get one successful on XP.

---- TryAllMsftSocketOptions ----
Option:             Authenticate           1 : error:  (10042 ProtocolOption)
Option:           XPAuthenticate  0x80000001 : error:  (10042 ProtocolOption)
Option:                  Encrypt           2 : error:  (10042 ProtocolOption)
Option:                   SetPin           3 : error:  (10042 ProtocolOption)
Option:                  SetLink           4 : error:  (10042 ProtocolOption)
Option:                  GetLink           5 : error:  (10042 ProtocolOption)
Option:                    XPMtu  0x80000007 : value: 666=0x29A
Option:                   SetMtu           6 : error:  (10042 ProtocolOption)
Option:                   GetMtu           7 : error:  (10042 ProtocolOption)
Option:             XPMtuMaximum  0x80000008 : error:  (10042 ProtocolOption)
Option:            SetMtuMaximum           8 : error:  (10042 ProtocolOption)
Option:            GetMtuMaximum           9 : error:  (10042 ProtocolOption)
Option:             XPMtuMinimum  0x8000000A : error:  (10042 ProtocolOption)
Option:            SetMtuMinimum          10 : error:  (10042 ProtocolOption)
Option:            GetMtuMinimum          11 : error:  (10042 ProtocolOption)
Option:              SetXOnLimit          12 : error:  (10042 ProtocolOption)
Option:              GetXOnLimit          13 : error:  (10042 ProtocolOption)
Option:             SetXOffLimit          14 : error:  (10042 ProtocolOption)
Option:             GetXOffLimit          15 : error:  (10042 ProtocolOption)
Option:            SetSendBuffer          16 : error:  (10042 ProtocolOption)
Option:            GetSendBuffer          17 : error:  (10042 ProtocolOption)
Option:         SetReceiveBuffer          18 : error:  (10042 ProtocolOption)
Option:         GetReceiveBuffer          19 : error:  (10042 ProtocolOption)
Option:              GetV24Break          20 : error:  (10042 ProtocolOption)
Option:                   GetRls          21 : error:  (10042 ProtocolOption)
Option:                  SendMsc          22 : error:  (10042 ProtocolOption)
Option:                  SendRls          23 : error:  (10042 ProtocolOption)
Option:              GetFlowType          24 : error:  (10042 ProtocolOption)
Option:           SetPageTimeout          25 : error:  (10042 ProtocolOption)
Option:           GetPageTimeout          26 : error:  (10042 ProtocolOption)
Option:                  SetScan          27 : error:  (10042 ProtocolOption)
Option:                  GetScan          28 : error:  (10042 ProtocolOption)
Option:                   SetCod          29 : error:  (10042 ProtocolOption)
Option:                   GetCod          30 : error:  (10042 ProtocolOption)
Option:          GetLocalVersion          31 : error:  (10042 ProtocolOption)
Option:         GetRemoteVersion          32 : error:  (10042 ProtocolOption)
Option: GetAuthenticationEnabled          33 : error:  (10042 ProtocolOption)
Option: SetAuthenticationEnabled          34 : error:  (10042 ProtocolOption)
Option:           ReadRemoteName          35 : error:  (10042 ProtocolOption)
Option:            GetLinkPolicy          36 : error:  (10042 ProtocolOption)
Option:            SetLinkPolicy          37 : error:  (10042 ProtocolOption)
Option:            EnterHoldMode          38 : error:  (10042 ProtocolOption)
Option:           EnterSniffMode          39 : error:  (10042 ProtocolOption)
Option:            ExitSniffMode          40 : error:  (10042 ProtocolOption)
Option:            EnterParkMode          41 : error:  (10042 ProtocolOption)
Option:             ExitParkMode          42 : error:  (10042 ProtocolOption)
Option:                  GetMode          43 : error:  (10042 ProtocolOption)
---- CLEAN EXIT ----

* WM
“setsockopt (Bluetooth)” http://msdn.microsoft.com/en-us/library/aa915899.aspx
“getsockopt (Bluetooth)” http://msdn.microsoft.com/en-us/library/aa916547.aspx

* Win32
“Bluetooth and Socket Options” http://msdn.microsoft.com/en-us/library/aa362911(VS.85).aspx

We’ve now added support for the BlueZ D-Bus API.  So with it we now have support for:

  • Radio: Reading Mode and HardwareStatus is supported.
  • DiscoverDevices: Get Remembered devices, so we have full support there except for ‘live’ discovery.
  • BluetoothSecurity.RemoveDevice: Supported.
  • BluetoothSecurity.PairRequest: Support for pin=null case, that is where we want the system to prompt the local user for passphrase/etc.

We need to do more work to support Radio.set_Mode and fully automated PairRequest.

We have new support for the BlueZ Bluetooth protocol stack on Linux. We running on the Mono CLR there.

 Currently we have the following support:

  • Radio: We list the values propertly (Address, Name, CoD).  We do not get the Mode or HardwareStatus or allow setting them.   We will list all the installed radios, but see below for multi-radio usage.
  • Client.Connect: Supported.  No support for Authenticate/Encrypt/SetPin etc.
  • Data transfer: BlueZ uses Sockets for data transfer so everything should be fine. 🙂
  • Listener: Supported, including advertising SDP record.  Again no support for Auth/Encrypt/SetPin.
  • Client.DiscoverDevices: we discover only in-range devices.  There is no support currently for remembered devices and will return an error if they are requested — there doesn’t seem a ‘hci’ API.  There is also currently no support for ‘live’ discovery.
  • Security: No support currently.

As noted above BluetoothClient.Connect and BluetoothListener use Sockets.  Current versions of Mono do not support opening or using Bluetooth sockets.  My old pal Andy Hume and I have created a patch to Mono to enable this support, and have posted it as Mono/Novell bug id 655153.

Apply that patch to your Mono source code, rebuild, test and let me have your comments…

As noted above, unlike apparently every stack on Windows, BlueZ does support multiple radios. We do list all the radios and BluetoothClient and BluetoothListener will bind to a particular local address if requested to do so. However the other functionality does not currently ensure it runs on a particular radio, DiscoverDevices and GetServiceRecords (and as used internally by Connect) will use the primary radio.

We currently are using the original BlueZ ‘hci’ API. For many things their D-Bus based API is recommended instead, we will add support for that at some stage… There seems for instance no native API to get the list of remembered device, unless I’ve missed it…

In the Microsoft Bluetooth API on Win32 (desktop Windows) there’s no simple way to get only the in-range-discoverable devices (as I’ve noted previously one can specify whether one wants remembered and/or unknown devices, but the ‘unknown-only’ result has had any remembered devices removed from it!)  This has prevented us from providing the DiscoverDevice(discoverableOnly=true) feature that we provide on every other platform.  We also haven’t been able to provide the ‘live’ discovery feature for in-range devices as provided through class BluetoothComponent.

I’ve recently implemented support for using the native Bluetooth events on this platform (I first implemented this code a long time ago, and see below for why I didn’t use it earlier).  So now that we have support for the events we can implement both of the features discussed above.  Briefly the events are provided through the RegisterDeviceNotifications API and the events are raised as Window Messages.  Thus we create a window on which to receive the messages, creating a new message-loop in most cases for the window, and call the API to enable the events.

Thus when ‘live’ discovery is requested via BluetoothComponent, we run a normal discovery process so that the Bluetooth hardware searches for devices, and we monitor the native events at the same time and use the resulting events to know which devices are in-range (and obviously in discoverable mode) and raise the respective BluetoothComponent events.

We also use this functionality to provided the “discoverableOnly” feature.  By monitoring the native events we make a list of all the devices that are range, and at the same time we run a discovery asking for all remembered and unknown devices — which make Windows return the union of all remembered and in-range devices — then we filter that list leaving only the devices in it that we saw ‘in-range’ events for.  Phew a lot of work for a simple thing. 😦

We should note however that this is useful only on Windows 7 (and perhaps Vista — I haven’t tested); it not useful on Windows XP.  The native events aren’t raised there correctly so we can’t use them to provide this functionality. 😦

This code is in the repository now, please test it and let me hear your feedback.  (Download the code from http://32feet.codeplex.com/SourceControl/list/changesets, build the FX2 project (only), and reference and use it with your application.  Test BluetoothClient.DiscoverDevices(discoverableOnly=true) and BluetoothComponent.DiscoverDevicesAsync(discoverableOnly=true).  One can see the native events directly with BluetoothWin32Events — currently it exposed the Radio-In-Range and Radio-Out-Of-Range event only, we don’t expose the HCI or L2CAP events etc.

Differences between Windows XP and 7

The Radio-In-Range event (on Window 7 at least) raises lots of apparently duplicate events, so we need to do some filtering to provide both functions.  On Windows XP however the native event is not useful for device discovery; when a discovery process is run we do not see Radio-In-Range events for all discoverable devices, it seems like there are no events for devices we’ve seen before.

It seem that one only reliably gets Radio-In-Range events when a connection is made to a particular remote device (e.g. for BluetoothClient.Connect or BluetoothDeviceInfo.GetServiceRecords).  So it seems that the event isn’t raised for discovered devices, but only for devices connected to.  During discovery Windows connects to the newly seen devices to then to get their Device Name — which isn’t transferred in the discovery process in earlier Bluetooth versions.  That’s why we do see events for new devices during discovery.

So that’s why this code has been “on the shelve” for a while.  When tested on Windows XP no useful events were raised.  With Windows 7 reliable events are raised during discovery so we can use it now!