How Event Packet Header Is Getiing in Hci_Send_Req API Implementation

OSX Bluetooth LE Peripheral transfer rates are slow

I decided my best course of action was to attempt to use Linux in a VM as there is more documentation available and access to the source code would hopefully guarantee that I could find a solution. For anyone who is also facing this problem, here's how you can issue a Connection Parameter Update Request on OS X (sort of).

Step 1

Install a Linux VM. I used Virtual Box with Linux Mint 15 (64-bit Cinnamon).

Step 2

Allow usage of the OS X Bluetooth device in your VM. Attempting to forward the Bluetooth USB Controller to your VM will give an error message. To allow this, you need to stop everything that is using the controller. On my machine, that included issuing the following commands from the command line:

sudo launchctl unload /System/Library/LaunchDaemons/com.apple.blued.plist

This will kill the OS X Bluetooth daemon. Attempting to kill blued from the Activity Monitor will just cause it to be automatically relaunched.

sudo kextunload -b com.apple.iokit.BroadcomBluetoothHostControllerUSBTransport

On my MacBook, I've got a Broadcom controller and this is the kernel module that OS X uses for it. Don't worry about issuing these commands. To undo the changes, you can power down and reboot your machine (note, in some cases when playing with the BT controller and it got into a bad state, I had to actually leave the machine powered down for ~10 seconds before rebooting to clear volatile memory).

If after running these two commands you still can't mount the BT controller, you can run kextstat | grep Bluetooth and see other Bluetooth related kernel modules and then try to unload them as well. I've got ones named IOBluetoothFamily and IOBluetoothSerialManager that don't need to be unloaded.

Step 3

Launch your VM and get your Linux BT stack. I checked out the bluez Git repo from here. I specifically grabbed the 5.14 release tag using git checkout tags/5.14 just to be sure it was at least a tagged version and less likely to be broken. 5.14 is the newest tag as of writing this answer.

Step 4

Build bluez. This was done using bootstrap, then configure, then make and make install. I used the --prefix=/opt/bluez flag on configure to prevent overwriting the install bluetooth stack. Also, I used the --enable-maintainer-mode configure flag for the reason stated in the next step. You also might need to use --disable-systemd to get it to configure. Bluez has a bunch of tools and utilities you can use for various things. In order to use the built Bluetooth daemon, you need to stop the system daemon using sudo service bluetooth stop. You can then launch the built one using sudo /opt/bluez/libexec/bluetooth/bluetoothd -n -d (this launches in non-daemon mode with debug output).

Step 5

Get your LE service running via bluez. You can view the bluez/plugins/gatt-example.c for how to do this. I directly modified this by removing the unnecessary code and using the battery service code as a template for my own service and characteristics. You need to recompile bluez to have this code added to the bluetooth daemon. One thing to note (that caused my a day or two of trouble getting this working) was that iOS caches the GATT service listing and this is not read/refreshed on each connection. If you add a service or characteristic or change a UUID, you'll need to disable Bluetooth on your iOS device and then re-enable it. This is undocumented in Apples docs and there is no programmatic way to do it.

Step 6

Unfortunately, this is where things get tricky. Bluez doesn't have support built-in for issuing the Connection Parameters Update Request using any of its utilities. I had to write it myself. I'm currently seeing if they want my code to be included in the bluez stack. I can't post the code currently as I'd need to first see if the bluez devs are interested in the code and then get approval from my workplace to give the code. However, I can currently explain what I did to enable support.

Step 7

Prime yourself on the Bluetooth Standard. Any version 4.0 or greater will have the details you need. Read the following sections.

  • See Vol. 2, Part E, 4.1 for Host to Controller HCI flow.
  • See Vol. 2, Part E, 5.4.2 for HCI ACL Data Packet format.
  • See Vol. 3, Part A, 4 for Signalling Packet format.
  • See Vol. 3, Part A, 4.20 for Connection Parameter Update Request format.

You're basically going to need to write the code to format the packets and then write them to the hci device. The HCI ACL Data Packet header will contain 4 bytes. This is followed by 4 bytes for the Signalling command's length and channel id. This is then followed by your signal payload which in my case was 12 bytes (for the Connection Parameter Update Request).

You can then write them to the device similar to hci_send_cmd in bluez/lib/hci.c. I did each packet header as it's own struct and wrote them each as iovecs to the device. I put my new function in the hci.c file and exposed it with a function prototype in bluez/lib/hci_lib.h. I then modified bluez/tools/hcitool.c to allow me to call this method from the command line. In my case, I made it so that the command was nearly identical to the lecup command as it requires the same parameters (lecup can't be used as it's meant to be called on the master side, not the slave).

Recompiled all of this and then, voila, I can use my new command on hcitool to send the parameters to the bluetooth controller. After sending my command, it then re-negotiates with the iOS device as expected.

Comments

This process is not for the faint of heart. Hopefully, either this, or some other method of setting the connection parameters is added to bluez to simplify this process. Ideally, Apple will allow the ability to do so via CoreBluetooth or IOBluetooth at some point as well (it could be possible, but undocumentated / difficult to do so, I gave up with the Apple libraries). I've journeyed down the rabbit hole and learned much more about the Bluetooth Spec then I thought I'd have to to simply change the connection parameters between a MacBook and an iPhone. Hopefully this will be helpful to somebody at some point (even if it's me checking back on how I did this).

I know I've left out a lot of details in this in order to keep it somewhat brief (i.e. usage on the bluez tools). Please comment if something isn't clear.

Detect if a BLE scan response is sent when a scan request is received in Android App

Sorry, that information is not exposed over the HCI layer. The Bluetooth controller simply sends a scan response without notifying the host about that.

BlueNRG wrong header

{0xff,..} would be correct.

AN4494 is for BlueNRG-MS, the 5-byte SPI header for MISO is: {Ready, WBufLen, 0x00, RBufLen, 0x00}. In BlueNRG-1 and BlueNRG-2, that SPI header has been modified to: {0xff, CmdLen0, CmdLen1, DataLen0, DataLen1}, in order to support ACI packets with length more than 127 bytes in a single SPI transaction. The first byte in the header is fixed to 0xff.

Unfortunately currently there is no document describing the modification. You may need to refer to DTM SPI sample project to know the details.

In your transaction, if executing a Read command and continuing to read MISO for the 6 byte data:

  • {0xff,0x08,0x00,0x06,0x00},{0x04,0xff,0x03,0x01,0x00,0x01}

That is a ACI Blue initialized event, saying firmware has started properly. That can be parsed:

  • Header
    • 0x0006: 6 bytes (data) is available to be read
  • Data
    • 0x04: HCI event packet
    • 0xFF: ACI (vendor specific) event
    • 0x03: parameter length
    • 0x10 0x00: event = 0x0001, which is ACI_BLUE_INITIALIZED_EVENT
    • 0x01: reason code = 1, which means firmware started properly


Related Topics



Leave a reply



Submit