一知半解的蓝牙

背景:

        最近在项目上遇到一个蓝牙的问题,项目基于嵌入式linux开发,系统上也没有集成bluetoothd服务,移植了一个Bluez的代码,开发了一个BLE的功能,与手机app端进行通信;基本功能没有什么问题,但根据反馈,连接稳定性不好,通常是有干扰,最大的问题就是,有概率在连接断开之后,手机APP再也连接不上,需要重跑嵌入式程序,手机APP才能重新建立连接;

        我也没有接触过蓝牙协议栈的代码,带着问题去分析了一下源代码,也不知道理解得对不对,各位看官如果发现我理解不对的地方,还请告知在下;

分析:

        从开发工程师上了解到,他使用的是开源的Bluez-5.63版本的代码,使用里面的peripheral目录下的例子修改的;我看了一下,peripheral下一共就没有几个文件:

        e6db72ad07b8486f826a3c622abe8e4d.png

        其中, attach.c,通过名称可以大概看出应该是和蓝牙绑定相关的,里面实现了打开串口的操作,还有就是通过串口的fd进行ioctl,对蓝牙的一些标志的设置和复位。

static int open_serial(const char *path);

static int attach_proto(const char *path, unsigned int proto,
						unsigned int flags);

void attach_start(void);

void attach_stop(void);

        efivars.c是efi相关的变量操作,具体是干啥用的我也没搞懂,主要提供了对变量的读取和写入的两个接口;

int efivars_read(const char *name, uint32_t *attributes,
					void *data, size_t size);

int efivars_write(const char *name, uint32_t attributes,
					const void *data, size_t size);

        log.c是类似提供了日志的功能,但是好像代码中也没有用上这个接口,所以这个好像也没什么用;

void log_open(void);
// 打开了/dev/kmsg节点,返回kmsg_fd

void log_close(void);
//关闭kmsg_fd

        剩下的main.c、gap.c和gatt.c是这次分析的主要对象,大部分的功能都是在这几个文件中实现的,我这里根据main的流程去分析。

int main(int argc, char *argv[])
{
	int exit_status;

	if (getpid() == 1 && getppid() == 0)
		is_init = true;

	mainloop_init();

	printf("Bluetooth periperhal ver %s\n", VERSION);

	prepare_filesystem();

	if (is_init) {
		uint8_t addr[6];

		if (efivars_read("BluetoothStaticAddress", NULL,
							addr, 6) < 0) {
			printf("Generating new persistent static address\n");

			if (util_getrandom(addr, sizeof(addr), 0) < 0) {
				perror("Failed to get random static address");
				return EXIT_FAILURE;
			}
			/* Overwrite the MSB to make it a static address */
			addr[5] = 0xc0;

			efivars_write("BluetoothStaticAddress",
					EFIVARS_NON_VOLATILE |
					EFIVARS_BOOTSERVICE_ACCESS |
					EFIVARS_RUNTIME_ACCESS,
					addr, 6);
		}

		gap_set_static_address(addr);

		run_shell();
	}

	log_open();
	gap_start();

	if (is_init)
		attach_start();

	exit_status = mainloop_run_with_signal(signal_callback, NULL);

	if (is_init)
		attach_stop();

	gap_stop();
	log_close();

	return exit_status;
}

        先看看大的流程,main里面调用了mainloop_init进行了初始化,这个mainloop_init没有在main里面定义,这里先不分析。

        prepare_filesystem和efivars_read、efivars_write主要的作用就是看有没有设置BluetoothStaticAddress这个变量,如果有,就读取这个变量的内容来设置一个静态的MAC地址。设置静态MAC地址这里调用的是gap_set_static_address函数进行设置,gap_set_static_address函数是在gap.c里定义的,其实就是把addr赋值给gap.c里的全局变量而已,完全可以自己修改这个全局数组实现。

static uint8_t static_addr[6] = { 0x90, 0x78, 0x56, 0x34, 0x12, 0xc0 };

