安卓蓝牙sco vohci设计实现

​ 大家都知道, sco/esco可以分别通过pcm/i2s和vohci来支持. 最近, 某厂因为惯性设计, PCB线路图漏了PCM连接并不改硬件, 所以只能要求支持vohci. 再加上公司自身也要做好这种储备, 所以就在安卓平台上移植并开发了vohci功能.

​ 除了安卓需要开发这个功能外, 蓝牙芯片本身也是需要支持才可以的. 目前验证过博通/RK/AIC蓝牙芯片支持. RTK看驱动代码应该以前是开发过的, 提供的思路也很好. 但不知为何,现在RTK回应是不支持了…

1 PCM通路原理

首先, 我们看PCM通路下, 整个安卓平台支持起来的原理:

hci
PCM/I2s
Bluetooth APK
bluedroid
Controler
AudioManger
tinyalsa_hal
kernel/alsa

以上, sco/esco是直接通过kernel的alsa驱动, 写到蓝牙控制器的pcm接口上了. 现在vohci没有了pcm硬件接口, 按照正常想法, 需要bluedroid收发语音数据, 并参考tinyalsa_hal或者a2dp alsa应用接口, 开发播放和录音的模块, 但这样开发起来应该比较费时, 而且bluedroid改动应该比较多, 不怎么好.

2 Vohci通路原理

参考原先移植过的蓝牙语音遥控器驱动应用和代码, 语音数据可以走bluedroid再到kernel, 由kernel再对接到tinyalsa_hal. 这样可以充分利用原来的通路和框架, 逻辑也比较清晰.

这里面重要的工作, 是kernel怎么对接bluedroid和tinyalsa_hal. 所以可以开发一个虚拟的alsa声卡驱动, 让bluedroid发送hci rx语音数据到asla声卡, 并从声卡接收语音数据, 发送到蓝牙控制器中.

hci
Bluetooth APK
bluedroid
Controler
AudioManger
tinyalsa_hal
kernel/alsa
2.1 bluedroid处理

Bluedroid处理的关键, 是收取语音的HCI数据, 发送到kernel, 反之亦然. 所以, 这里通过一个文件节点(这里是字符文件), 把收到的控制器语音数据发送到alsa驱动; 并通过这个节点, 收取alsa驱动的数据, 发送到蓝牙控制器芯片.

可能大家会问, 上面说的是读写同一个文件节点, 那读写的数据会不会是同一份? 不会的, 具体下一节描述.

2.2 kernel alsa虚拟声卡驱动

为了同tinyalsa_hal交互, 就必须有一个声卡可供读写数据. 所以必须实现一个虚拟的vohci声卡驱动. 同时, alsa数据内容需要绑定到字符文件设备节点上, 应用写声卡的数据绑定到字符设备读取通道上, 而应用读声卡的语音数据则绑定到字符设备写通道上.

write
read
write
read
tinyalsa_hal
虚拟声卡驱动
proc/asound/cardx
bluedroid
字符设备文件
/dev/hci_sco_dev

以上虚拟声卡和字符设备因为要绑定, 所以还有一根双向的虚线, 由于markdown序列图不好画, 所以作罢.

2.3 tinyalsa_hal处理

在RK平台上, 声卡一般是两声道, 由于虚拟声卡驱动实现的也是两声道, 所以tinyalsa_hal无需特别处理, 只需要把声卡加到声卡即可. 当然, 这里前提是tinyalsa_hal已经支持蓝牙hfp sco pcm语音, 如果没有支持, 参考前面那篇<…16k … HFP PCM语音通话支持>那篇.

代码比较简单, 如下:

diff --git a/tinyalsa_hal/audio_hw.c b/tinyalsa_hal/audio_hw.c
index 441f5f0..e9f5922 100755
--- a/tinyalsa_hal/audio_hw.c
+++ b/tinyalsa_hal/audio_hw.c
@@ -325,6 +325,7 @@ struct dev_proc_info SPDIF_OUT_NAME[] =
 struct dev_proc_info BT_OUT_NAME[] =
 {
     {"rockchipbt", NULL,},
+    {"sndscohci", NULL},/*keep it behind rockchipbt*/
     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
 };
 
@@ -359,7 +360,8 @@ struct dev_proc_info HDMI_IN_NAME[] =
 
 struct dev_proc_info BT_IN_NAME[] =
 {
-    {"rockchipbt", NULL},
+    {"rockchipbt", NULL,},
+    {"sndscohci", NULL},/*keep it behind rockchipbt*/
     {NULL, NULL}, /* Note! Must end with NULL, else will cause crash */
 };
 
