3、bluetoothctl工具代码分析
Bluetoothctl工具的入口程序client/main.c中的main函数,现在跳到main函数开始分析
(1)client/main.c/main函数分析
c
int main(int argc, char *argv[])
{
......
//命令行输入初始化,该函数里面调用rl_init函数,这是linux的命令行功能,里面的rl_handler函数在键盘输入字符是会被调用,从而解析出命令
bt_shell_init(argc, argv, &opt);
......
//向dbus系统总线注册一个连接
dbus_conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
//绑定管理对象,这个好像没什么用
g_dbus_attach_object_manager(dbus_conn);
//申请一个client结构体存储空间,并注册接收其它dbus总线发来的信号的回调函数,g_dbus_client_new里面调用了g_dbus_client_new_full函数,后面直接分析该函数
client = g_dbus_client_new(dbus_conn, "org.bluez", "/org/bluez");
......
//注册代理,bluez核心代码里向dbus系统总线注册了多个对象,例如"/"、" /org/bluez/hci%d"对象等,每个对象里有多个接口,例如" /org/bluez/hci%d"对象里提供了名字为" org.bluez.Adapter1"的接口,该接口下面有"StartDiscovery"方法等。每个接口放在一个代理存储空间里,代理里存放了接口的名字,接口有哪些方法,有哪些属性,接口的对象路径地址等,这样便于程序通过代理链表查找。该函数里面调用了get_managed_objects函数,后面直接分析该函数
g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
property_changed, NULL);
......
}
(2)client/client.c/g_dbus_client_new_full函数分析
c
GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
const char *service,
const char *path,
const char *root_path)
{
......
//dbus总线的注册方法,当其它进程广播dbus信号时,message_filter函数会被调用
if (dbus_connection_add_filter(connection, message_filter,
client, NULL) == FALSE) {
......
}
(3)client/client.c/message_filter函数分析
c
static DBusHandlerResult message_filter(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
......
//用g_dbus_client_set_signal_watch函数注册的信号的回调函数会被调用
client->signal_func(connection, message, client->signal_data);
}
(4)client/client.c/get_managed_objects函数分析
c
static void get_managed_objects(GDBusClient *client)
{
......
//使用dbus库函数向bluez核心代码发送获取所有被管理的对象。调用时传入的参数是bluez核心进程dbus连接名称""org.bluez",对象名称是"/",接口方法是DBUS_INTERFACE_OBJECT_MANAGER的宏定义名称,接口方法的名字是"GetManagedObjects"。我们在bluez5.50/src/mian.c/main函数中调用connect_dbus->g_dbus_attach_object_manager->add_interface(data, DBUS_INTERFACE_OBJECT_MANAGER,manager_methods, manager_signals,NULL, data, NULL);在这个函数中注册了manager_methods方法。当bluez核心进程收到当前进程的GetManagedObjects方法时,manager_methods方法列表中的get_objects函数会被调用
msg = dbus_message_new_method_call(client->service_name,
client->root_path,
DBUS_INTERFACE_OBJECT_MANAGER,
"GetManagedObjects");
......
//当收到bluez核心进程的应答消息是改注册函数里的get_managed_objects_reply方法会被调用
dbus_pending_call_set_notify(client->get_objects_call,
get_managed_objects_reply,
client, NULL);
}
(5)gdbus/object.c/get_objects函数分析
c
static DBusMessage *get_objects(DBusConnection *connection,
DBusMessage *message, void *user_data)
{
......
//搜索data->objects链表上所有的dbus对象,对每个对象调用append_object回调函数处理。例如创建的根目录对象"/"和蓝牙适配器对象"/org/bluez/hci%d"都添加在data->objects链表上。data->objects链表中的数据是注册dbus对象时调用invalidate_parent_data函数添加到链表上的。append_object函数里面调用append_interfaces函数,接着调用append_interface,接着调用append_properties,接着调用append_property
g_slist_foreach(data->objects, append_object, &array);
}
(6)gdbus/object.c/append_property函数分析
c
static void append_property(struct interface_data *iface,
const GDBusPropertyTable *p, DBusMessageIter *dict)
{
......
//适配器注册dbus时添加了adapter_properties,所以p->get会逐个调用adapter_properties列表里的property_get_address函数,一直调用到property_get_modalias函数,也就是适配器的属性信息都将被读取并返回给用户。当扫描到蓝牙设备后,蓝牙设备注册到dbus里的device_properties列表下的get函数也会被调用
p->get(p, &value, iface->user_data);
......
}
(7)gdbus/client.c/get_managed_objects_reply函数分析
c
static void get_managed_objects_reply(DBusPendingCall *call, void *user_data)
{
......
//解析所有的管理对象及对象下所有的接口,给每个对象申请一个代理。该函数里调用parse_managed_objects->parse_interfaces->parse_properties
parse_managed_objects(client, reply);
......
//获取每个接口下的所有属性
refresh_properties(client->proxy_list);
......
}
(8)gdbus/client.c/parse_properties函数分析
c
static void parse_properties(GDBusClient *client, const char *path,
const char *interface, DBusMessageIter *iter)
{
......
//查看proxy_list链表里有没有名字为interface的接口代理,初始化时默认是没有的
proxy = g_dbus_proxy_lookup(client->proxy_list, NULL,
path, interface);
......
//申请接口代理,并把代理添加到proxy_list链表上
proxy = proxy_new(client, path, interface);
......
//获取每个接口的属性信息,并把第一个适配器接口设置为默认适配器,第一个扫描到的设备作为默认设备
update_properties(proxy, iter, FALSE);
//根据接口类型把接口代理进行分类。例如接口是适配器接口,就把接口代理添加到适配器链表,设备接口就把接口代理添加到扫描它的适配器链表下的设备链表上。该函数里调用client->proxy_added(proxy, client->user_data),又client->proxy_added=proxy_added
proxy_added(client, proxy);
}
(9)client/main.c/proxy_added函数分析
c
static void proxy_added(GDBusProxy *proxy, void *user_data)
{
......
//获取代理的接口名
interface = g_dbus_proxy_get_interface(proxy);
......
//接口名字是"org.bluez.Device1"时,把该代理添加到扫描它的适配器的设备链表上
device_added(proxy);
......
//接口名字是"org.bluez.Adapter1"时,把代理添加到适配器链表
adapter_added(proxy);
}
4、bluetoothctl工具部分命令实现分析
(1)client/main.c/ cmd_list函数分析
c
static void cmd_list(int argc, char *argv[])
{
......
// 打印所有的适配器地址,ctrl_list链表存放所有适配器代理,在adapter_new函数中把适配器加入ctrl_list链表
for (list = g_list_first(ctrl_list); list; list = g_list_next(list))
}
(2)client/main.c/ cmd_scan函数分析
c
static void cmd_scan(int argc, char *argv[])
{
......
//发送设置扫描过滤消息给bluez核心进程,主要设置扫描到有效设备的信号强度阈值等
set_discovery_filter();
......
//发送启动扫描设备消息给bluez核心进程,bluez核心进程会调用bluez5.50/src/adapter.c/start_discovery函数,当调用收到响应消息后start_discovery_reply函数会被调用
if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
NULL, start_discovery_reply,
GUINT_TO_POINTER(enable), NULL) == FALSE)
}
(3)src/adapter.c/start_discovery函数分析
static DBusMessage *start_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
c
static DBusMessage *start_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
......
//触发开始扫描设备,设置扫描超时时间,该函数里调用trigger_start_discovery函数,当超时时间到了会调用start_discovery_timeout函数
err = update_discovery_filter(adapter);
......
}
(4)src/adapter.c/start_discovery_timeout函数分析
c
static gboolean start_discovery_timeout(gpointer user_data)
{
......
//调用hci接口发送启动扫描命令给指定蓝牙适配器,当适配器启动扫描设备时,会通过hci接口发送响应命令,这时bluez5.50/src/mgmt.c/can_read_data被调用,里面调用request_complete,里面调用request->callback,而start_discovery_complete函数就注册在request->callback里。start_discovery_complete-> g_dbus_emit_property_changed-> g_dbus_emit_property_changed_full-> add_pending。该函数里注册了 process_changes函数,在主循环空闲时,process_changes函数被调用。process_changes函数里调用emit_interfaces_added函数,里面调用dbus_message_new_signal(root->path,DBUS_INTERFACE_OBJECT_MANAGER,"InterfacesAdded");这个函数把增加的接口通过dbus信号广播给bluetoothctl进程。
mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
start_discovery_complete, adapter, NULL);
}
(5)src/adapter.c/ device_found_callback函数分析
c
static void device_found_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
......
//启动扫描设备后,当适配器扫描到设备后,驱动程序会通过hci接口发设备信息响应包给bluez核心进程。Bluez核心进程里can_read_data会被触发,最后调用到device_found_callback函数。该函数目的是为扫描到的设备在dbus总线上添加一个设备对象,并把该设备所有的信息绑定到给设备对象上,方便其它进程通过dbus消息获取设备信息
update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
ev->rssi, confirm_name, legacy,
flags & MGMT_DEV_FOUND_NOT_CONNECTABLE,
eir, eir_len);
}
(6)src/adapter.c/ update_found_devices函数分析
c
static void update_found_devices(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type, int8_t rssi,
bool confirm, bool legacy,
bool not_connectable,
const uint8_t *data, uint8_t data_len)
{
......
//查看当前适配器的设备链表adapter->devices上有没有设备地址是bdaddr的设备存在,默认是不存在该设备的
dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);
......
//把新设备作为一个新的dbus对象添加到dbus总线上
dev = adapter_create_device(adapter, bdaddr, bdaddr_type);
......
}
(7)src/adapter.c/ adapter_create_device函数分析
c
static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
{
......
//创建新的设备dbus对象,device_create-> device_new
device = device_create(adapter, bdaddr, bdaddr_type);
......
//把设备添加到适配器的设备链表adapter->devices上
adapter->devices = g_slist_append(adapter->devices, device);
}
(8)src/device.c/ device_new函数分析
c
static struct btd_device *device_new(struct btd_adapter *adapter,
const char *address)
{
......
//给设备赋值一个dbus对象的路径
device->path = g_strdup_printf("%s/dev_%s", adapter_path, address_up);
......
//给设备注册dbus对象,对象路径是device->path,对象下面注册一个接口名为DEVICE_INTERFACE,接口提供了方法集合device_methods,属性集合device_properties
if (g_dbus_register_interface(dbus_conn,
device->path, DEVICE_INTERFACE,
device_methods, NULL,
device_properties, device,
device_free) == FALSE)
......
}
(9)bluez5.50/gdbus/client.c/ g_dbus_client_new_full函数分析
c
GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
const char *service,
const char *path,
const char *root_path)
{
......
//在main初始化过程中在这给函数中注册了一个接口增加回调函数,当bluez核心进程发送"InterfacesAdded"该信号时,bluetoothctl进程会触发调用interfaces_added函数。interfaces_added-> parse_interfaces-> parse_properties-> proxy_added,最后把新增的接口根据类型添加到适配器列表、设备列表或其他列表
client->added_watch = g_dbus_add_signal_watch(connection, service,
client->root_path,
DBUS_INTERFACE_OBJECT_MANAGER,
"InterfacesAdded",
interfaces_added,
client, NULL);
......
}