bluetoothd: source file reading

bluez

程序入口

首先从 /src/main.c 的 main() 函数开始,对于本地定义的函数功能作出解读。

  • 第一部分

    init_defaults() 默认值的初始化操作,主要是给全局变量 btd_opts 设置默认值。其中有我们所关心的一些信息:BlueZ版本、临时设备(Temporary)被清理的定时时长、适配器配对属性(Pairable)和可发现属性(Discoverable)默认有效时长 等等。

  • 第二部分

    解析bluetoothd支持的输入参数,options 结构体变量预定义了一些参数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    // 省略...

    // 创建一个上下文结构体指针变量context
    context = g_option_context_new(NULL);
    // 将options的条目增加到context
    g_option_context_add_main_entries(context, options, NULL);

    // 通过用户输入的参数argv与预定义的命令行参数对比解析
    if (g_option_context_parse(context, &argc, &argv, &err) == FALSE) {
    if (err != NULL) {
    g_printerr("%s\n", err->message);
    g_error_free(err);
    } else
    g_printerr("An unknown error occurred\n");
    exit(1);
    }

    g_option_context_free(context);
    // 省略...

    注意:上述几个函数都是 Glib库中的函数

    1、g_option_context_new

    2、g_option_context_add_main_entries

    3、g_option_context_parse

    4、g_option_context_free

  • 第三部分

    debug和log部分的初始化处理

  • 第四部分

    用户配置文件导入和解析,前提条件:用户运行bluetoothd时通过参数-f指定了配置文件,若用户没有指定配置文件,那么将使用main.conf。

  • 第五部分

    连接dbus daemon,然后向dbus上注册 org.bluez 服务。

  • 第六部分

    adapter_init() 函数用于适配器初始化操作,查找本地蓝牙适配器数量,在适配器上注册所有的事件(event)通知回调函数,这里需要重点关注几个:

    1
    2
    device_found_callback()
    discovering_callback()

    当底层的蓝牙适配器controller发现蓝牙设备通过 device_found_callback() 回调函数通知bluetoothd应用

    当底层的蓝牙适配器controller启动/停止扫描通过 discovering_callback() 回调函数通知bluetoothd应用

    Host和Controller之间通过HCI通信,Host创建socket接口,接着注册 can_read_data() 回调函数,可以读取Controller的Event,当然也可以通过send_request() 发送Command给Controller。

    1
    socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);
  • 第七部分

    device、agent、profile、plugin的初始化

    1
    2
    3
    btd_device_init();
    btd_agent_init();
    btd_profile_init();
  • 第八部分

    注册信号,然后进入glib事件循环

    1
    mainloop_run_with_signal(signal_callback, NULL);

bluetoothd就启动完毕了。

对于bluetoothd源码的解读还有非常的多的疑问,互联网上对于bluez中bluetoothd资料更是凤毛麟角,只能是暂时自己对于bluetoothd源码的一个简单的认识。

MGMT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#define MGMT_OP_START_DISCOVERY		0x0023
struct mgmt_cp_start_discovery {
uint8_t type;
} __packed;

#define MGMT_OP_STOP_DISCOVERY 0x0024
struct mgmt_cp_stop_discovery {
uint8_t type;
} __packed;

#define MGMT_EV_DEVICE_FOUND 0x0012
struct mgmt_ev_device_found {
struct mgmt_addr_info addr;
int8_t rssi;
uint32_t flags;
uint16_t eir_len;
uint8_t eir[0];
} __packed;

#define MGMT_EV_DISCOVERING 0x0013
struct mgmt_ev_discovering {
uint8_t type;
uint8_t discovering;
} __packed;

设备发现

当适配器开始扫描时,发现了附近的蓝牙设备,蓝牙设备被添加到适配器的过程是怎么样的?