diff --git a/tinyalsa_hal/audio_hw.h b/tinyalsa_hal/audio_hw.h
index f23f529..7b8963a 100755
--- a/tinyalsa_hal/audio_hw.h
+++ b/tinyalsa_hal/audio_hw.h
@@ -190,15 +190,15 @@ struct pcm_config pcm_config_hfp = {
 struct pcm_config pcm_config_ap_sco = {
     .channels = 2,
     .rate = 8000,
-    .period_size = 160,
-    .period_count = 4,
+    .period_size = 480,
+    .period_count = 2,
 };
 
 struct pcm_config pcm_config_in_bt = {
     .channels = 2,
     .rate = 8000,
-    .period_size = 120,
-    .period_count = 4,
+    .period_size = 480,
+    .period_count = 2,
     .format = PCM_FORMAT_S16_LE,
 };

3 代码实现

3.1 bluedroid

这部分代码早期bluedroid是有的, 但是google后来删除了. 不过, ESP的bluedroid代码还是保留了, 目前还能支持SCO的HCI流控, buffer的协商等.只不过ESP语音的处理是bluedroid发送到应用层处理, 而我们这里是和/dev/hci_sco_dev设备文件交互, 并驱动内部转到虚拟声卡驱动.

需要注意的是, RK平台是两声道声卡, 而HCI是单声道, 所以bluedroid需要做好声道数据的转换工作.

另外, 我们添加了一个persist.bluetooth.vohci的设定, 设置到enabled, bluedroid自动走vohci, 反之走PCM. 这样达到PCM和vohci的自动兼容.

3.2 虚拟声卡驱动

声卡驱动实现为加载并sco连接后, 才会看到声卡, 不打开是看不见声卡的, 这样上层tinyalsa_hal可以兼容PCM sco声卡的情况.

同样要注意, RK平台tinyalsa_hal的两声道立体声实现, 为了避免上层不必要的改动, 声卡驱动也实现为两声道立体声.

3.3 tinyalsa_hal

如2.3节说明.

3.4 其他

安卓平台有些权限需要添加, 具体在device/rockchip目录下添加.

目的是加载声卡驱动后, 自动修改/dev/hci_sco_dev为bluetooth:net_bt用户组权限. 其他还有selinux的一些设定, 就不赘述.

4 结束

原理应该比较清楚了. 大家应该可以开始实现了. 虚拟声卡的驱动代码如果不会, 可以参考rtk的USB蓝牙声卡驱动; 或者aic的声卡驱动.

最后, 如果您不想改, 并可能想支持各种蓝牙HCI模组,又恰好是RK的客户, 可以尝试联系RK, 获取相应代码.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
蓝牙SCO编程是指在蓝牙技术中使用SCO(Synchronous Connection-Oriented)连接进行编程开发。SCO是一种同步连接类型,通常在蓝牙传输中用于音频数据的传输。 在Windows操作系统中进行蓝牙SCO编程,首先需要通过蓝牙API与蓝牙硬件进行通信。Windows提供了一些API函数,如BluetoothSetServiceState、BluetoothRegisterForIncomingConnect、BluetoothScoConnect等,可帮助开发者实现相关功能。 在蓝牙SCO编程中,首先需要使用BluetoothSetServiceState函数设置蓝牙设备的服务状态,确保SCO连接可用。接着,使用BluetoothRegisterForIncomingConnect函数注册回调函数,以便在有新的SCO连接请求时进行处理。在处理SCO连接请求时,可以使用BluetoothScoConnect函数与远程设备建立SCO连接。 与SCO连接建立后,可以使用Windows提供的音频API函数对音频数据进行读写。音频数据的读写可以使用WaveInOpen、WaveInPrepareHeader、WaveInAddBuffer等函数来实现。对于音频数据的传输,则可以使用BluetoothScoSendData函数来发送数据。 除了基本的音频数据传输外,还可以通过BluetoothScoDisconnect函数断开SCO连接。在断开连接后,使用WaveInClose函数关闭音频输入设备。 总之,通过使用Windows提供的蓝牙API函数,可以实现蓝牙SCO编程。具体的步骤包括设置蓝牙服务状态、注册回调函数、建立SCO连接、读写音频数据以及断开连接等。这些步骤可以帮助开发者在Windows操作系统上进行蓝牙SCO编程,并实现音频数据的传输。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值