This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Setting wl12xx Bluetooth bd_addr



Hello

I would like to change the Bluetooth bd_addr on my embedded linux device to use my organization's OUI and address.  I have read the post at http://e2e.ti.com/support/low_power_rf/f/307/t/34498.aspx, but it is not really helpful.

In responding to the same question from Gunter, Miguel suggested using "Send_HCI_VS_Write_BD_ADDR 0xFC06".  A responder said that he could find no such command, to which Miguel responded 'use the "hcitool cmd 0xgf 0xcf params" option'.

This reply makes no sense.  Miguel apparently meant to use the hcitool arbitrary command facility - "hcitool cmd ogf ocf <params>".  0xgf, of course, is not meaningful - neither is 0xcf in this context.

So, the post tantalizingly infers that an HCI command can be used to change the bd_addr Bluetooth address … but what are the real details here?  While I found a command to read the address ("hcitool cmd 0x04 0x09"), I did not find one in the Bluetooth core spec for writing the address.

Please post details for setting (overriding) the Bluetooth address at initialization time.  It would help me and probably many others who face the same issue.

-S

  • OK, so while waiting for a response to this query, I was able to make a few guesses based upon clues from several other sources.  I found that

    hcitool cmd 0x3f 0x06 0x99 0xaa 0xbb 0xcc 0xdd 0xee

    will set bd_addr to 99:aa:bb:cc:dd:ee - but only temporarily.  It is persistent only while the BT radio is on.  Turning it off and on again will cause bd_addr to revert to the value stored in the fuse ROM.

    So, now Part 2 of my question: I would really like to set this address once at kernel init time (from a value passed on the Linux boot command line).  So, the information I now need is "how can bd_addr be set from the kernel?"  That is, where in the kernel does the HCI command 0x3f 0x06 <address> actually go, and what data structures / hardware does it affect?

  • Hi Scoot,

    There is no way to permanently set the BD address.

    Only way is to configure the BD Address of the device after booting up every time.

  • I understand that there is no way to permanently set bd_addr.  I also now know how to set bd_addr with

    hcitool cmd 0x3f 0x06 ...

    However, there is still my outstanding question about how to do the equivalent in kernel space.  What is the sequence through the kernel when an "hcitool cmd" is executed?  I would like to be able to call a kernel method at initialization time and set bd_addr.

    Also, what is the scope (duration) of the (volatile) configuration of bd_addr?  You say "after booting up" above, but anecdotally it seems that the address is lost if the chip is put to sleep and reawakened (e.g. bluetoth off / bluetooth on in Android.)

  • Hi Scott,

    hcitool cmd can be used once the "/dev/hci0" is up. This means that you have to do "hciconfig hci0 up" first before using the hcitool commands.

    If you look at the hcitool.c, you can see that it is quite simple - it sends data to "/dev/hci0" device.

    Assuming you are using the btwilink/shared transport driver, this means that the ti_st_send_frame() (btwilink.c) call is executed when you do a hcitool cmd operation.

    This then calls st_int_write() which writes to the tty driver->uart driver->chip.

    So if you are looking at some kind of a hack, I suggest you take a look at the btwilink.c code, after the st_register() call. But it will only succeed if BT device is up.

    Regarding the duration of the configuration of bd_addr -> it is valid as long as the chip is on. (Until you do "hciconfig hci0 down") 

  • Sundeep, thank you for your answer.  The key was identifying the ti_st_send_frame() method.

    After instrumenting btwilink.c, I found that the kernel equivalent of "hcitool cmd 0x3F 0x06 <address>" in the kernel is

    hci_send_cmd(hdev, 0xFC06, 6, bd_addr);

    where bd_addr is a u8[6] array containing the board address; OUI high byte in bd_addr[0].

    As you point out, the scope is "as long as the chip is on."  For platforms which turn the radio on and off (e.g. Android) it is necessary to reissue this command every time the radio is turned on - which fortuitously always involves a ti_st_open() call.  Hence the command can be inserted at the end of ti_st_open().

    I think that this would be useful for anyone using their own bd_addr (e.g. their own OUI) with one of these devices.  Hence I offer the attached patch, which creates a ti_btwilink_plat_data platform data structure containing a bd_addr.  It can be set (dynamically or statically) at system init when the platform code registers the device, and is picked up (if it exists) when the btwilink probe() occurs.  It is backward compatible; previous implementations will not have platform_data for btwilink and the code will have no effect.

    --- a/drivers/bluetooth/btwilink.c
    +++ b/drivers/bluetooth/btwilink.c
    @@ -46,12 +46,14 @@
      *	to be used by the driver during send_frame.
      * @wait_reg_completion - completion sync between ti_st_open
      *	and st_reg_completion_cb.
    + * @bd_addr: Board address set by probe() if supplied by platform
      */
     struct ti_st {
     	struct hci_dev *hdev;
     	char reg_status;
     	long (*st_write) (struct sk_buff *);
     	struct completion wait_reg_completion;
    +	u8 bd_addr[8];
     };
     
     /* Increments HCI counters based on pocket ID (cmd,acl,sco) */
    @@ -152,6 +154,8 @@
     	unsigned long timeleft;
     	struct ti_st *hst;
     	int err, i;
    +	#define TI_HCICMD_SET_BDADDR 0xFC06
    +	#define TI_HCICMD_BDADDR_LEN 6
     
     	BT_DBG("%s %p", hdev->name, hdev);
     
    @@ -227,6 +231,13 @@
     			return -EIO;
     		}
     	}
    +	/* If a board address has been assigned (e.g. OUI != 0), use it. */
    +	if (hst->bd_addr[0] != 0 || hst->bd_addr[2] != 0 ||
    +			hst->bd_addr[3] != 0) {
    +		hci_send_cmd(hdev, TI_HCICMD_SET_BDADDR, TI_HCICMD_BDADDR_LEN,
    +				hst->bd_addr);
    +	}
    +
     	return 0;
     }
     
    @@ -299,6 +308,7 @@
     
     static int bt_ti_probe(struct platform_device *pdev)
     {
    +	struct ti_btwilink_plat_data *pdata = pdev->dev.platform_data;
     	static struct ti_st *hst;
     	struct hci_dev *hdev;
     	int err;
    @@ -326,6 +336,16 @@
     	hdev->destruct = ti_st_destruct;
     	hdev->owner = THIS_MODULE;
     
    +	memset(hst->bd_addr, 0, sizeof(hst->bd_addr));
    +	if (pdata != 0 && (pdata->bd_addr[0] != 0 || pdata->bd_addr[1] != 0 ||
    +			pdata->bd_addr[2] != 0)) {
    +		memcpy(hst->bd_addr, pdata->bd_addr, sizeof(hst->bd_addr));
    +		BT_INFO("BD_ADDR address initialized to "
    +				"%02X:%02X:%02X:%02X:%02X:%02X.",
    +				hst->bd_addr[0], hst->bd_addr[1], hst->bd_addr[2],
    +				hst->bd_addr[3], hst->bd_addr[4], hst->bd_addr[5]);
    +	}
    +
     	err = hci_register_dev(hdev);
     	if (err < 0) {
     		BT_ERR("Can't register HCI device error %d", err);
    --- a/include/linux/ti_wilink_st.h
    +++ b/include/linux/ti_wilink_st.h
    @@ -445,4 +445,15 @@
     	int (*chip_awake) (void);
     };
     
    +/**
    + * struct ti_btwilink_plat_data - platform data shared between btwilink driver
    + *	and platform specific board file which adds the ST device.
    + * @bd_addr: Host's default BT board address.  If specified, this address will
    + *	override the address in the WL12xx fuse ROM.  8 byte array for
    + *	alignment; bytes 0-5 used for address.  Byte 0 is first byte of OUI.
    + */
    +struct ti_btwilink_plat_data {
    +	u8 bd_addr[8];
    +};
    +
     #endif /* TI_WILINK_ST_H */
    
    

  • Hi Scott,

    Maybe you might want to change:

    u8 bd_addr[8]; to 

    u8 bd_addr[6]; ???

    Otherwise, the patch looks fine.

  • I made bd_addr 8 bytes throughout for alignment reasons, even though only 6 are used.  Old habits!

    Now, it would be really useful if the same principal (assign default MAC addr in platform data) was applied to the WiFi side.  Unfortunately, that driver already has a platform data structure which it shares with other stuff, and the structure has changed in recent releases, so I am not comfortable offering my implementation there.  Volunteers?

    -S