当适配器执行扫描时,发现的蓝牙设备通过回调函数传递(bluetoothd在main函数中执行adapter_init函数就注册所有的事件回调函数),以下就是关于设备发现的回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
static void device_found_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
const struct mgmt_ev_device_found *ev = param;
struct btd_adapter *adapter = user_data;
const uint8_t *eir;
uint16_t eir_len;
uint32_t flags;
char addr[18];

if (length < sizeof(*ev)) {
btd_error(adapter->dev_id,
"Too short device found event (%u bytes)", length);
return;
}

eir_len = btohs(ev->eir_len);
if (length != sizeof(*ev) + eir_len) {
btd_error(adapter->dev_id,
"Device found event size mismatch (%u != %zu)",
length, sizeof(*ev) + eir_len);
return;
}

if (eir_len == 0)
eir = NULL;
else
eir = ev->eir;

flags = le32_to_cpu(ev->flags);

ba2str(&ev->addr.bdaddr, addr);
DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
index, addr, ev->rssi, flags, eir_len);

btd_adapter_device_found(adapter, &ev->addr.bdaddr,
ev->addr.type, ev->rssi, flags,
eir, eir_len, false);
}

接着关注这个函数 btd_adapter_device_found,由于这个函数太长,仅放出部分代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
void btd_adapter_device_found(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type, int8_t rssi,
uint32_t flags,
const uint8_t *data, uint8_t data_len,
bool monitoring)
{
// 省略...

// 从适配器下面查找目标设备,若没有该设备,则执行花括号的内容
dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);
if (!dev) {
/* In case of being just a scan response don't attempt to create
* the device.
*/
if (scan_rsp) {
eir_data_free(&eir_data);
return;
}

/* Monitor Devices advertising Broadcast Announcements if the
* adapter is capable of synchronizing to it.
*/
if (eir_get_service_data(&eir_data, BCAA_SERVICE_UUID) &&
btd_adapter_has_settings(adapter,
MGMT_SETTING_ISO_SYNC_RECEIVER))
monitoring = true;

if (!discoverable && !monitoring && not_connectable) {
eir_data_free(&eir_data);
return;
}

dev = adapter_create_device(adapter, bdaddr, bdaddr_type);
}

// 更新定时器
device_update_last_seen(dev, bdaddr_type, !not_connectable);

if (bdaddr_type != BDADDR_BREDR && eir_data.flags &&
!(eir_data.flags & EIR_BREDR_UNSUP)) {
device_set_bredr_support(dev);
/* Update last seen for BR/EDR in case its flag is set */
device_update_last_seen(dev, BDADDR_BREDR, !not_connectable);
}

if (eir_data.name != NULL && eir_data.name_complete)
device_store_cached_name(dev, eir_data.name);

// 省略...
}

device_store_cached_name函数用于在cache目录下创建设备的持久化存储,接下来关注这个函数 adapter_create_device

1
2
3
4
5
6
7
8
9
10
11
12
13
static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
{
struct btd_device *device;

device = device_create(adapter, bdaddr, bdaddr_type);
if (!device)
return NULL;

adapter_add_device(adapter, device);
return device;
}

重点来了,此时看到了函数 device_create 用于创建设备,具体看它的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
struct btd_device *device_create(struct btd_adapter *adapter,
const bdaddr_t *bdaddr, uint8_t bdaddr_type)
{
struct btd_device *device;
char dst[18];
char *str;
const char *storage_dir;

ba2str(bdaddr, dst);
DBG("dst %s", dst);

device = device_new(adapter, dst);
if (device == NULL)
return NULL;

device->bdaddr_type = bdaddr_type;

if (bdaddr_type == BDADDR_BREDR)
device->bredr = true;
else
device->le = true;

storage_dir = btd_adapter_get_storage_dir(adapter);
str = load_cached_name(device, storage_dir, dst);
if (str) {
strcpy(device->name, str);
g_free(str);
}

load_cached_name_resolve(device, storage_dir, dst);

return device;
}

