How to use UART3 for bluetooth HCI?



I need to use UART3(J2002 - 3,5,7,9 pins) for bluetooth HCI so that I can connect another bluetooth controller module to the hikey960 board. Please let me know how to route the HCI traffic to UART3 instead of default UART4(/dev/ttyAMA4).


running Android ?

Configure access rights:

  • Set correct permission to the tty device in device/linaro/hikey/ueventd.common.rc
    e.g: /dev/ttyAMA3 0660 bluetooth bluetooth
  • sepolicy: Change /dev/ttyAMA3 file context from console_device to hci_attach_dev in device/linaro/hikey/sepolicy/file_contexts.

Now depending your BT controller you have to attach it to the BT stack at boot time using hciattach or btattach tool:
$ hciattach /dev/ttyAMA3 any

You should then have a hci device created in /dev, hci0 for legacy wilink device and hci1 for your device.

By default android use hci0, but you can force hci1 usage by setting the hci index property (bluetooth.interface)
$ setprop bluetooth.interface 1

If it works, you will need to include this in init script (rc).


Hi Loic,

Thank you very much for your detail and clear explanation. I am running Android 8.0.0 on my Hikey960.
I will have another BT controller connected to Hikey board via UART 3 pins (J2002). As far as I know, ‘hciattach’ works with BlueZ stack. As Android-8.0.0 has Fluoride Stack not BlueZ, I cannot find any hciattach tool. Can you please guide me how to attach hikey ttyAMA3 to the module which will be connected to J2002?

Regarding setprop, where should I include that command? init.common.rc/init.common.usb.rc/init.hikey.power.rc/init.hikey960.power.rc ?

Thanking you,

Arnab Dey


You’re right the default Android Bluetooth host stack is now Fluoride (formerly Bluedroid). This stack is intended to communicate with Bluetooth controller via HCI-H4 protocol which is defined by the BT specification. Basically, Fluoride stack relies on some vendor layer HAL for controller specific stuff and is so controller agnostic.

Since serial-uart is the dominant BT interface in embedded world, many vendors have implemented their own vendor lib/HAL(user-space driver) in which they typically open a tty device (e.g. /dev/ttyS1) perform the chip initialization, download the firmware and then give control to the BT stack .

                     [ FLUORIDE STACK ]
      [ VENDOR HAL1 ]  [ VENDOR HAL2 ] [ VENDOR HAL3 ]
    --------------------------------------------------- (user/kernel)
                [ (ttyS0) uart subsystem ]
                             | (uart)
                       BT CONTROLLER

However, that means you need to re-implement a vendor HAL if you want to support a new chip or use a different bus (USB, SDIO, internal bus…), which can make the things complicated. Point is that most of this support is already implemented in the Linux Kernel Bluetooth subsystem which exposes a standard BT interface via hardware agnostic hci devices (hci0, hci1…).

This interface is accessed via sockets (AF_BLUETOOTH) offering different protocol levels, (L2CAP, RFCOMM…) including raw HCI protocol. Bluez user-space daemon typically uses most of these sockets to implement Bluetooth profiles.

                  [ Bluez ]
 --------------------------------------- (user / kernel)
      [ (hci0) RFCOMM | L2CAP | HCI sockets ]
              [ Bluetooth Subsystem ]
 [ BT Driver 1 ] [ BT Driver 2 ] [ BT Driver 3 ]
                        |  (usb, uart, sdio....)
                   BT CONTROLLER

