交叉编译均在Ubuntu 22.04环境操作

交叉编译前准备

目录结构

由于bluez编译过程中依赖项比较多,所以创建一个文件夹,作为编译目录,文件目录结构如下

bluez-cross-complier/
├── download #源码下载在此处操作
└── output #编译产物输出到此目录
└── source-code #源码存放目录

源码下载

BlueZ Readline Ncurses D-Bus Expat Glib Zlib libffi gettext

cd bluez-cross-complier/download/
wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.66.tar.xz
wget ftp://ftp.cwru.edu/pub/bash/readline-8.2.tar.gz
wget https://ftp.gnu.org/gnu/ncurses/ncurses-6.4.tar.gz
wget https://dbus.freedesktop.org/releases/dbus/dbus-1.15.0.tar.xz
wget https://github.com/libexpat/libexpat/releases/download/R_2_6_2/expat-2.6.2.tar.gz
wget https://download.gnome.org/sources/glib/2.46/glib-2.46.2.tar.xz
wget https://zlib.net/zlib-1.3.1.tar.gz
wget https://github.com/libffi/libffi/releases/download/v3.4.5/libffi-3.4.5.tar.gz
wget https://ftp.gnu.org/pub/gnu/gettext/gettext-0.22.5.tar.gz
for i in ./*.tar.*; do tar -xvf "$i" -C ./../source-code/; done

最终的目录结构为

bluez-cross-complier/
├── download
│ ├── bluez-5.66.tar.xz
│ ├── dbus-1.15.0.tar.xz
│ ├── expat-2.6.2.tar.gz
│ ├── gettext-0.22.5.tar.gz
│ ├── glib-2.46.2.tar.xz
│ ├── libffi-3.4.5.tar.gz
│ ├── ncurses-6.4.tar.gz
│ ├── readline-8.2.tar.gz
│ └── zlib-1.3.1.tar.gz
├── output
└── source-code
├── bluez-5.66
├── dbus-1.15.0
├── expat-2.6.2
├── gettext-0.22.5
├── glib-2.46.2
├── libffi-3.4.5
├── ncurses-6.4
├── readline-8.2
└── zlib-1.3.1

设置环境变量

为了方便后面编译时直接使用

export bluez_output=/home/kaikai/hl_project/bluez-cross-complier/mstar-output
export bluez_cc=arm-sigmastar-linux-uclibcgnueabihf-gcc
export bluez_cxx=arm-sigmastar-linux-uclibcgnueabihf-g++
export bluez_host=arm-sigmastar-linux-uclibcgnueabihf

交叉编译

Readline交叉编译

注意:如果是用的6.5版本的ncures,生成的库名是libncursesw.a

cd source-code/readline-8.2/
./configure --prefix=$bluez_output CC=$bluez_cc CXX=$bluez_cxx --host=$bluez_host CFLAGS="-fPIC"
make -j8 && make install
  • CFLAGS=”-fPIC” : 为了后面方便连接静态库

ncurses交叉编译

cd source-code/ncurses-6.4/
./configure --prefix=$bluez_output CC=$bluez_cc CXX=$bluez_cxx --host=$bluez_host --with-shared
make -j8 && make install
  • –with-shared 默认生成静态库,加这个参数同时生成静态库和动态库

glib-2.46.2编译前准备

在正式编译glib之前需要提前编译依赖库

gettext交叉编译

解决:You must have either have gettext support in your C library, or use the GNU gettext library.

cd source-code/gettext-0.22.5/
./configure --prefix=$bluez_output CC=$bluez_cc CXX=$bluez_cxx --host=$bluez_host CFLAGS="-fPIC"
make -j8 && make install

编译libffi

解决: gclosure.c:28:10: fatal error: ffi.h: No such file or directory

cd source-code/libffi-3.4.5
./configure --prefix=$bluez_output CC=$bluez_cc CXX=$bluez_cxx --host=$bluez_host CFLAGS="-fPIC"
make -j8 && make install

编译zlib

解决:gzlibcompressor.c:26:10: fatal error: zlib.h: No such file or directory

cd source-code/zlib-1.3.1/
./configure --prefix=$bluez_output

由于zlib的auto configure不支持命令行修改编译链,所以需要手动修改makefile

CC=arm-sigmastar-linux-uclibcgnueabihf-gcc
CFLAGS=-O3 -D_LARGEFILE64_SOURCE=1 -DHAVE_HIDDEN -fPIC
LDSHARED=arm-sigmastar-linux-uclibcgnueabihf-gcc -shared -Wl,-soname,libz.so.1,--version-script,zlib.map
CPP=arm-sigmastar-linux-uclibcgnueabihf-gcc -E
AR=arm-sigmastar-linux-uclibcgnueabihf-ar
RANLIB=arm-sigmastar-linux-uclibcgnueabihf-ranlib
make -j8 && make install

增加arm-linux.cache

解决:configure: error: cannot run test program while cross compiling 参考连接
vim glib-2.46.2/arm-linux.cache

#编译链不同,检查的内容也不通
glib_cv_stack_grows=no
glib_cv_uscore=no
ac_cv_func_posix_getpwuid_r=yes
ac_cv_func_posix_getgrgid_r=no

修改源码

可选项:这是代码编译过程中警告被当作错误处理,如果不想修改源码,可使用-Wno-error参数忽略指定错误

  • gdate.c:2497:7
    解决:gdate.c:2497:7: error: format not a string literal, format string not checked [-Werror=format-nonliteral]
    添加下面的代码到gdate.c
    #pragma GCC diagnostic ignored "-Wformat-nonliteral"
  • gdbusauth.c
    解决:gdbusauth.c:1295:11: error: ‘%s’ directive argument is null [-Werror=format-overflow=]
    gdbusmessage.c:2698:30: error: ‘%s’ directive argument is null [-Werror=format-overflow=]

    参考gdbusauth.c:1302:11: error: ‘%s’ directive argument is null

正式编译glib-2.46.2

cd source-code/glib-2.46.2/
./configure --prefix=$bluez_output CC=$bluez_cc --host=$bluez_host CPPFLAGS="-I$bluez_output/include" LDFLAGS="-L$bluez_output/lib" --cache-file=arm-linux.cache --enable-static
make CFLAGS="-Wno-error=format-nonliteral" ZLIB_LIBS=-l:libz.a LIBFFI_LIBS="-l:libffi.a" -j8
make install
  • configure 编译添加 ZLIB_LIBS=-l:libz.a LIBFFI_LIBS=-l:libffi.a链接静态库
  • –enable-static 编译同时生成静态库

编译D-Bus

编译expat-2.6.2

cd source-code/expat-2.6.2/
sudo apt-get install -y docbook2x
./configure --prefix=$bluez_output CC=$bluez_cc CXX=$bluez_cxx --host=$bluez_host CFLAGS="-fPIC"
make -j8 && make install

编译D-Bus

cd source-code/dbus-1.15.0/
./configure --prefix=$bluez_output CC=$bluez_cc CXX=$bluez_cxx --host=$bluez_host CFLAGS="-fPIC" CPPFLAGS="-I$bluez_output/include" LDFLAGS="-L$bluez_output/lib" --with-dbus-user=root --with-system-socket=/tmp/system_bus_socket --with-system-pid-file=/tmp/messagebus.pid --enable-selinux=no --without-x
make EXPAT_LIBS="-l:libexpat.a" LIBS="-l:libz.a -l:libffi.a -l:libintl.a" GLIB_LIBS="-l:libgio-2.0.a -l:libgmodule-2.0.a -l:libgobject-2.0.a -l:libglib-2.0.a" -j8
make install
  • –with-dbus-user=root –with-system-socket=/tmp/system_bus_socket –with-system-pid-file=/tmp/messagebus.pid:如何你的目标开发板文件系统是只读的建议加这几个参数

编译BlueZ

静态链接readline/glib/d-bus

glib/d-bus: GLIB_LIBS=”-l:libglib-2.0.a” DBUS_LIBS=”-l:libdbus-1.a”
readline: 生成makefile后将makefile里的-lreadline替换为-l:libreadline.a

cd source-code/bluez-5.66/
./configure --prefix=$bluez_output CC=$bluez_cc CXX=$bluez_cxx --host=$bluez_host CFLAGS="-fPIC" CPPFLAGS="-I$bluez_output/include" LDFLAGS="-L$bluez_output/lib" --enable-btpclient --disable-systemd --disable-udev --disable-cups --disable-obex --enable-library --enable-deprecated --localstatedir=/tmp --disable-bap --disable-mcp --disable-vcp
make GLIB_LIBS="-l:libglib-2.0.a" DBUS_LIBS="-l:libdbus-1.a" LIBS="-l:libintl.a -l:libreadline.a -l:libncurses.a" -j8
make install
  • –enable-deprecated: 编译hciconfig、hcitool、hcidump、hciattach
  • –enable-btpclient:编译btpclient
  • –localstatedir=/tmp: bluetoothd运行会创建文件保存信息,确保路径可读写
    以上,至此Bluez交叉编译完成。

移植到目标开发板

hci工具

生成路径:$bluez_output/bin
hciconfig、hcitool、hcidump、hciattach这4个工具可以独立使用,如果编译链接了readline和ncurses的静态库

dbus

dbus-daemon生成路径:$bluez_output/bin
dbus-daemon conf路径:$bluez_output/share/dbus-1
libdbus-1.so.3路径:$bluez_output/lib

bluetoothd/bluetoothctl

bluetoothd生成路径:$bluez_output/libexec/bluetooth
bluetoothctl生成路径:$bluez_output/bin
配置文件路径:bluez源码,src/bluetooth.conf
libreadline.so.8路径:$bluez_output/lib
bluetooth.confbluetooth.conf复制到share/dbus-1/system.d/目录下

最后移植的内容如下

├── bin
│ ├── bluetoothctl
│ ├── bluetoothd
│ ├── dbus-daemon
│ ├── hciattach
│ ├── hciconfig
│ ├── hcidump
│ └── hcitool
├── lib
│ ├── libdbus-1.so
│ ├── libdbus-1.so.3
│ ├── libdbus-1.so.3.34.0
│ ├── libreadline.so
│ ├── libreadline.so.8
│ └── libreadline.so.8.2
└── share
└── dbus-1
├── services
├── session.conf
├── session.d
├── system.conf
├── system.d
│ └── bluetooth.conf
└── system-services

bluez运行

运行bluetoothd

下面的操作是将程序和库放到SD卡里使用

export LD_LIBRARY_PATH='/system/lib:/mnt/sdcard/bluez-5.66/lib'
./dbus-daemon --config-file=/mnt/sdcard/bluez-5.66/share/dbus-1/system.conf
./bluetoothd -n -C &

测试bluetoothctl

./bluetoothctl

Agent registered        uetoothd...
[CHG] Controller EB:BF:AD:FC:FD:FE Pairable: yes
[bluetooth]# help
Menu main:
Available commands:
-------------------
advertise Advertise Options Submenu
monitor Advertisement Monitor Options Submenu
scan Scan Options Submenu
gatt Generic Attribute Submenu
admin Admin Policy Submenu
player Media Player Submenu
endpoint Media Endpoint Submenu
transport Media Transport Submenu
list List available controllers
show [ctrl] Controller information
select <ctrl> Select default controller
devices [Paired/Bonded/Trusted/Connected] List available devices, with an optional property as the filter
system-alias <name> Set controller alias
reset-alias Reset controller alias
power <on/off> Set controller power
pairable <on/off> Set controller pairable mode
discoverable <on/off> Set controller discoverable mode
discoverable-timeout [value] Set discoverable timeout
agent <on/off/capability> Enable/disable agent with given capability
default-agent Set agent as the default one
advertise <on/off/type> Enable/disable advertising with given type
set-alias <alias> Set device alias
scan <on/off/bredr/le> Scan for devices
info [dev] Device information
pair [dev] Pair with device
cancel-pairing [dev] Cancel pairing with device
trust [dev] Trust device
untrust [dev] Untrust device
block [dev] Block device
unblock [dev] Unblock device
remove <dev> Remove device
connect <dev> Connect device
disconnect [dev] Disconnect device
menu <name> Select submenu
version Display version
quit Quit program
exit Quit program
help Display help about this program
export Print environment variables
[bluetooth]#

bluetoothd/bluetoothctl使用过程中遇到的其他问题

bluetoothd启动后没有报错直接退出

内核没有开启CONFIG_SIGNALFD配置,导致不支持signalfd函数,内核开启该配置即可。

原因bluez源码此处报错直接返回了,没有打印错误信息

static struct io *setup_signalfd(void *user_data)
{
struct io *io;
sigset_t mask;
int fd;

sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGUSR2);
sigaddset(&mask, SIGCHLD);

if (sigprocmask(SIG_BLOCK, &mask, NULL) < 0)
return NULL;

fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (fd < 0)
return NULL;

io = io_new(fd);

io_set_close_on_destroy(io, true);
io_set_read_handler(io, signal_read, user_data, free);

return io;
}

测试demo

编译后在板子里执行如果报错Function not implemented,在内核开启signalfd配置即可

#include <signal.h>
#include <sys/signalfd.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
sigset_t mask;
int sfd;
struct signalfd_siginfo fdsi;

// 初始化信号集
sigemptyset(&mask);
sigaddset(&mask, SIGINT); // 添加 SIGINT 到信号集

// 创建 signalfd
sfd = signalfd(-1, &mask, 0);
if (sfd == -1) {
perror("signalfd"); //此处报错Function not implemented
exit(EXIT_FAILURE);
}

// 读取信号
while (1) {
ssize_t s = read(sfd, &fdsi, sizeof(fdsi));
if (s != sizeof(fdsi)) {
perror("read");
exit(EXIT_FAILURE);
}
if (fdsi.ssi_signo == SIGINT) {
printf("Received SIGINT\n");
}
}

// 关闭文件描述符
close(sfd);

return 0;
}

bluetoothctl报错

Failed to start discovery: org.bluez.Error.NotReady

[bluetooth]# scan on
Failed to start discovery: org.bluez.Error.NotReady
[bluetooth]# power on
Changing power on succeeded
[CHG] Controller EB:BF:AD:FC:FD:FE Powered: yes

Failed to start discovery: org.bluez.Error.InProgress

[bluetooth]# scan on
Failed to start discovery: org.bluez.Error.InProgress
  • 网上搜到类似问题 ,但是无法解决我遇到的问题 网上类似问题
  • hcitool可以scan,通过对比二者的hcilog,发现hcitool使用的基础指令,bluetoothd使用的是extened指令集,解决方案需要蓝牙芯片厂商给controller开启extened指令集
    //bluetoothd/bluetoothctl使用
    #define BT_HCI_CMD_LE_SET_EXT_SCAN_PARAMS 0x2041
    struct bt_hci_cmd_le_set_ext_scan_params {
    uint8_t own_addr_type;
    uint8_t filter_policy;
    uint8_t num_phys;
    uint8_t data[0];
    } __attribute__ ((packed));

    //hcitool使用
    #define BT_HCI_CMD_LE_SET_SCAN_PARAMETERS 0x200b
    struct bt_hci_cmd_le_set_scan_parameters {
    uint8_t type;
    uint16_t interval;
    uint16_t window;
    uint8_t own_addr_type;
    uint8_t filter_policy;
    } __attribute__ ((packed));