bluez inquiry 流程梳理--从代码层面理解bluez架构

news2024/12/24 9:58:42

贴一张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);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1263361.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

webpack项目工程初始化

一、初始化项目 默认系统已经安装node //初始化 pnpm init//安装webpack pnpm i -D webpack webpack-cli 新建一个index.html的入口文件 新建一个src文件存放js代码&#xff0c;src里面新建一个index.js package.josn配置打包命令 {"name": "webpack-cs&q…

vue+elementUI的tabs与table表格联动固定与滚动位置

有个变态的需求&#xff0c;要求tabs左侧固定&#xff0c;右侧是表格&#xff0c;点击左侧tab&#xff0c;右侧表格滚动到指定位置&#xff0c;同时&#xff0c;右侧滚动的时候&#xff0c;左侧tab高亮相应的item 上图 右侧的高度非常高&#xff0c;内容非常多 常规的瞄点不适…

零基础可以学编程吗,不懂英语怎么学编程,中文编程工具实例

零基础可以学编程吗&#xff0c;不懂英语怎么学编程&#xff0c;中文编程工具实例 上图是中文编程工具界面、标尺实例。 给大家分享一款中文编程工具&#xff0c;零基础轻松学编程&#xff0c;不需英语基础&#xff0c;编程工具可下载。 这款工具不但可以连接部分硬件&#x…

spring-boot对rabbitMQ的操作

一、安装rabbitMQ 1、直接使用docker拉取镜像 docker pull rabbitmq:3.82、启动容器 docker run \-e RABBITMQ_DEFAULT_USERadmin \-e RABBITMQ_DEFAULT_PASS123456 \-v mq-plugins:/plugins \--name rabbit01 \--hostname rabbit01 --restartalways \-p 15672:15672 \-p 5672:…

在微服务架构中的数据一致性

当从传统的单体应用架构转移到微服务架构时&#xff0c;特别是涉及数据一致性时&#xff0c;数据一致性是微服务架构中最困难的部分。传统的单体应用中&#xff0c;一个共享的关系型数据库负责处理数据一致性。在微服务架构中&#xff0c;如果使用“每个服务一个数据库”的模式…

碧莲盛 x Tapdata:实时数据如何赋能医疗美容行业,助力医疗决策及个性化服务升级

使用 Tapdata&#xff0c;化繁为简&#xff0c;轻量代替 OGG、DSG 这样的同步工具&#xff0c;以及 Kettle、Informatica、Python 这样的 ETL 工具或脚本&#xff0c;帮助企业在五花八门的数据需求面前&#xff0c;实现“做且仅做最后一次 ETL”的目标&#xff0c;这绝非纸上谈…

TIME_WAIT状态TCP连接导致套接字无法重用实验

理论相关知识可以看一下《TIME_WAIT相关知识》。 #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<arpa/inet.h> #include<sys/socket.h> #include<errno.h> #include<syslog.h> #inc…

API接口测试工具的主要作用及选择指南

API接口测试是现代软件开发中至关重要的一环。为了确保不同组件之间的无缝集成和功能正常运作&#xff0c;API接口测试工具应运而生。本文将介绍API接口测试工具的主要作用&#xff0c;以及在选择适合项目的工具时需要考虑的因素。 1、功能测试&#xff1a;API接口测试工具的首…

基于Python+requests编写的自动化测试项目-实现流程化的接口串联

框架产生目的&#xff1a;公司走的是敏捷开发模式&#xff0c;编写这种框架是为了能够满足当前这种发展模式&#xff0c;用于前后端联调之前&#xff08;后端开发完接口&#xff0c;前端还没有将业务处理完毕的时候&#xff09;以及日后回归阶段&#xff0c;方便为自己腾出学(m…

Stable Diffusion绘画系列【3】:二次元动漫画风

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

C语言——求π的近似值

#define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h> #include<math.h> int main() {int s;double n,t,pi;t1;pi0;n1.0;s1;while (fabs(t)>1e-6){pipit; nn2; s-s; ts/n;}pipi*4;printf("pi%lf\n",pi);return 0; }这里是求小数点后6位——1e-6&#…

漏洞环境靶场搭建(内含DVWA SQLi-LABS upload-labs等)

一、docker环境搭建(以redhat8为例&#xff0c;centos7命令基本一致) 1、安装docker yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin 2、启动docker systemctl start docker 3、配置镜像加速 vim /etc/docker/daemon.json {"registr…

Linux下基于MPI的hello程序设计

Linux下基于MPI的hello程序设计 一、MPICH并行计算库安装实验环境部署创建SSH信任连接&#xff0c;实现免密钥互相连接node1安装MPICH 3.4配置NFS注意(一定要先看)环境测试 二、HELLO WORLD并行程序设计 一、MPICH并行计算库安装 在Linux环境下安装MPICH执行环境&#xff0c;配…

Photoshop Elements 2023 v21.0(ps简化版)

Photoshop Elements 2023是一款ps简化版图像处理软件&#xff0c;它加入了一些新的功能和工具&#xff0c;以帮助用户更高效地处理图片。 新功能&#xff1a;软件加入了黑科技&#xff0c;采用Adobe Sensei AI技术&#xff0c;主打人工智能&#xff0c;一键P图&#xff0c;新增…

tabs切换,当点击tabItem时候,改变选中样式,以及content内容区域

效果图展示&#xff1a; html原生代码&#xff1a; <div><div class"buttons-row nav-select riskType" style"padding: 10px;"><div class"shoucang-title-box flex-start"><div class"shoucang-title-item active&q…

Django回顾【二】

目录 一、Web框架 二、WSGI协议 三、 Django框架 1、MVC与MTV模型 2、Django的下载与使用 补充 3、启动django项目 补充 5、 Django请求生命周期 四、路由控制 1、路由是什么&#xff1f; 2、如何使用 3、path详细使用 4、re_path详细使用 5、反向解析 6、路由…

The Sandbox 与 Shemaroo Entertainment 达成合作,将宝莱坞标杆作品带入元宇宙

我们非常高兴地宣布与印度领先的媒体和娱乐集团 Shemaroo Entertainment 建立合作伙伴关系。BharatBox 是全新的文化元宇宙中心&#xff0c;将展示印度娱乐业的知名艺术家和品牌。该项目标志着 Shemaroo 将逐步涉足数字领域&#xff0c;将电影传统与最先进的技术相结合&#xf…

算法之插入排序及希尔排序(C语言版)

我们来实现上述排序 一.插入排序. 当插入第i(i>1)个元素时&#xff0c;前面的array[0],array[1],.,array[i-1]已经排好序&#xff0c;此时用array[i的排序码与array[i-1]array[i-2].的排序码顺序进行比较&#xff0c;找到插入位置即将arrayU插入&#xff0c;原来位置上的元…

【小布_ORACLE】Part11-1--RMAN Backups笔记

Oracle的数据备份于恢复RMAN Backups 学习第11章需要掌握&#xff1a; 一.RMAN的备份类型 二.使用backup命令创建备份集 三.创建备份文件 四.备份归档日志文件 五.使用RMAN的copy命令创建镜像拷贝 文章目录 Oracle的数据备份于恢复RMAN Backups1.RMAN Backup Concepts&#x…

安卓开发学习---kotlin版---笔记(一)

Hello word 前言&#xff1a;上次学习安卓&#xff0c;学了Java开发&#xff0c;简单的搭了几个安卓界面。这次要学习Kotlin语言&#xff0c;然后开发安卓&#xff0c;趁着还年轻&#xff0c;学点新东西&#xff0c;坚持~ 未来的你会感谢现在努力的你~ 主要学习资料&#xff1a…