我们知道蓝牙设备被适配器添加后,会注册类似这种对象路径 /org/bluez/hci0/dev_50_64_2B_BF_46_36,这部分内容就在这个函数 device_new()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
static const GDBusMethodTable device_methods[] = {
{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
{ GDBUS_ASYNC_METHOD("ConnectProfile", GDBUS_ARGS({ "UUID", "s" }),
NULL, connect_profile) },
{ GDBUS_ASYNC_METHOD("DisconnectProfile", GDBUS_ARGS({ "UUID", "s" }),
NULL, disconnect_profile) },
{ GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) },
{ GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
{ }
};

static const GDBusPropertyTable device_properties[] = {
{ "Address", "s", dev_property_get_address },
{ "AddressType", "s", property_get_address_type },
{ "Name", "s", dev_property_get_name, NULL, dev_property_exists_name },
{ "Alias", "s", dev_property_get_alias, dev_property_set_alias },
{ "Class", "u", dev_property_get_class, NULL,
dev_property_exists_class },
{ "Appearance", "q", dev_property_get_appearance, NULL,
dev_property_exists_appearance },
{ "Icon", "s", dev_property_get_icon, NULL,
dev_property_exists_icon },
{ "Paired", "b", dev_property_get_paired },
{ "Bonded", "b", dev_property_get_bonded },
{ "Trusted", "b", dev_property_get_trusted, dev_property_set_trusted },
{ "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked },
{ "LegacyPairing", "b", dev_property_get_legacy },
{ "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi },
{ "Connected", "b", dev_property_get_connected },
{ "UUIDs", "as", dev_property_get_uuids },
{ "Modalias", "s", dev_property_get_modalias, NULL,
dev_property_exists_modalias },
{ "Adapter", "o", dev_property_get_adapter },
{ "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data,
NULL, dev_property_manufacturer_data_exist },
{ "ServiceData", "a{sv}", dev_property_get_service_data,
NULL, dev_property_service_data_exist },
{ "TxPower", "n", dev_property_get_tx_power, NULL,
dev_property_exists_tx_power },
{ "ServicesResolved", "b", dev_property_get_svc_resolved, NULL, NULL },
{ "AdvertisingFlags", "ay", dev_property_get_flags, NULL,
dev_property_flags_exist },
{ "AdvertisingData", "a{yv}", dev_property_get_advertising_data,
NULL, dev_property_advertising_data_exist },
{ "WakeAllowed", "b", dev_property_get_wake_allowed,
dev_property_set_wake_allowed,
dev_property_wake_allowed_exist },
{ "Sets", "a{oa{sv}}", dev_property_get_set, NULL,
dev_property_set_exists },
{ }
};

static struct btd_device *device_new(struct btd_adapter *adapter,
const char *address)
{
char *address_up;
struct btd_device *device;
const char *adapter_path = adapter_get_path(adapter);

DBG("address %s", address);

device = g_try_malloc0(sizeof(struct btd_device));
if (device == NULL)
return NULL;

device->tx_power = 127;
device->volume = -1;

device->db = gatt_db_new();
if (!device->db) {
g_free(device);
return NULL;
}

memset(device->ad_flags, INVALID_FLAGS, sizeof(device->ad_flags));

device->ad = bt_ad_new();
if (!device->ad) {
device_free(device);
return NULL;
}

address_up = g_ascii_strup(address, -1);
device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up);
g_strdelimit(device->path, ":", '_');
g_free(address_up);

str2ba(address, &device->bdaddr);

device->client_dbus = btd_gatt_client_new(device);
if (!device->client_dbus) {
error("Failed to create btd_gatt_client");
device_free(device);
return NULL;
}

DBG("Creating device %s", device->path);

if (g_dbus_register_interface(dbus_conn,
device->path, DEVICE_INTERFACE,
device_methods, NULL,
device_properties, device,
device_free) == FALSE) {
error("Unable to register device interface for %s", address);
device_free(device);
return NULL;
}

device->adapter = adapter;
device->sirks = queue_new();
device->temporary = true;

device->db_id = gatt_db_register(device->db, gatt_service_added,
gatt_service_removed, device, NULL);

device->refresh_discovery = btd_opts.refresh_discovery;

return btd_device_ref(device);
}

这个函数 g_dbus_register_interface 为蓝牙设备注册了 org.bluez.Device1 服务

扫描注意事项:

参考来源:Core_amended_2024_06_11_v5.4.pdf

  • 查询扫描类型(inquiry scan type)

    Inquiry_Scan_Type 配置参数指示查询扫描是否使用非隔行扫描或隔行扫描进行。目前,定义了一种强制查询扫描类型和一种可选查询扫描类型。

    image-20241023191822349

  • 查询扫描窗口(inquiry scan window):持续扫描查询的时间,必须小于或等于扫描间隔。取值范围:0x0011 ~ 0x1000,转化为时间单位范围:10.625 ms ~ 2560 ms,默认值:0x0012(11.25ms)

    image-20241008212348333

    注意:一个扫描窗口并不会扫描所有的40个射频通道,完全取决于硬件底层实现。

  • 查询扫描间隔(inquiry scan interval):从上一次扫描查询开始扫下一次扫描查询开始的时间间隔。取值范围:0x0012 ~ 0x1000,转化为时间单位范围:11.25 ms ~ 2560 ms,默认值:0x1000(2560ms)

    Inquiry_Scan_Interval 配置参数定义连续查询扫描之间的时间量。这定义为从 BR/EDR 控制器开始其上一次查询扫描到开始下一次查询扫描的时间间隔

    image-20241008212407775

  • 页扫描间隔(page scan interval):Page_Scan_Interval 配置参数定义连续页面扫描之间的时间量。此时间间隔定义为从控制器开始其上一次页面扫描到开始下一次页面扫描的时间。

    image-20241023193938906

  • 页扫描窗口(page scan window):Page_Scan_Window 配置参数定义页面扫描的持续时间。Page_Scan_Window 只能小于或等于 Page_Scan_Interval。

    image-20241023194040460

扫描窗口和扫描间隔的关系:

其他学习参考BLE设备功耗太高?弄懂这几个参数,功耗想不降都难

扫描参数

在 main.conf 配置文件中涉及到bredr 和 le 的扫描参数。

1
2
3
4
5
6
7
8
9
10
11
12
[BT]
# BR/EDR Inquiry scan activity configuration
#InquiryScanType=
#InquiryScanInterval=
#InquiryScanWindow=

[LE]
# LE scanning parameters used for active scanning supporting discovery
# proceedure
#ScanIntervalDiscovery=
#ScanWindowDiscovery=

在src/main.c 中若用户没有指定配置文件(例如main.conf),那么就会给定一个默认值。

通过读取配置文件,覆盖默认扫描参数:

  • LE扫描参数范围
1
2
3
4
5
6
7
8
9
10
{ "ScanIntervalDiscovery",
&btd_opts.defaults.le.scan_interval_discovery,
sizeof(btd_opts.defaults.le.scan_interval_discovery),
0x0004,
0x4000},
{ "ScanWindowDiscovery",
&btd_opts.defaults.le.scan_win_discovery,
sizeof(btd_opts.defaults.le.scan_win_discovery),
0x0004,
0x4000},
  • BREDR扫描参数范围
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{ "InquiryScanType",
&btd_opts.defaults.br.scan_type,
sizeof(btd_opts.defaults.br.scan_type),
0,
1},
{ "InquiryScanInterval",
&btd_opts.defaults.br.scan_interval,
sizeof(btd_opts.defaults.br.scan_interval),
0x0012,
0x1000},
{ "InquiryScanWindow",
&btd_opts.defaults.br.scan_win,
sizeof(btd_opts.defaults.br.scan_win),
0x0011,
0x1000},