蓝牙协议栈自定义的数据结构,例如:alloctor(内存分配工具)、hashmap、list、array、fixed_queue、config、buffer、socket、thread、alarm、eager_reader、wacklock、samphore等,保存在**system\bt\osi**路径下。

名词解释
/*
HAL :HardwareAbstraction Layer

Btif :Bluetoothinterface

Bta :Bluetoothapplication

Btu :Bluetoothuper layer

Bte :Bluetoothembedded layer

Btm :Bluetooth devicemanager

CO : callout\CI: call in

HF : HandsfreeProfile

HH :HID HostProfile

HL :HealthDevice Profile

AV :audio\vidio

Ag :audiogateway

Ar :audio/videoregistration

Gattc :GATT client

Gatts :GATT server

BLE :Bluetooth Low Energy
*/


Thread.c

线程数据结构贯穿了整个协议栈,目前可以发现几个比较重要的线程,management_thread(协议栈管理线程)、bt_jni_workqueue_thread(jni工作线程)、bt_workqueue_thread(蓝牙协议栈中心工作线程)、hci_thread(hci层工作线程)等。

蓝牙协议栈自定义线程的数据结构如下:

struct thread_t{

bool is_joined; //是否正在被等待结束

pthread_t pthread; //线程实例

pid_t tid; //线程ID

char name[THREAD_NAME_MAX + 1]; //线程名

reactor_t *reactor; //用于工作队列在线程中注册反应堆

fixed_queue_t *work_queue; //工作线程

};


线程工具数据结构方法介绍:

  1. thread_new_sized为线程结构体分配内存空间,
  • 参数:(const char *name,size_t work_queue_capacity),name指线程名,work_queue_capacity代表工作队列的长度。
  • 返回值:thread_t * 即返回线程结构体指针。
  1. thread_free 终止并回收线程
  2. thread_post 向线程工作队列发送消息。
  • 参数:(thread_t *thread,thread_fn func, void *context)
  • Func代表消息执行函数,context代表消息执行参数,两者组成一个work_item_t结构体并放置到线程工作队列中,等待执行。
  1. thread_stop 终止线程
  2. run_thread 线程执行函数,在执行时注册并启动了reactor反应堆,进入工作队列中的消息会通过reactor反应堆分发出去。
  3. work_queue_read_cb工作队列消息执行回调,每进去一个消息会通过该回调将消息从工作队列中退出并执行该消息。


Reactor.c

使用I/O多路复用epoll模型构建的消息反应堆。

Epoll 相关函数如下:

int epoll_create(int size); 创建epoll监听fd,此处fd是一个fd数组,fd可能是一个socket端口,文件输入输出流,eventfd等I/O流,协议栈中使用的是eventfd。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);添加或者删除一个fd到监听fd数组中。

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);监听fd数组的I/O变化并提醒。

Eventfd是一个事件通知,可以创建一个fd,阻塞监听read,以及写入write数据到fd。

比如说当一个队列有消息入队时,使用eventfd写入1到fd中,并且此时fd已经被epoll监听,这样就实现了epoll监听消息入队并将消息分发执行。事实上协议栈中的Reactor消息反应堆模型就是这样搭建的。任何一个固定队列(Fixed_queue)都包含了一个出队fd(dequeue_sem),当有消息入队,会通过向fd中写入数据来通知epoll。任何一个固定队列都可以注册到一个线程的Reactor中,返回一个reactor_object_t结构体。



Reactor结构体如下:

struct reactor_t{

int epoll_fd; //该反应堆监听的epoll fd数组

int event_fd; //控制reactor终止的fd

pthread_mutex_t list_lock; //无效队列锁

list_t *invalidation_list; //当前无效的reactor_object队列

pthread_t run_thread; //reactor依赖的线程

bool is_running; //标志线程函数是否在执行

bool object_removed; //是否有reactor_object被移除

};


Reactor_object_t结构体如下:

struct reactor_object_t {

int fd; //需要监听的fd,比如队列出队fd。

void *context; //消息回调函数的上下文,比如队列

reactor_t *reactor; //该reactor object所注册到的reactor

pthread_mutex_t lock; //reactorobject锁

void (*read_ready)(void *context); //当fd可读时的消息回调,比如队列有消息入队

void (*write_ready)(void *context); //当fd可写时的消息回调

};


Reactor结构体方法介绍如下:

  1. reactor_new 为reactor分配内存空间,并初始化结构体,包括创建一个epoll,并创建event_fd用于控制reactor的终止。
  2. reactor_register为注册一个新的reactor object到reactor中。
  • 参数:(reactor_t*reactor,int fd, void context, void (read_ready)(void context), void (write_ready)(void *context)) reactor代表reactorobject要注册的reactor,fd代表要监听的fd,context代表回调函数的上下文,read_ready代表fd可读时的回调,write_ready代表可写时的回调。
  1. reactor_unregister为从reactor中注销一个reactor object。
  2. reactor_start代表开始执行reactor。
  3. run_reactor代表reactor运行函数,在该函数中会进行循环epoll_wait监听所有的fd并分发消息,当收Reactor 的event_fd时终止该循环。