void gap_set_static_address(uint8_t addr[6])
{
	memcpy(static_addr, addr, sizeof(static_addr));

	printf("Using static address %02x:%02x:%02x:%02x:%02x:%02x\n",
			static_addr[5], static_addr[4], static_addr[3],
			static_addr[2], static_addr[1], static_addr[0]);
}

        然后就是gap_start函数了,之后是mainloop_run_with_signal,看上去是进入主循环了,main函数会在这里阻塞住,直到signal的到来,然后函数返回,执行gap_stop函数。

        重点看一下gap_start函数,这个函数在gap.c中定义

static struct mgmt *mgmt = NULL;

void gap_start(void)
{
	mgmt = mgmt_new_default();
	if (!mgmt) {
		fprintf(stderr, "Failed to open management socket\n");
		return;
	}

	if (!mgmt_send(mgmt, MGMT_OP_READ_COMMANDS,
				MGMT_INDEX_NONE, 0, NULL,
				read_commands_complete, NULL, NULL)) {
		fprintf(stderr, "Failed to read supported commands\n");
		return;
	}
}

        这个函数中,使用mgmt_new_default创建了mgmt,这个mgmt是个全局变量指针,文件中有大量的mgmt_send函数会使用这个指针作为句柄进行消息的发送,看上去像是一个类似socket的东西;

        mgmt_new_default函数在src/shared/mgmt.c中定义

struct mgmt *mgmt_new_default(void)
{
	struct mgmt *mgmt;
	union {
		struct sockaddr common;
		struct sockaddr_hci hci;
	} addr;
	int fd;

	fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
								BTPROTO_HCI);
	if (fd < 0)
		return NULL;

	memset(&addr, 0, sizeof(addr));
	addr.hci.hci_family = AF_BLUETOOTH;
	addr.hci.hci_dev = HCI_DEV_NONE;
	addr.hci.hci_channel = HCI_CHANNEL_CONTROL;

	if (bind(fd, &addr.common, sizeof(addr.hci)) < 0) {
		close(fd);
		return NULL;
	}

	mgmt = mgmt_new(fd);
	if (!mgmt) {
		close(fd);
		return NULL;
	}

	mgmt->close_on_unref = true;

	return mgmt;
}

        从函数中可以看出,mgmt确实和socket有关,它绑定了一个socket的fd句柄,这个socket是使用PF_BLUETOOTH创建的,绑定的地址中,使用了HCI_CHANNEL_CONTROL作为hci的channel,从这些信息中分析,这个mgmt应该就是可蓝牙管理控制相关的句柄。

struct mgmt {
	int ref_count;
	int fd;
	bool close_on_unref;
	struct io *io;
	bool writer_active;
	struct queue *request_queue;
	struct queue *reply_queue;
	struct queue *pending_list;
	struct queue *notify_list;
	unsigned int next_request_id;
	unsigned int next_notify_id;
	bool need_notify_cleanup;
	bool in_notify;
	void *buf;
	uint16_t len;
	uint16_t mtu;
	mgmt_debug_func_t debug_callback;
	mgmt_destroy_func_t debug_destroy;
	void *debug_data;
};

        从mgmt的结构体定义中看到,除了这个fd之外,结构体中一个io的数据,还有几个队列的数据,还有mtu等等的参数。其中这个io是比较关键的数据,可以进一步分析一下这个数据:

	mgmt->io = io_new(fd);
	if (!mgmt->io) {
		free(mgmt->buf);
		free(mgmt);
		return NULL;
	}

        mgmt初始化的过程中,把socket得到的fd进行了一次io_new操作,映射到这个mgmt->io上了,这个io_new函数在bluez中有好几个定义的地方,追查代码发现,应该是对应src/shared/io-mainloop.c中定义的:


struct io *io_new(int fd)
{
	struct io *io;

	if (fd < 0)
		return NULL;

	io = new0(struct io, 1);
	io->fd = fd;
	io->events = 0;
	io->close_on_destroy = false;

	if (mainloop_add_fd(io-&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mr_xiaogui

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值