bluez inquiry 流程梳理--从代码层面理解bluez架构
贴一张bluez架构图方便理解
user space
APP:上层应⽤程序
Pluseaudio/pipewire:A2DP的组件
Bluetoothd: 蓝⽛守护进程
Bluez: 包括Bluez tool跟Bluez lib
kernel space
内核代码包含以下⼏部分
driver/bluetooth
net/bluetooth
include/net/bluetooth
bluez——mgmt分析
1,cmd下发
linux系统的bluez的代码是存在与两部分,一部分在kernel,实现协议的一些基本功能,还有一部分在user space实现协议的一些上层功能。
两部分之间的交互通过sockt机制,就是mgmt。
cmd的下发主要调用的是mgmt.c中的mgmt_send()函数
unsigned int mgmt_send(struct mgmt *mgmt, uint16_t opcode, uint16_t index,
uint16_t length, const void *param,
mgmt_request_func_t callback,
void *user_data, mgmt_destroy_func_t destroy)
{
return mgmt_send_timeout(mgmt, opcode, index, length, param, callback,
user_data, destroy, 0);
}
这个函数有8个参数,第一个是mgmt的参数,暂时没找到其定义,第二是比较重要的数值,在mgmt_api和mgmt.h中都有定义和说明,每一个opcode对应一个cmd,在kernel部分也一模一样定义了对应的opcode。
mgmt_send后就是靠opcode是数值对应在kernel中需要调用的对应函数。
第二参数是index
第三个需要传递的参数大小
第四个是需要传递的参数
第五个是传递的回调,用于执行完该cmd后需要回调的数据
第六个是user需要传递的参数,一般未NULL,第六个也是,是预留设计。
2,event上报
除了前面说的cmd下发注册的回调外,kernel部分的event上报一般调用mgmt.c中的mgmt_event()函数来完成,该函数实际是调用的mgmt_send_event()
static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len,
struct sock *skip_sk)
{
return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
HCI_SOCK_TRUSTED, skip_sk);
}
第一个参数无容置疑就是注册的evnet数值,
bluez inquiry 流程
接下来我们通过bluetoothctl工具来分析bluez 的inquiry流程
bluez源码结构
src:核心程序bluetoothd的源码位置。 其中还包括了shared文件夹,该文件夹编译生成了一个共享库,供bluetoothd和其他程序使用(shared库是重点)。
client:bluetoothctl程序的源码位置。
mesh:mesh协议栈的源码位置,但是不包含proxy相关的功能。
tools:hciattach,hciconfig,hcitool等工具的源码目录,如果打开–enable-testing,–enable-test的话,在该目录中,还会有相关的*-tester执行程序。
monitor:btmon工具的源码位置,到目前5.66为止,这个工具已经很强大了,监控生成的hci数据,可以转化为支持ellisys软件打开的.pkt格式,也可以转化为支持wireshare软件打开的格式.cfa格式。
test:测试蓝牙功能的py脚本,通过dbus接口和bluetoothd进行通信。 dbus提供支持python和c语言的接口。
emulator:从字面意思可以推测出它是用来仿真的,通过阅读代码发现它是仿真controller的,通过btdev_create函数来创建一个虚拟的蓝牙controller设备。 kernel也是支持的,"/dev/vhci"这个设备被虚拟为一个虚拟的蓝牙设备。
lib:bluez的一些基础访问库的源码位置,shared库也引用了lib里面的函数定义。 编译生成libbluetooth-internal.la和libbluetooth.la。
gdbus:dbus的源码仓库。(把dbus封装了一下,方便使用)
ell:The Embedded Learning Library,对嵌入式系统的支持,需要在configure时指定使用ell库,方便编译出更少内存和flash的嵌入式平台的bluez的相关固件。
android:早期的android系统使用的是bluez的开源库,后面改为使用bludroid协议栈了。
peripheral:一个ble外围设备的demo,没有通过dbus和bluetoothd进行通信,而是自己实现了一套,使用了shared库的api。 默认不会编译,需要在configure的时候,–enable-test --enable-testing才会编译该目录的源码。
profiles:android平台的协议栈支持的一部分,配合和android目录一起使用。
plugins:貌似是为bluetoothd程序提供一些插件功能。
obexd:经典蓝牙的obex文件传输协议的源码实现。
unit:一些测试shared库的单元测试case,c代码辨析的。
attrib:gatttool工具的实现源码位置。
btio:bt_io_*的相关函数源码位置,bluetooth的io操作的基础库。
inquiry入口
bluetoothctl程序里面打开蓝牙后,输入scan on即可开始扫描。
在cmd_scan里面就会去判断是打开scan还是关闭scan
static void cmd_scan(int argc, char *argv[])
{
dbus_bool_t enable;
const char *method;
const char *mode;
if (!parse_argument(argc, argv, scan_arguments, "Mode", &enable,
&mode))
return bt_shell_noninteractive_quit(EXIT_FAILURE);
if (check_default_ctrl() == FALSE)
return bt_shell_noninteractive_quit(EXIT_FAILURE);
if (enable == TRUE) {
if (!g_strcmp0(mode, "")) {
g_free(filter.transport);
filter.transport = NULL;
filter.set = false;
} else {
g_free(filter.transport);
filter.transport = g_strdup(mode);
filter.set = false;
}
set_discovery_filter(false);
method = "StartDiscovery"; //这里就是dbus总线需要的信息
} else
method = "StopDiscovery";
if (g_dbus_proxy_method_call(default_ctrl->proxy, method,
NULL, start_discovery_reply,
GUINT_TO_POINTER(enable), NULL) == FALSE) { //进入gdbus目录的client.c执行g_dbus_proxy_method_call
bt_shell_printf("Failed to %s discovery\n",
enable == TRUE ? "start" : "stop");
return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
}
这个g_dbus_proxy_method_call方法实际上是进行dbus通信,调用method对应的方法,这里就是StartDiscovery。
gboolean g_dbus_proxy_method_call(GDBusProxy *proxy, const char *method,
GDBusSetupFunction setup,
GDBusReturnFunction function, void *user_data,
GDBusDestroyFunction destroy)
{
struct method_call_data *data;
GDBusClient *client;
DBusMessage *msg;
DBusPendingCall *call;
if (proxy == NULL || method == NULL)
return FALSE;
client = proxy->client;
if (client == NULL)
return FALSE;
msg = dbus_message_new_method_call(client->service_name,
proxy->obj_path, proxy->interface, method); //"org.bluez","/org/bluez/hci0","org.bluez.Adapter1","StartDiscovery" 这里差不多就是这样
if (msg == NULL)
return FALSE;
if (setup) {
DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter);
setup(&iter, user_data);
}
if (!function)
return g_dbus_send_message(client->dbus_conn, msg);
data = g_try_new0(struct method_call_data, 1);
if (data == NULL)
return FALSE;
data->function = function;
data->user_data = user_data;
data->destroy = destroy;
if (g_dbus_send_message_with_reply(client->dbus_conn, msg,
&call, METHOD_CALL_TIMEOUT) == FALSE) {
dbus_message_unref(msg);
g_free(data);
return FALSE;
}
dbus_pending_call_set_notify(call, method_call_reply, data, g_free);
dbus_pending_call_unref(call);
dbus_message_unref(msg);
return TRUE;
}
通过发送的dbus消息,可以知道下一步会进入到src/adapter.c里面去,能看到在src/adapter.c
static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_ASYNC_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
{ GDBUS_METHOD("SetDiscoveryFilter",
GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
set_discovery_filter) },
{ GDBUS_ASYNC_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
{ GDBUS_ASYNC_METHOD("RemoveDevice",
GDBUS_ARGS({ "device", "o" }), NULL, remove_device) },
{ GDBUS_METHOD("GetDiscoveryFilters", NULL,
GDBUS_ARGS({ "filters", "as" }),
get_discovery_filters) },
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ConnectDevice",
GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
connect_device) },
{ }
};//这段代码就可以看到StartDiscovery对应start_discovery
-->
static DBusMessage *start_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct btd_adapter *adapter = user_data;
const char *sender = dbus_message_get_sender(msg);
struct discovery_client *client;
bool is_discovering;
int err;
DBG("sender %s", sender);
if (!btd_adapter_get_powered(adapter))
return btd_error_not_ready(msg);
is_discovering = get_discovery_client(adapter, sender, &client);
/*
* Every client can only start one discovery, if the client
* already started a discovery then return an error.
*/
if (is_discovering)
return btd_error_busy(msg);
/*
* If there was pre-set filter, just reconnect it to discovery_list,
* and trigger scan.
*/
if (client) {
if (client->msg)
return btd_error_busy(msg);
adapter->set_filter_list = g_slist_remove(
adapter->set_filter_list, client);
adapter->discovery_list = g_slist_prepend(
adapter->discovery_list, client);
goto done;
}
client = g_new0(struct discovery_client, 1);
client->adapter = adapter;
client->owner = g_strdup(sender);
client->discovery_filter = NULL;
client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
discovery_disconnect, client,
NULL);
adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
client);
done:
/*
* Just trigger the discovery here. In case an already running
* discovery in idle phase exists, it will be restarted right
* away.
*/
err = update_discovery_filter(adapter); //这里触发发现
if (!err)
return dbus_message_new_method_return(msg);
/* If the discovery has to be started wait it complete to reply */
if (err == -EINPROGRESS) {
client->msg = dbus_message_ref(msg);
adapter->client = client;
return NULL;
}
return btd_error_failed(msg, strerror(-err));
}
-->
static int update_discovery_filter(struct btd_adapter *adapter)
{
struct mgmt_cp_start_service_discovery *sd_cp;
DBG("");
if (discovery_filter_to_mgmt_cp(adapter, &sd_cp)) {
btd_error(adapter->dev_id,
"discovery_filter_to_mgmt_cp returned error");
return -ENOMEM;
}
/* Only attempt to overwrite current discoverable setting when not
* discoverable.
*/
if (!(adapter->current_settings & MGMT_SETTING_DISCOVERABLE)) {
GSList *l;
for (l = adapter->discovery_list; l; l = g_slist_next(l)) {
struct discovery_client *client = l->data;
if (!client->discovery_filter)
continue;
if (client->discovery_filter->discoverable) {
set_discovery_discoverable(adapter, true);
break;
}
}
}
/*
* If filters are equal, then don't update scan, except for when
* starting discovery.
*/
if (filters_equal(adapter->current_discovery_filter, sd_cp) &&
adapter->discovering != 0) {
DBG("filters were equal, deciding to not restart the scan.");
g_free(sd_cp);
return 0;
}
g_free(adapter->current_discovery_filter);
adapter->current_discovery_filter = sd_cp;
trigger_start_discovery(adapter, 0); //进入这个方法
return -EINPROGRESS;
}
-->
static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
{
DBG("");
cancel_passive_scanning(adapter);
if (adapter->discovery_idle_timeout > 0) {
timeout_remove(adapter->discovery_idle_timeout);
adapter->discovery_idle_timeout = 0;
}
/*
* If the controller got powered down in between, then ensure
* that we do not keep trying to restart discovery.
*
* This is safe-guard and should actually never trigger.
*/
if (!btd_adapter_get_powered(adapter))
return;
adapter->discovery_idle_timeout = timeout_add_seconds(delay,
start_discovery_timeout, adapter, NULL); //这里会跳转到start_discovery_timeout执行
}
-->
static bool start_discovery_timeout(gpointer user_data)
{
struct btd_adapter *adapter = user_data;
struct mgmt_cp_start_service_discovery *sd_cp;
uint8_t new_type;
DBG("");
adapter->discovery_idle_timeout = 0;
/* If we're doing filtered discovery, it must be quickly restarted */
adapter->no_scan_restart_delay = !!adapter->current_discovery_filter;
DBG("adapter->current_discovery_filter == %d",
!!adapter->current_discovery_filter);
new_type = get_scan_type(adapter);
if (adapter->discovery_enable == 0x01) {
struct mgmt_cp_stop_discovery cp;
/*
* If we're asked to start regular discovery, and there is an
* already running regular discovery and it has the same type,
* then just keep it.
*/
if (!adapter->current_discovery_filter &&
!adapter->filtered_discovery &&
adapter->discovery_type == new_type) {
if (adapter->discovering)
return FALSE;
adapter->discovering = true;
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discovering");
return FALSE;
}
/*
* Otherwise the current discovery must be stopped. So
* queue up a stop discovery command.
*
* This can happen if a passive scanning for Low Energy
* devices is ongoing, or scan type is changed between
* regular and filtered, or filter was updated.
*/
cp.type = adapter->discovery_type;
mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
NULL, NULL, NULL);
/* Don't even bother to try to quickly start discovery
* just after stopping it, it would fail with status
* MGMT_BUSY. Instead discovering_callback will take
* care of that.
*/
return FALSE;
}
/* Regular discovery is required */
if (!adapter->current_discovery_filter) {
struct mgmt_cp_start_discovery cp;
cp.type = new_type;
mgmt_send(adapter->mgmt,
,
adapter->dev_id, sizeof(cp), &cp,
start_discovery_complete, adapter,
NULL);
return FALSE;
}
/* Filtered discovery is required */
sd_cp = adapter->current_discovery_filter;
DBG("sending MGMT_OP_START_SERVICE_DISCOVERY %d, %d, %d",
sd_cp->rssi, sd_cp->type,
btohs(sd_cp->uuid_count));
mgmt_send(adapter->mgmt, MGMT_OP_START_SERVICE_DISCOVERY,
adapter->dev_id, sizeof(*sd_cp) +
btohs(sd_cp->uuid_count) * 16,
sd_cp, start_discovery_complete, adapter, NULL);
return FALSE;
}
上面的函数里面就可以看到mgmt_send了,这个函数会通过io下发命令到内核层
kernel层
mgmt_handlers
–> start_discovery()
--> start_discovery_internal()
--> generic_cmd_complete()
--> mgmt_cmd_complete()
--> hci_send_to_channel()
static struct hci_mgmt_chan chan = {
.channel = HCI_CHANNEL_CONTROL,
.handler_count = ARRAY_SIZE(mgmt_handlers),
.handlers = mgmt_handlers,
.hdev_init = mgmt_init_hdev,
}; //mgmt_handlers这个对应的就是对于上层传递的处理
int mgmt_init(void)
{mgmt_chan
return hci_mgmt_chan_register(&chan); //注册mgmt_chan
}
//通过查看mgmt_handlers可以知道会调用到那个方法
{ start_discovery, MGMT_START_DISCOVERY_SIZE },
{ stop_discovery, MGMT_STOP_DISCOVERY_SIZE },
static int start_discovery(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
return start_discovery_internal(sk, hdev, MGMT_OP_START_DISCOVERY,
data, len);
}
static int start_discovery_internal(struct sock *sk, struct hci_dev *hdev,
u16 op, void *data, u16 len)
{
struct mgmt_cp_start_discovery *cp = data;
struct mgmt_pending_cmd *cmd;
u8 status;
int err;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
if (!hdev_is_powered(hdev)) {
err = mgmt_cmd_complete(sk, hdev->id, op,
MGMT_STATUS_NOT_POWERED,
&cp->type, sizeof(cp->type));
goto failed;
}
if (hdev->discovery.state != DISCOVERY_STOPPED ||
hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) {
err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY,
&cp->type, sizeof(cp->type));
goto failed;
}
if (!discovery_type_is_valid(hdev, cp->type, &status)) {
err = mgmt_cmd_complete(sk, hdev->id, op, status,
&cp->type, sizeof(cp->type));
goto failed;
}
/* Clear the discovery filter first to free any previously
* allocated memory for the UUID list.
*/
hci_discovery_filter_clear(hdev);
hdev->discovery.type = cp->type;
hdev->discovery.report_invalid_rssi = false;
if (op == MGMT_OP_START_LIMITED_DISCOVERY)
hdev->discovery.limited = true;
else
hdev->discovery.limited = false;
cmd = mgmt_pending_add(sk, op, hdev, data, len); //这里添加到列表
if (!cmd) {
err = -ENOMEM;
goto failed;
}
cmd->cmd_complete = generic_cmd_complete; //
hci_discovery_set_state(hdev, DISCOVERY_STARTING);
queue_work(hdev->req_workqueue, &hdev->discov_update);
err = 0;
failed:
hci_dev_unlock(hdev);
return err;
}
static int generic_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status)
{
return mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
cmd->param, cmd->param_len);
}
//mgmt_util.c
int mgmt_cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status,
void *rp, size_t rp_len)
{
struct sk_buff *skb, *mskb;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *ev;
int err;
BT_DBG("sock %p", sk);
skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
hdr->index = cpu_to_le16(index);
hdr->len = cpu_to_le16(sizeof(*ev) + rp_len);
ev = skb_put(skb, sizeof(*ev) + rp_len);
ev->opcode = cpu_to_le16(cmd);
ev->status = status;
if (rp)
memcpy(ev->data, rp, rp_len);
mskb = create_monitor_ctrl_event(hdr->index, hci_sock_get_cookie(sk),
MGMT_EV_CMD_COMPLETE,
sizeof(*ev) + rp_len, ev);
if (mskb)
skb->tstamp = mskb->tstamp;
else
__net_timestamp(skb);
err = sock_queue_rcv_skb(sk, skb);
if (err < 0)
kfree_skb(skb);
if (mskb) {
hci_send_to_channel(HCI_CHANNEL_MONITOR, mskb,
HCI_SOCK_TRUSTED, NULL);
kfree_skb(mskb);
}
return err;
}
event事件上报
hci_inquiry_result_evt()
–> mgmt_device_found()
--> mgmt_event()
--> mgmt_send_event()
--> hci_send_to_channel()
--> __hci_send_to_channel()
//hci_event.c hci_event_packet()
case HCI_EV_INQUIRY_RESULT:
hci_inquiry_result_evt(hdev, skb);
break;
static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct inquiry_data data;
struct inquiry_info *info = (void *) (skb->data + 1);
int num_rsp = *((__u8 *) skb->data);
BT_DBG("%s num_rsp %d", hdev->name, num_rsp);
if (!num_rsp || skb->len < num_rsp * sizeof(*info) + 1)
return;
if (hci_dev_test_flag(hdev, HCI_PERIODIC_INQ))
return;
hci_dev_lock(hdev);
for (; num_rsp; num_rsp--, info++) {
u32 flags;
bacpy(&data.bdaddr, &info->bdaddr);
data.pscan_rep_mode = info->pscan_rep_mode;
data.pscan_period_mode = info->pscan_period_mode;
data.pscan_mode = info->pscan_mode;
memcpy(data.dev_class, info->dev_class, 3);
data.clock_offset = info->clock_offset;
data.rssi = HCI_RSSI_INVALID;
data.ssp_mode = 0x00;
flags = hci_inquiry_cache_update(hdev, &data, false);
mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00,
info->dev_class, HCI_RSSI_INVALID,
flags, NULL, 0, NULL, 0);
}
hci_dev_unlock(hdev);
}
//mgmt.c
void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 addr_type, u8 *dev_class, s8 rssi, u32 flags,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
{
char buf[512];
struct mgmt_ev_device_found *ev = (void *)buf;
size_t ev_size;
/* Don't send events for a non-kernel initiated discovery. With
* LE one exception is if we have pend_le_reports > 0 in which
* case we're doing passive scanning and want these events.
*/
if (!hci_discovery_active(hdev)) {
if (link_type == ACL_LINK)
return;
if (link_type == LE_LINK && list_empty(&hdev->pend_le_reports))
return;
}
if (hdev->discovery.result_filtering) {
/* We are using service discovery */
if (!is_filter_match(hdev, rssi, eir, eir_len, scan_rsp,
scan_rsp_len))
return;
}
if (hdev->discovery.limited) {
/* Check for limited discoverable bit */
if (dev_class) {
if (!(dev_class[1] & 0x20))
return;
} else {
u8 *flags = eir_get_data(eir, eir_len, EIR_FLAGS, NULL);
if (!flags || !(flags[0] & LE_AD_LIMITED))
return;
}
}
/* Make sure that the buffer is big enough. The 5 extra bytes
* are for the potential CoD field.
*/
if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf))
return;
memset(buf, 0, sizeof(buf));
/* In case of device discovery with BR/EDR devices (pre 1.2), the
* RSSI value was reported as 0 when not available. This behavior
* is kept when using device discovery. This is required for full
* backwards compatibility with the API.
*
* However when using service discovery, the value 127 will be
* returned when the RSSI is not available.
*/
if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi &&
link_type == ACL_LINK)
rssi = 0;
bacpy(&ev->addr.bdaddr, bdaddr);
ev->addr.type = link_to_bdaddr(link_type, addr_type);
ev->rssi = rssi;
ev->flags = cpu_to_le32(flags);
if (eir_len > 0)
/* Copy EIR or advertising data into event */
memcpy(ev->eir, eir, eir_len);
if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
NULL))
eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV,
dev_class, 3);
if (scan_rsp_len > 0)
/* Append scan response data to event */
memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len);
ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
ev_size = sizeof(*ev) + eir_len + scan_rsp_len;
mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
}
static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len,
struct sock *skip_sk)
{
return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len,
HCI_SOCK_TRUSTED, skip_sk);
}
//mgmt_util.c
int mgmt_send_event(u16 event, struct hci_dev *hdev, unsigned short channel,
void *data, u16 data_len, int flag, struct sock *skip_sk)
{
struct sk_buff *skb;
struct mgmt_hdr *hdr;
skb = alloc_skb(sizeof(*hdr) + data_len, GFP_KERNEL);
if (!skb)
return -ENOMEM;
hdr = skb_put(skb, sizeof(*hdr));
hdr->opcode = cpu_to_le16(event);
if (hdev)
hdr->index = cpu_to_le16(hdev->id);
else
hdr->index = cpu_to_le16(MGMT_INDEX_NONE);
hdr->len = cpu_to_le16(data_len);
if (data)
skb_put_data(skb, data, data_len);
/* Time stamp */
__net_timestamp(skb);
hci_send_to_channel(channel, skb, flag, skip_sk);
if (channel == HCI_CHANNEL_CONTROL)
hci_send_monitor_ctrl_event(hdev, event, data, data_len,
skb_get_ktime(skb), flag, skip_sk);
kfree_skb(skb);
return 0;
}
--> hci_send_to_channel(channel, skb, flag, skip_sk);
void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
int flag, struct sock *skip_sk)
{
read_lock(&hci_sk_list.lock);
__hci_send_to_channel(channel, skb, flag, skip_sk);
read_unlock(&hci_sk_list.lock);
}
/* Send frame to sockets with specific channel */
static void __hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
int flag, struct sock *skip_sk)
{
struct sock *sk;
BT_DBG("channel %u len %d", channel, skb->len);
sk_for_each(sk, &hci_sk_list.head) {
struct sk_buff *nskb;
/* Ignore socket without the flag set */
if (!hci_sock_test_flag(sk, flag))
continue;
/* Skip the original socket */
if (sk == skip_sk)
continue;
if (sk->sk_state != BT_BOUND)
continue;
if (hci_pi(sk)->channel != channel)
continue;
nskb = skb_clone(skb, GFP_ATOMIC);
if (!nskb)
continue;
if (sock_queue_rcv_skb(sk, nskb))
kfree_skb(nskb);
}
}
user space 处理event事件
can_read_data
–> process_notify()
--> device_found_callback()
--> btd_adapter_device_found() -->dbus
struct mgmt *mgmt_new(int fd)
{
struct mgmt *mgmt;
...
if (!io_set_read_handler(mgmt->io, can_read_data, mgmt, NULL)) {
queue_destroy(mgmt->notify_list, NULL);
queue_destroy(mgmt->pending_list, NULL);
queue_destroy(mgmt->reply_queue, NULL);
queue_destroy(mgmt->request_queue, NULL);
io_destroy(mgmt->io);
free(mgmt->buf);
free(mgmt);
return NULL;
}
...
return mgmt_ref(mgmt);
}
static bool can_read_data(struct io *io, void *user_data) //这个方法在mgmt_new里面注册
{
struct mgmt *mgmt = user_data;
struct mgmt_hdr *hdr;
struct mgmt_ev_cmd_complete *cc;
struct mgmt_ev_cmd_status *cs;
ssize_t bytes_read;
uint16_t opcode, event, index, length;
bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len);
if (bytes_read < 0)
return false;
if (bytes_read < MGMT_HDR_SIZE)
return true;
hdr = mgmt->buf;
event = btohs(hdr->opcode);
index = btohs(hdr->index);
length = btohs(hdr->len);
if (bytes_read < length + MGMT_HDR_SIZE)
return true;
mgmt_ref(mgmt);
switch (event) {
case MGMT_EV_CMD_COMPLETE:
cc = mgmt->buf + MGMT_HDR_SIZE;
opcode = btohs(cc->opcode);
DBG(mgmt, "[0x%04x] command 0x%04x complete: 0x%02x",
index, opcode, cc->status);
request_complete(mgmt, cc->status, opcode, index, length - 3,
mgmt->buf + MGMT_HDR_SIZE + 3);
break;
case MGMT_EV_CMD_STATUS:
cs = mgmt->buf + MGMT_HDR_SIZE;
opcode = btohs(cs->opcode);
DBG(mgmt, "[0x%04x] command 0x%02x status: 0x%02x",
index, opcode, cs->status);
request_complete(mgmt, cs->status, opcode, index, 0, NULL);
break;
default:
DBG(mgmt, "[0x%04x] event 0x%04x", index, event);
process_notify(mgmt, event, index, length,
mgmt->buf + MGMT_HDR_SIZE);
break;
}
mgmt_unref(mgmt);
return true;
}
static void process_notify(struct mgmt *mgmt, uint16_t event, uint16_t index,
uint16_t length, const void *param)
{
struct event_index match = { .event = event, .index = index,
.length = length, .param = param };
mgmt->in_notify = true;
queue_foreach(mgmt->notify_list, notify_handler, &match); //通过notify_handler到达对应的方法
mgmt->in_notify = false;
if (mgmt->need_notify_cleanup) {
queue_remove_all(mgmt->notify_list, match_notify_removed,
NULL, destroy_notify);
mgmt->need_notify_cleanup = false;
}
}
mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_FOUND,
adapter->dev_id,
device_found_callback,
adapter, NULL);
//adapter.c
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);
}
// client/mgmt.c
static void device_found(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_ev_device_found *ev = param;
struct mgmt *mgmt = user_data;
uint16_t eir_len;
uint32_t flags;
if (len < sizeof(*ev)) {
error("Too short device_found length (%u bytes)", len);
return;
}
flags = btohl(ev->flags);
eir_len = get_le16(&ev->eir_len);
if (len != sizeof(*ev) + eir_len) {
error("dev_found: expected %zu bytes, got %u bytes",
sizeof(*ev) + eir_len, len);
return;
}
if (discovery) {
char addr[18], *name;
ba2str(&ev->addr.bdaddr, addr);
print("hci%u dev_found: %s type %s rssi %d "
"flags 0x%04x ", index, addr,
typestr(ev->addr.type), ev->rssi, flags);
if (ev->addr.type != BDADDR_BREDR)
print("AD flags 0x%02x ",
eir_get_flags(ev->eir, eir_len));
name = eir_get_name(ev->eir, eir_len);
if (name)
print("name %s", name);
else
print("eir_len %u", eir_len);
free(name);
}
if (discovery && (flags & MGMT_DEV_FOUND_CONFIRM_NAME)) {
struct mgmt_cp_confirm_name cp;
memset(&cp, 0, sizeof(cp));
memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
if (resolve_names)
cp.name_known = 0;
else
cp.name_known = 1;
mgmt_reply(mgmt, MGMT_OP_CONFIRM_NAME, index, sizeof(cp), &cp,
confirm_name_rsp, NULL, NULL);
}
}