So one of the idea has been to use the existing bluetooth linux subsystem for harware abstraction but keeping Fluroide/bluedroid has upper stack, this can be achieved creating a ‘generic’ vendor lib which only have to open a HCI socket and give control to upper stack.

               [ Fluoride/Bluedroid ]
                [ Linux Vendor Lib ]
               [ (HCI0 HCI socket ]
              [ Bluetooth Subsystem ]
 [ BT Driver 1 ] [ BT Driver 2 ] [ BT Driver 3 ]
                        |  (usb, uart, sdio....)
                   BT CONTROLLER

Note that high level protocols sockets/interfaces (RFCOMM, L2CAP…) are not used in that case because Fluoride/Bluedroid stack implements this internally.

The advantage of this is that only one vendor lib can now support all Bluetooth controller already supported by Linux, bringing by the way BT usb dongle support to Android. Avoiding in the same time driver fragmentation and effort for vendors/manufacturer, only one driver for regular-Linux/Android instead of two, allowing by the same time ‘bluez’ tools usage (hciconfig, btmon…) on both OS.

Today, this architecture is used with the Hikey960 via the ‘bt-linux’ vendor HAL which does exactly what I explained, open a generic HCI socket to hci0. So there is no change to apply at this level.

However, on ‘old’ kernel there is no simple way to enumerate/detect Bluetooth controllers behind serial/uart. You just have your standard tty device and need to communicate to the BT subsystem, Hey! there is a BT controller behind this tty, this is a TI, qcom, broadcom or generic one, please create a hci device. It is precisely the role of btattach (or hciattach), which can be found here:, This contains so you can just clone this repo in android externel directory and then add hciattach (and tools) to the list of packages to build:
e.g, in device/linaro/hikey/hikey960/
PRODUCT_PACKAGES += hciattach hciconfig hcitool

I suggest you to test hciattach manually in command line before trying to configure this at init:
Something like hciattach /dev/ttyAMA3 any should give you a hci device you can test with hciconfig/hcitools.

Once all of this works You will have to configure the permission/policy as I already explained before and add a service in the init.rc:

service hciattach /system/bin/hciattach -n /dev/ttyAMA4 any
class main
user bluetooth
group bluetooth net_bt_admin

And also link hciattach to the bluetooth ‘hci_attach’ domain:
/(vendor|system/vendor)/bin/hciattach u:object_r:hci_attach_exec:s0

You can remove the existing uim service in init.common.rc, this should avoid the creation of the hci device for the embedded controller and so let your external controller get index 0 (hci0).


Hi Loic,

Thank you very much for this details of the architecture. After porting hciattach, hciconfig, hcitool from BlueZ stack, I could communicate with my BT controller via UART3 from command line. However, getting ‘hci0’ index for my controller did not work. init code is not executing hciattach service. Can you please help?

I have added the following in init file -

service hciattach /system/bin/hciattach -s 115200 /dev/ttyAMA3 any
class main
user bluetooth
group bluetooth net_bt_admin system

Added the following in ‘file_contexts’ -

/system/bin/hciattach u:object_r:hci_attach_exec:s0

I can not see any trace of it in kernel message.

manually starting the service(#start hciattach) throws the following error -

init: Could not ctl.start service ‘hciattach’: File /system/bin/hciattach(labeled “u:object_r:system_file:s0”) has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined

Please help.

Thanking you,

Arnab Dey


Hi Loic,

I had to do the following to make the hciattach service start as part of boot sequence -

  1. add the following in service declaration in init.common.rc -

       seclabel u:object_r:hci_attach_exec:s0
  2. change selinux permission at the kernel command line in /device/linaro/hikey960/ -

     BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive

Then it worked and I can see hci1 available after boot up. Can you please tell how to address the issue to make hciattach work without removing selinux permission(i.e skipping step 2 above as it is not recommended I believe)?

Thanking you,

Arnab Dey


Problem is that there is a unpermitted transition from the init domain (init process) to the system_file domain (system file) when executing /system/bin/hciattach. However this should be a transition from init domain to hci_attach domain since you defined /system/bin/hciattach with the hci_attach_exec type (hci_attach domain).
Did you re-flash system and boot partition ?

Not sure, but it could be an android /system/bin restriction, try tor install tools in vendor instead (system/vendor) and add the following rule:
/(vendor|system/vendor)/bin/hciattach u:object_r:hci_attach_exec:s0

In a first step I would suggest to run in permissive mode and run the service manually, then start your service and retrieve all the selinux related errors in dmesg, then fix them manually (if any) or using audit2allow tool.

Once all the rules are fixed you can re-enable enforce mode.


Hi Loic,

If I change any sepolicy, usually I re-flash both system and boot. If I change only in init.rc, then only boot.
Moved ‘hciattach’ to /system/vendor, still getting sepolicy denial error -

type=1400 audit(15.555:47): avc: denied { transition } for pid=2159 comm="init" path="/system/vendor/bin/hciattach" dev="sdd10" ino=2539 scontext=u:r:init:s0 tcontext=u:object_r:hci_attach_exec:s0 tclass=process permissive=0

I changed the ‘user’ option to ‘root’ also in the service declaration in init.rc file, still getting the same error. Please help.

Thanking you,

Arnab Dey


I think this comes from the seclabel line you added:

Remove it or use this one instead:
seclabel u:r:hci_attach:s0


Hi Loic,

If I remove that label, the service is not being called at all as part of init. Using u:r:hci_attach:s0 throws invalid context error.

I used audit2allow tool to analyze and it gave me following suggestion -

allow init hci_attach_exec:process transition;

If I add it in device/linaro/hikey/sepolicy/init.te, and then make system image + boot image again it is not taking effect. Where should I add it? Will I have to clean and rebuild?
Should I add this as suggested by audit2allow ?

Thanking you,

Arnab Dey


If you kept the original hikey sepolicy files, domain transition should be already done in hci_attach.te:

What is looking weird to me is that audit2allow suggest to add the transition rule with hci_attach_exec as destination domaine, this should be hci_attach. So I think there is a problem in your rules, hci_attach is a domain, hci_attach_exec is a type. Could you please check this point and provide all the rules you changed ?


Hi Loic,

I have not changed any sepolicy rules. Only I have added the following in device/linaro/hikey/sepolicy/file_contexts -

/(vendor|system/vendor)/bin/hciattach u:object_r:hci_attach_exec:s0

in device/linaro/hikey/ueventd.common.rc, I have modified -

/dev/ttyAMA3      0660 bluetooth bluetooth

My device/linaro/hikey/sepolicy/hci_attach.te contains the following -

type hci_attach, domain;
type hci_attach_exec, exec_type, file_type;


allow hci_attach kernel:system module_request;
allow hci_attach hci_attach_dev:chr_file rw_file_perms;
allow hci_attach bluetooth_efs_file:dir r_dir_perms;
allow hci_attach bluetooth_efs_file:file r_file_perms;

allow hci_attach rootfs:lnk_file getattr;
allow hci_attach sysfs:file r_file_perms;

Please help.

Thanking you,

Arnab Dey

Not able to start my system service added to system_server

Hi Loic,

You are absolutely right. I tried removing ‘seclabel’ and ‘seclabel u:r:hci_attach:s0’ when my hciattach was in /system/bin. After moving it to /system/vendor/bin as suggested by you, I did not try this. Now my hciattach is in system/vendor/bin. I just removed seclabel and it worked fine!!! Thank you a lot!!!
Can you please make me understand why specifying this here causes problem? Is not it equivalent to mentioning in ‘file_contexts’ as we did?

audit2allow shows following suggestions -

#============= audioserver ==============
allow audioserver default_android_service:service_manager add;
allow audioserver hal_broadcastradio_hwservice:hwservice_manager find;

#============= healthd ==============
allow healthd self:capability2 wake_alarm;

#============= init ==============
allow init cgroup:file create;

#============= mediadrmserver ==============
allow mediadrmserver default_android_service:service_manager add;

#============= netd ==============
allow netd proc_net:dir write;

#============= zygote ==============
allow zygote cgroup:file create;

Should I make the suggested changes or skip them?

Thanking you,

Arnab Dey


Great !

Not necessarily, except if you observe issues associated.


Hi Loic,

Sure. Now, how to make my BT controller to be detected as hci0?

or if I do -

   setprop bluetooth.interface 1

under ‘on post-fs’ will my controller respond from UI(i.e. from android toggle button to turn on/off bluetooth)?

Thanking you,

Arnab Dey


The hci ID is allocated by the Bluetooth subsystem (kernel) on hci device creation. So yes you have two possibilities:

  1. Prevent hci0 creation
    Think you can prevent this by removing uim service from init
  2. Force usage of hci1 by the Androdi BT stack
    setprop bluetooth.interface 1 should work, but never tested.


Hi Loic,

removing uim service does not make my BT controller become hci0. Instead default TI chip-hci0 stops responding to bluetooth turning on/off -

service call bluetooth_manager 6

if I have uim running, then the above command turns hci0(TI chip) ‘UP RUNNING’ and without uim there is no effect on hci0(TI chip). It stays ‘DOWN’ in that case.

setprop method also did not work.

Is it possible to change the hikey960 kernel code to achieve this? i.e. I could see in the device tree (/arch/arm64/boot/dts/hisilicon/hi3660-hikey960.dts) in hikey kernel code,
&uart4 has the following child-node declared -

bluetooth {
compatible = “ti,wl1837-st”;
enable-gpios = <&gpio15 6 GPIO_ACTIVE_HIGH>;
max-speed = <921600>;

Is it possible to do some tweaks here for my controller for uart3?
Or is there any specific command I can provide to kernel using ‘BOARD_KERNEL_CMDLINE’ in

Thanking you,

Arnab Dey


OK, so the basic idea would be to remove this bluetooth child node from uart3, then hci0 should not be created.
For your case, don’t add any child node, the ‘enumeration’ performed by user-space (hciattach).


Hi Loic,

Removing child-node from uart4 node did not work. I tried to remove status overlay by making uart4 status=“disabled” in hi3660-hikey960.dts. That also did not work. hci0 was still getting created for TI controller. Can you please guide?

Thanking you,

Arnab Dey


I don’t really see how it can work, if you removed uim service and device-tree entry no TI hci device can be created. Could you please double check new DTB is used (build instruction), basically modify your dts, build the new DTB, copy the dtb into aosp /device/linaro/hikey-kernel/hi3660-hikey960.dtb-4.9, and then regenerate and flash the boot image.

Note that you can check access device tree in /proc/device-tree at runtime to check your updates.