下面看一下RPi 3B的bluetooth模块
- Raspbian系统
在Raspbian系统中,系统是通过ttyAMA0与bluetooth模块进行通信的,同时我们可以通过NOOBS_v1.9.0.zip/os/Raspbian/root.tar.xz/lib/systemd/system/hciuart.service文件知道Raspbian系统使用的BlueZ协议栈:
[Unit] Description=Configure Bluetooth Modems connected by UART ConditionPathIsDirectory=/proc/device-tree/soc/gpio@7e200000/bt_pins Before=bluetooth.service After=dev-ttyAMA0.device [Service] Type=forking ExecStart=/usr/bin/hciattach /dev/ttyAMA0 bcm43xx 921600 noflow - [Install] WantedBy=multi-user.target
从这里我们可以知道当/proc/device-tree/soc/gpio@7e20000/bt_pins目录存在的时候会去执行/usr/bin/hciattach /dev/ttyAMA0 bcm43xx 921600 noflow -命令,执行这条命令时:
- 上传patch文件:NOOBS_v1.9.0.zip/os/Raspbian/root.tar.xz/lib/firmware/BCM43430A1.hcd
- 更改ttyAMA0的波特率为921600。
- 创建rfkill1(/sys/class/rfkill/rfkill1)
之后,我们就可以通过bluetoothctl命令对bluetooth进行操作了。
NOTE:在kernel中,会使用UART (H4) protocol (drivers/bluetooth/hci_h4.c)。
- Brillo系统
在brillo-m10-dev系统中,与bluetooth相关的文件结构如下:
/local/brillo-m10-dev-rpi3b +-- device | `-- hzak | `-- rpi3b | `-- bsp | `-- bluetooth | +-- BCM43430A1.hcd | +-- bt_rpi3b.rc | +-- bt_vendor.conf | `-- vnd_rpi.txt +-- hardware | `-- broadcom | `--libbt `-- system `-- bt
1. patch文件存放在device/hzak/rpi3b/bsp/bluetooth/BCM43430A1.hcd编译后会放在system/vendor/firmware/下。
2. 配置libbt-vendor的编译环境(device/hzak/rpi3b/bsp/bluetooth/vnd_rpi.txt):
BLUETOOTH_UART_DEVICE_PORT = "/dev/ttyAMA0" FW_PATCHFILE_LOCATION = "/vendor/firmware/" BT_WAKE_VIA_PROC = FALSE BT_WAKE_VIA_USERIAL_IOCTL = FALSE LPM_IDLE_TIMEOUT_MULTIPLE = 5 BTVND_DBG = TRUE BTHW_DBG = TRUE VNDUSERIAL_DBG = TRUE UPIO_DBG = TRUE SCO_CFG_INCLUDED = FALSE SCO_PCM_IF_CLOCK_RATE = 2 USE_CONTROLLER_BDADDR = FALSE UART_TARGET_BAUD_RATE = 921600
- 使用串口:/dev/ttyAMA0
- patch文件放在/vendor/firmware即/system/vendor/firmware目录下
- 无唤醒控制
- 指定bluetooth初始化之后使用的串口波特率为921600
3. 修改system/bt/osi/src/alarm.c文件(再次强调):
hzak@B85RPI:/local/brillo-m10-dev-rpi3b/system/bt$ git diff diff --git a/osi/src/alarm.c b/osi/src/alarm.c index fa4f856..62d396c 100644 --- a/osi/src/alarm.c +++ b/osi/src/alarm.c @@ -96,11 +96,11 @@ struct alarm_t { int64_t TIMER_INTERVAL_FOR_WAKELOCK_IN_MS = 3000; static const clockid_t CLOCK_ID = CLOCK_BOOTTIME; -#if defined(KERNEL_MISSING_CLOCK_BOOTTIME_ALARM) && (KERNEL_MISSING_CLOCK_BOOTTIME_ALARM == TRUE) +//#if defined(KERNEL_MISSING_CLOCK_BOOTTIME_ALARM) && (KERNEL_MISSING_CLOCK_BOOTTIME_ALARM == TRUE) static const clockid_t CLOCK_ID_ALARM = CLOCK_BOOTTIME; -#else -static const clockid_t CLOCK_ID_ALARM = CLOCK_BOOTTIME_ALARM; -#endif +//#else +//static const clockid_t CLOCK_ID_ALARM = CLOCK_BOOTTIME_ALARM; +//#endif // This mutex ensures that the |alarm_set|, |alarm_cancel|, and alarm callback // functions execute serially and not concurrently. As a result, this mutex
4. 设置系统属性ro.rfkilldisabled=1,由于使用的是bluedroid(system/bt)协议栈,不再需要rfkill?
NOTE:
a. 由于bluetooth与WiFi模块在同一个芯片中,所以需要WiFi的固件要先加载
b. 当前bluetooth模块不支持multi-adversing,这就意味着无法将bluetooth role设为peripheral
[2016-04-06 21:46:09]
下面的代码片段使Android设备的Bluetooth模块工作在peripheral模式下:
public class BleActivity extends Activity { private final static String TAG = "BleActivity"; private BluetoothGattServerCallback mCallback = new BluetoothGattServerCallback() { @Override public void onConnectionStateChange(BluetoothDevice device, int status, int newState) { } @Override public void onServiceAdded(int status, BluetoothGattService service) { } @Override public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) { } @Override public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { } @Override public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) { } @Override public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) { } @Override public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) { } @Override public void onNotificationSent(BluetoothDevice device, int status) { } @Override public void onMtuChanged(BluetoothDevice device, int mtu) { } }; private BluetoothManager mBm; private BluetoothGattServer mServer; private BluetoothGattService mPrimaryService; private BluetoothGattCharacteristic mPrimaryChar; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBm = (BluetoothManager)getSystemService(BLUETOOTH_SERVICE); mPrimaryService = new BluetoothGattService(UUID.fromString( "0000180d-0000-1000-8000-00805f9b34fb"), BluetoothGattService.SERVICE_TYPE_PRIMARY); mPrimaryChar = new BluetoothGattCharacteristic(UUID.fromString( "00002a37-0000-1000-8000-00805f9b34fb"), BluetoothGattCharacteristic.PROPERTY_READ, BluetoothGattCharacteristic.PERMISSION_READ); mPrimaryService.addCharacteristic(mPrimaryChar); } @Override public void onResume() { super.onResume(); mServer = mBm.openGattServer(getApplicationContext(), mCallback); mServer.addService(mPrimaryService); AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder(); builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY); builder.setConnectable(true); builder.setTimeout(0); builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH); AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder(); dataBuilder.addServiceUuid(ParcelUuid.fromString( "0000180d-0000-1000-8000-00805f9b34fb")); dataBuilder.setIncludeDeviceName(true); BluetoothAdapter adapter = mBm.getAdapter(); BluetoothLeAdvertiser advertiser = adapter.getBluetoothLeAdvertiser(); if (advertiser == null) { Toast.makeText(this, "Bluetooth LE advertising is not support!", Toast.LENGTH_SHORT).show(); return; } advertiser.startAdvertising(builder.build(), dataBuilder.build(), new AdvertiseCallback() { @Override public void onStartFailure(int errCode) { } @Override public void onStartSuccess(AdvertiseSettings settingsInEffect) { } }); } @Override public void onPause() { super.onPause(); mServer.close(); } }
NOTE: 需要Android系统具有android.hardware.bluetooth_le这个feature。Nexus4 bluetooth模块不支持Bluetooth LE advertising, 执行BluetoothAdapter.getBluetoothLeAdvertiser()会返回null。
- 相关的参考文档:
- https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothOverview/CoreBluetoothOverview.html#//apple_ref/doc/uid/TP40013257-CH2-SW1
4. 设置系统属性ro.rfkilldisabled=1,由于使用的是bluedroid(system/bt)协议栈,不再需要rfkill?
—>rfkill是用来控制蓝牙上下电的。
a. 由于bluetooth与WiFi模块在同一个芯片中,所以需要WiFi的固件要先加载
—>不需要先加载wifi固件。
b. 当前bluetooth模块不支持multi-adverser,这就意味着无法将bluetooth role设为peripheral
—>不支持multi-adverser也可以设为peripheral。
1. 目前来看由于RPi 3B使用的BCM43438模块,WiFi与Bluetooth模块共用RF, bluetooth的RF无法关闭?我对这一部分外行,也没有找到相关的文档。如果将不将ro.rfkilldisabled设为1, 在bluetooth-cli中执行enable时,bluetoothtbd service会挂掉,所以将ro.killdisabled设为了0。
2. 是要先加载WiFi固件的,我猜想这个固件是对针对整个模块,不单单只是使WiFi模块进入工作状态。WiFi固件没有加载,无法通过bluetooth-cli中enable bluetooth。
3. 我的表述有问题,由于当然Brillo系统只是让bluetooth模块工作在ble模式,而在ble模式下,当bluetooth role设为peripheral,需要max advertise instance >= 5, 由于RPi 3B max advertise instance=1达不到,无法进行advertising, 其他bluetooth设备无法搜索到该设备:
在执行bluetooth-cli
# enable
# register-ble
# start-adv -n -t -c
后,logcat中会有如下log:
04-02 11:09:58.539 391 397 E bt_btif : btgattc_handle_event invalid index in BTIF_GATTC_ENABLE_ADV
04-02 11:09:58.539 391 405 W bt_btif : bta_gattc_multi_adv_cback Invalid p_ref received
1
1
1