andrewisaacfox
Posts: 18
Joined: Fri Jul 29, 2022 5:19 pm

Configuring Bluetooth on the CYW43438 chip in Bare Metal

Fri Aug 05, 2022 10:00 pm

I've been piecing together different tutorials trying to configure Bluetooth for my Raspberry Pi Bare metal build with little success. Is there a clear datasheet or tutorial that shows all of the different steps required to activate bluetooth and link with a device (a bluetooth keyboard)? I am using a Raspberry Pi 3 Model B v 1.2 in Aarch64 mode with just C and ARMv8 ASM. The main tutorial I am following is https://github.com/isometimes/rpi4-osde ... -bluetooth, but it is for the wrong version of Raspberry Pi and BCM chip, so I think my errors are coming from those inconsistencies. I already switched the firmware to https://github.com/RPi-Distro/bluez-fir ... 3430A1.hcd, but loading the firmware prevents any other HCIcommand from executing.

tldr: Where can I find the exact order and argument set for HCI commands in order to set up bluetooth for the Rpi 3B v1.2?

Schnoogle
Posts: 170
Joined: Sun Feb 11, 2018 4:47 pm

Re: Configuring Bluetooth on the CYW43438 chip in Bare Metal

Sat Aug 06, 2022 12:23 pm

Hi there,

I'm not sure where exactly I found the majority of information in the web thus I'm not sure whether I could simply upload the pdf's I've found to another location for you to share.