Fixed_queue.c

由列表(List)数据结构实现的可以注册出队消息信号到reactor的固定队列。

fixed_queue_t结构体如下:

typedef struct fixed_queue_t {

list_t *list; //列表

semaphore_t *enqueue_sem; //入队信号量

semaphore_t *dequeue_sem; //出队信号量

pthread_mutex_t lock; //队列锁

size_t capacity; //容量

reactor_object_t *dequeue_object;//注册出队信号的reactorobject结构体

fixed_queue_cb dequeue_ready; //出队消息回调函数

void *dequeue_context; //回调参数

} fixed_queue_t;


入队和出队信号量实际就是一个fd,队列可以将此fd注册到一个线程的reactor中进行监听,当监听到消息时进行消息回调。

Fixed_queue结构体方法介绍如下:

  1. fixed_queue_new为固定队列分配内存空间并进行初始化
  2. fixed_queue_is_empty队列是否为空
  3. fixed_queue_enqueue入队,入队时置出队信号量为1,表示有消息进入队列。
  4. fixed_queue_dequeue出队,出队时置入队信号量为1,表示此时允许消息入队。
  5. fixed_queue_register_dequeue注册出队信号到一个线程的Reactor中进行监听。
  • 参数(fixed_queue_t*queue, reactor_t *reactor, fixed_queue_cb ready_cb, void *context),queue代表要注册的队列,reactor代表一个线程的reactor,ready_cb代表出队消息的回调,context代表回调参数。
  1. fixed_queue_unregister_dequeue将队列从reactor中注销。

Thread与Reactor和FixedQueue共同构成了具有反应堆的工作线程,具体工作过程如下:

Bluetooth相关数据结构_协议栈

线程的创建主要包括reactor的创建,线程的运行包括reactor的注册以及运行过程,进入线程的消息封装work_item结构体,通过thread_post入队,等待队列执行该work_item。



Eager_reader.c

该数据结构用于监听vendor(controller)串口的输入事件,并分发到注册在该reader上的线程(即Hci线程)中去。

Eager_reader结构体如下定义:

struct eager_reader_t {

int bytes_available_fd; //使用eventfd来记录信道所收到的字节流总长度

int inbound_fd; //代表监听哪一个信道的有输入事件

const allocator_t *allocator; //内存分配器

size_t buffer_size; //接收一次输入字节流的缓冲区大小

fixed_queue_t *buffers; //reader从信道中读取的若干“字节流缓冲区”列表

data_buffer_t *current_buffer; //临时存放一个字节流缓冲区的数据

thread_t *inbound_read_thread; //用于监听和读取信道数据的线程

reactor_object_t *inbound_read_object; //注册到输入线程 reactor中的reactorobject对象

reactor_object_t *outbound_registration; //注册bytes_available_fd到输出线程reactor 中的reactorobject对象

eager_reader_cb outbound_read_ready; //告知输出线程缓冲区已经有数据的回调

void *outbound_context; //回调参数

};


Eager_reader结构体方法介绍如下:

  1. eager_reader_new为Eager_reader结构体分配内存空间并进行初始化,创建信道监听线程。
  • 参数:(int fd_to_read,const allocator_t *allocator, size_t buffer_size, size_t max_buffer_count,const char *thread_name)
  • fd_to_read代表需要监听的信道,allocator代表内存分配器,buffer_size代表一个字节流缓冲区的大小,max_buffer_count代表缓冲区的最多个数,thread_name代表监听信道的线程名。
  1. eager_reader_register在eager_reader注册一个线程,用于eager_reader分发所接收到的数据,这里的线程是Hci线程。
  • 参数:(eager_reader_t*reader, reactor_t *reactor, eager_reader_cb read_cb, void *context) reader代表需要注册的reader,reactor代表注册线程的reactor,read_cb代表注册在reader中“已经有数据可读”的回调,context代表回调参数。
  1. eager_reader_read从eager_reader中的buffers中读取数据。
  • 参数:(eager_reader_t*reader, uint8_t *buffer, size_t max_size)reader代表信道数据的reader,buffer代表读取数据存放的buffer,max_size代表需要读取数据的大小。
  1. inbound_data_waiting用于reader内部线程从信道中读取数据存放到buffers中,并写入buffers中buffer的个数到bytes_available_fd中,通知上层线程(Hci_thread)reader中已经有数据。
  2. has_byte用于检测eager_reader_read检测当前reader是否已经从信道中读取到了数据。

Eager_reader工作流程如下:

Bluetooth相关数据结构_Bluetooth_02

Eager_reader主要的工作就是监听controller端串口是否有数据输入,当有数据输入就可以上报到Hci_layer线程,并提供读取数据的方法。