低功耗蓝牙ble开发(二)——bluez5源码分析

3、bluetoothctl工具代码分析

Bluetoothctl工具的入口程序client/main.c中的main函数,现在跳到main函数开始分析

(1)client/main.c/main函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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)

{

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函数分析
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函数分析
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函数分析
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函数分析
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函数分析
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函数分析

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);
     ……
}
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BLE低功耗蓝牙)是一种通过蓝牙无线技术进行低功耗通信的协议。它是在传统蓝牙(Classic Bluetooth)的基础上发展而来,主要用于物联网、智能家居和健康追踪等领域。 BLE主要特点有以下几个方面: 1. 低功耗BLE采用了一种优化的通信方式,使设备在通信过程中的功耗大大降低,从而延长了设备的电池寿命,这对于需要长时间运行的设备非常重要。 2. 简化传输:BLE使用了一种称为GATT(通用属性)的协议,将数据分为服务和特征,通过读、写或订阅操作来传输数据,这种简化了传输过程,减少了额外的开销。 3. 快速连接:BLE的连接速度比传统蓝牙更快,可以在几十毫秒内建立连接,这对于移动设备和传感器等需要快速响应的设备非常重要。 4. 多设备连接:BLE支持同时连接多个设备,可以通过同一个移动设备与多个BLE设备进行通信,提高了系统的灵活性和可扩展性。 Android提供了一套完整的BLE开发API,开发者可以使用这些API来实现BLE通信功能。在Android中,开发BLE应用涉及到四个主要组件:BLE设备扫描、设备连接、数据传输和GATT服务管理。 开发者可以使用Android的BluetoothAdapter类来进行设备扫描和连接操作,可以通过BluetoothGatt类来进行GATT服务的操作,包括读、写、订阅等。 总之,BLE作为一种低功耗蓝牙通信协议,在物联网和智能设备领域应用广泛。在Android平台上进行BLE开发,可以借助Android提供的API,快速实现BLE通信功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值