However, the procedure how I setup my BTLE adaptor in RPI 3B+ was like the following:
1. Send Reset-Command
2. Send DownloadMiniDriver Command
3. Once this command has been acknowledged send the FW in chunks using the Vendor-Command. The FW is organized as BT-HCI commands thus you could create the proper message from the BT-FW blob. This is BCM4345C0.hcd for Pi3B+ and BCM43430A1.hcd for Pi 3B
4. Once the FW load has finished the BT Controller performs a restart. Thus to continue from the ARM side a wait time of at least 500ms has proofen to be sufficient
5. Read Host Address if required (this also proofes your bt contrloller is working
6. Configure the ClassOfDevice to be e.g. a Desktop Computer with a WriteClassOfDevice Command
7. Set the local device name with a WriteLocalName Command
8. enable BT scan to allow your BT controller to be discovered by other devices with a WriteScanEnable Command.

Each Command contains a header and a payload part.
Please find below the structure of messages used for the setup:

Code: Select all


#define HCI_MAX_EVENT_SIZE	    257
#define HCI_MAX_COMMAND_SIZE	258
#define HCI_MAX_DATA_SIZE	    HCI_MAX_COMMAND_SIZE

#define HCI_BD_ADDR_SIZE		6
#define HCI_CLASS_SIZE		    3
#define HCI_NAME_SIZE	        248
#define HCI_PINCODE_SIZE        16
#define HCI_LINKKEY_SIZE        16

typedef struct __attribute__((packed)) {
    uint16_t    opCode;         // command code
    uint8_t     paramLength;    // total zize of parameters (bytes)
} THciCommandHdr;

typedef struct __attribute__((packed)) {
    THciCommandHdr hdr;
} THciCommandReset, 
  THciCommandDownloadDriver,
  THciCommandReadBDAddr;
  
typedef struct __attribute__((packed)) {
    THciCommandHdr hdr;

    uint8_t data[255];
} THciCommandVendorBcm; 
  
  typedef struct __attribute__((packed)) {
    THciCommandHdr hdr;

    uint8_t deviceClass[HCI_CLASS_SIZE];
} THciCommandWriteClassOfDevice;

typedef struct __attribute__((packed)) {
    THciCommandHdr hdr;

    uint8_t name[HCI_NAME_SIZE];
} THciCommandWriteLocalName;

typedef struct __attribute__((packed)) {
    THciCommandHdr hdr;

    uint8_t enable;
#define SCAN_ENABLE_NONE		    0x00
#define SCAN_ENABLE_INQUIRY_ENABLED	0x01
#define SCAN_ENABLE_PAGE_ENABLED	0x02
#define SCAN_ENABLE_BOTH_ENABLED	0x03
} THciCommandWriteScanEnable;
whenever you send a command ensure that the bt host does provide a reply for this command before sending the next one.

Hope this helps...

petzval
Posts: 54
Joined: Sat Aug 10, 2013 12:15 pm

Re: Configuring Bluetooth on the CYW43438 chip in Bare Metal

Sat Aug 06, 2022 1:17 pm

If you're just looking for info on HCI command formats, the reference sections 5.2 and 5.3 in the documentation here give full details with multiple examples showing exactly how to construct each packet, what replies come back, and how you must respond to incoming commands.
https://github.com/petzval/btferret

petzval
Posts: 54
Joined: Sat Aug 10, 2013 12:15 pm

Re: Configuring Bluetooth on the CYW43438 chip in Bare Metal

Sat Aug 06, 2022 5:55 pm

One thought to add to the previous post. While the btferret documentation lays out the sequences of HCI commands needed to establish connections and send data, there is an essential first step which is handled by the operating system socket functions. Since these are not active, it will have to be done as follows. The event masks must be set by sending the following HCI commands.

Code: Select all

unsigned char eventmask[12] =  { 0x01,0x01,0x0C,0x08,0xFF,0xFF,0xFB,0xFF,0x07,0xF8,0xBF,0x3D };  
unsigned char lemask[12] = { 0x01,0x01,0x20,0x08,0xBF,0x05,0,0,0,0,0,0 };

andrewisaacfox
Posts: 18
Joined: Fri Jul 29, 2022 5:19 pm

Re: Configuring Bluetooth on the CYW43438 chip in Bare Metal

Sat Aug 06, 2022 8:09 pm

Schnoogle wrote:
Sat Aug 06, 2022 12:23 pm
Hi there,

I'm not sure where exactly I found the majority of information in the web thus I'm not sure whether I could simply upload the pdf's I've found to another location for you to share.

However, the procedure how I setup my BTLE adaptor in RPI 3B+ was like the following:
1. Send Reset-Command
2. Send DownloadMiniDriver Command
3. Once this command has been acknowledged send the FW in chunks using the Vendor-Command. The FW is organized as BT-HCI commands thus you could create the proper message from the BT-FW blob. This is BCM4345C0.hcd for Pi3B+ and BCM43430A1.hcd for Pi 3B
4. Once the FW load has finished the BT Controller performs a restart. Thus to continue from the ARM side a wait time of at least 500ms has proofen to be sufficient
5. Read Host Address if required (this also proofes your bt contrloller is working
6. Configure the ClassOfDevice to be e.g. a Desktop Computer with a WriteClassOfDevice Command
7. Set the local device name with a WriteLocalName Command
8. enable BT scan to allow your BT controller to be discovered by other devices with a WriteScanEnable Command.

Each Command contains a header and a payload part.
Please find below the structure of messages used for the setup:

whenever you send a command ensure that the bt host does provide a reply for this command before sending the next one.

Hope this helps...
So I get the response back on the first three steps, so hypothetically those three requests worked, but when i try to set the baud address, i get no response back from the chip after writing the request. here is my code:

bluetooth.c

Code: Select all

#include "common.h"
#include "bluetooth.h"
#include "gpio.h"
#include "uart0.h"
#include "graphics.h"

bool bluetooth_read_byte_ready() {
    return ((*UART0_FR & 0x10) >> 4) == 1 ? false : true;
}

unsigned char bluetooth_read_byte() {
    while (bluetooth_read_byte_ready() == false) {
        wait_cycles(1);
    }
    return (unsigned char) (*UART0_DR & 0xFF);
}

void bluetooth_write_byte(char byte) {
    while ((*UART0_FR & 0x20) != 0) {
        wait_cycles(1);
    };
    *UART0_DR = (unsigned int) byte;
}

bool HCIcommand(unsigned char *opcodebytes, volatile unsigned char *data, unsigned char length) {
    bluetooth_write_byte(HCI_COMMAND_PKT);
    bluetooth_write_byte(opcodebytes[0]);
    bluetooth_write_byte(opcodebytes[1]);
    bluetooth_write_byte(length);

    for (int i = 0; i < length; i++) {
        bluetooth_write_byte(data[i]);
    }

    if (bluetooth_read_byte() != HCI_EVENT_PKT) {
        print(" err1 ");
        return false;
    }

    unsigned char code = bluetooth_read_byte();
    if (code == CONNECT_COMPLETE_CODE) {
        if (bluetooth_read_byte() != 4) {
            print(" err2 ");
            return false;
        }

        unsigned char err = bluetooth_read_byte();
        if (err != 0) {
            print(" err3 ");
	        return false;
        }

        if (bluetooth_read_byte() == 0) {
            return false;
            print(" err4 ");
        } else if (bluetooth_read_byte() != opcodebytes[0]) {
            return false;
            print(" err5 ");
        } else if (bluetooth_read_byte() != opcodebytes[1]) {
            return false;
            print(" err6 ");
        }
    } else if (code == COMMAND_COMPLETE_CODE) {
        if (bluetooth_read_byte() != 4) {
            return false;
            print(" err7 ");
        } else if (bluetooth_read_byte() == 0) {
            return false;
            print(" err8 ");
        } else if (bluetooth_read_byte() != opcodebytes[0]) {
            return false;
            print(" err9 ");
        } else if (bluetooth_read_byte() != opcodebytes[1]) {
            return false;
            print(" err10 ");
        } else if (bluetooth_read_byte() != 0) {
            return false;
            print(" err11 ");
        }
    } else {
        return false;
        print(" err12 ");
    }

    return true;
}

bool bluetooth_init() {
    gpio_select_function(14, FUNCTION_INPUT);
    gpio_select_function(15, FUNCTION_INPUT);
    gpio_select_function(30, FUNCTION_ALT3);
    gpio_select_function(31, FUNCTION_ALT3);
    gpio_select_function(32, FUNCTION_ALT3);
    gpio_select_function(33, FUNCTION_ALT3);

    while (bluetooth_read_byte_ready()) {
        bluetooth_read_byte();
    }

    uart0_init();

    print("finished bt_init\n");

    wait_microseconds(500000);

    unsigned short opcode = OGF_HOST_CONTROL << 10 | COMMAND_RESET_CHIP;
    unsigned char opcodebytes[2] = {lo(opcode), hi(opcode)};
    volatile unsigned char command0[] = {};
    if (HCIcommand(opcodebytes, command0, 0) == false) {
        return false;
    }

    print("finished bt_reset\n");
    
    opcode = OGF_VENDOR << 10 | COMMAND_LOAD_FIRMWARE;
    opcodebytes[0] = lo(opcode);
    opcodebytes[1] = hi(opcode);
    volatile unsigned char command1[] = {};
    if (HCIcommand(opcodebytes, command1, 0) == false) {
        return false;
    }

    extern unsigned char _binary_BCM43430A1_hcd_start[];
    extern unsigned char _binary_BCM43430A1_hcd_size[];

    unsigned int c = 0;
    unsigned int size = (long) &_binary_BCM43430A1_hcd_size;

    unsigned char length;
    unsigned char *data = &(_binary_BCM43430A1_hcd_start[0]);

    while (c < size) {
        opcodebytes[0] = *data;
        wait_cycles(1);
        opcodebytes[1] = *(++data);
        length = *(++data);
        data++;
        if (HCIcommand(opcodebytes, data, length) == false) {
	        return false;
	    }
	    data += length;
        c += length + 3;
    }
    
    print("finished bt_loadfirmware\n");

    wait_microseconds(500000);

    opcode = OGF_VENDOR << 10 | COMMAND_SET_BDADDR;
    opcodebytes[0] = lo(opcode);
    opcodebytes[1] = hi(opcode);
    volatile unsigned char command3[6] = {0xEE, 0xFF, 0xC0, 0xEE, 0xFF, 0xC0};
    if (HCIcommand(opcodebytes, command3, 6) == false) {
        return false;
    }

    print("finished bt_setbdaddr\n");

    return true;
}
any idea what I'm doing wrong here? I am using BCM43430A1.hcd from the raspberry pi kernel github, and the raspberry pi is a model 3B v1.2

Schnoogle
Posts: 170
Joined: Sun Feb 11, 2018 4:47 pm

Re: Configuring Bluetooth on the CYW43438 chip in Bare Metal

Sat Aug 06, 2022 10:13 pm

Hi there,

have you tried to read the actual BDADDR value of your BTLE host? Maybe the driver for the PI does not allow to manually set a local BDADDR?

andrewisaacfox
Posts: 18
Joined: Fri Jul 29, 2022 5:19 pm

Re: Configuring Bluetooth on the CYW43438 chip in Bare Metal

Sun Aug 07, 2022 4:05 am

Schnoogle wrote:
Sat Aug 06, 2022 10:13 pm
have you tried to read the actual BDADDR value of your BTLE host? Maybe the driver for the PI does not allow to manually set a local BDADDR?
I initially thought the issue might be with the baud address, but i commented out the load firmware command, and the set baud address command executed, so i'm not 100% certain, but i do think the issue is in either my command code or the firmware.

Schnoogle
Posts: 170
Joined: Sun Feb 11, 2018 4:47 pm

Re: Configuring Bluetooth on the CYW43438 chip in Bare Metal

Mon Aug 08, 2022 10:28 pm

andrewisaacfox wrote:
Sun Aug 07, 2022 4:05 am
Schnoogle wrote:
Sat Aug 06, 2022 10:13 pm
have you tried to read the actual BDADDR value of your BTLE host? Maybe the driver for the PI does not allow to manually set a local BDADDR?
I initially thought the issue might be with the baud address, but i commented out the load firmware command, and the set baud address command executed, so i'm not 100% certain, but i do think the issue is in either my command code or the firmware.
Well, just to clarify this for me: If you keep the code to load the firmware do you ever reach this line of code:

Code: Select all

 print("finished bt_loadfirmware\n");
? But not:

Code: Select all

 print("finished bt_setbdaddr\n");
?

And if you remove the bt firmware loading part, do you ever reach this code:

Code: Select all

 print("finished bt_setbdaddr\n");
?

And by the way BDADDR stands for Bluetooth Device Address not Baud Address ;) :geek:

Return to “Bare metal, Assembly language”