BlueZ BLE ATT 读写分析
本文只分析ATT架构,BT 5.2版本之后提出了EATT,如果需求可以留言给我,后续发布。
ATT读写,首先要知道它的读写规则及限制:

下面对读写进行详细分析:
//bluez初始化
read_info_completeadapter_registerbtd_gatt_database_new//创建监听socket,等待外部设备连接database->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,BT_IO_OPT_SOURCE_BDADDR, addr,BT_IO_OPT_SOURCE_TYPE,btd_adapter_get_address_type(adapter),BT_IO_OPT_CID, BT_ATT_CID,BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,BT_IO_OPT_INVALID);
//外部设备连接成功
connect_cbdevice_attach_attattrib = g_attrib_new(io,cid == ATT_CID ? BT_ATT_DEFAULT_LE_MTU : dev->att_mtu,false)GAttrib *attr;struct bt_att *att;attr->io = io; attr->att = bt_att_new(fd, ext_signed);fd = g_io_channel_unix_get_fd(io);//底层反馈的socketatt = new0(struct bt_att, 1);att->fd = fd;att->io = io_new(fd);struct io *io;io->fd = fd;mainloop_add_fd(io->fd, io->events, io_callback, io, io_cleanup)epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev)/* Attribute PDUs have one of six types:* Commands: PDUs sent to a server by a client that do not invoke a response.* Requests:PDUs sent to a server by a client that invoke a response.* Responses:PDUs sent to a client by a server in response to a request* Notifications:Unsolicited PDUs sent to a client by a server that do not invoke a confirmation.* Indications:Unsolicited PDUs sent to a client by a server that invoke a confirmation.* Confirmations:PDUs sent to a server by a client to confirm receipt of an indication.*/att->req_queue = queue_new();att->ind_queue = queue_new();att->write_queue = queue_new(); att->notify_list = queue_new();att->disconn_list = queue_new();//当底层有可读事件调用can_read_dataio_set_read_handler(att->io, can_read_data, att, NULL)events = io->events | EPOLLIN;io->read_callback = can_read_data; io->read_data = att; mainloop_modify_fd(io->fd, events)epoll_ctl(epoll_fd, EPOLL_CTL_ADD, data->fd, &ev)att->buf = malloc(att->mtu);attr->buf = malloc0(mtu);attr->buflen = mtu;attr->callbacks = queue_new();attr->track_ids = queue_new();//struct btd_device *devdev->attrib = attrib;dev->att = g_attrib_get_att(attrib);database = btd_adapter_get_database(dev->adapter);//本地client初始化gatt_client_init(dev); //struct bt_gatt_client *client;bt_gatt_client_new(device->db, device->att, device->att_mtu, 0);gatt_client_new(db, att, features);gatt_client_init(client, mtu)/* Configure the MTU */bt_gatt_exchange_mtu(client->att, mtu,exchange_mtu_cb,discovery_op_ref(op),discovery_op_unref);btd_gatt_client_connected...//本地server初始化gatt_server_init(dev, database);struct gatt_db *db = database->db;device->server = bt_gatt_server_new(db, device->att, device->att_mtu,main_opts.key_size);struct bt_gatt_server *server;server = new0(struct bt_gatt_server, 1);server->db = gatt_db_ref(db);server->att = bt_att_ref(att);server->mtu = MAX(mtu, BT_ATT_DEFAULT_LE_MTU);server->max_prep_queue_len = DEFAULT_MAX_PREP_QUEUE_LEN;server->prep_queue = queue_new();server->min_enc_size = min_enc_size;gatt_server_register_att_handlers/* BLUETOOTH CORE SPECIFICATION Version 5.3 | Vol 3, Part F* 3.4 ATTRIBUTE PROTOCOL PDUS* ATTRIBUTE PROTOCOL PDUS * ATT_ERROR_RSP/ATT_EXCHANGE_MTU_RSP* ATT_FIND_INFORMATION_REQ/ATT_FIND_INFORMATION_RSP* ATT_FIND_BY_TYPE_VALUE_REQ/ATT_FIND_BY_TYPE_VALUE_RSP* ... ...*///下面是注册各类ATT的命令对应的处理函数/* Exchange MTU */server->mtu_id = bt_att_register(server->att,BT_ATT_OP_MTU_REQ,exchange_mtu_cb,server, NULL);/* Read By Group Type */bt_att_register(server->att,BT_ATT_OP_READ_BY_GRP_TYPE_REQ,read_by_grp_type_cb, server, NULL);struct att_notify *notify;notify->opcode = BT_ATT_OP_READ_BY_GRP_TYPE_REQ;notify->callback = read_by_grp_type_cb;//把对应的req的处理函数放入notify_list里面queue_push_tail(att->notify_list, notify) /* Read By Type *//* Find Information *//* Find By Type Value *//* find_by_type_val_cb *//* Write Request *//* Write Command *//* Read Request *//* Read Blob Request *//* Prepare Write Request *//* Execute Write Request *///底层io有收到远端发来的事件
can_read_datastruct bt_att *att = user_data;/* Act on the received PDU based on the opcode type */switch (get_op_type(opcode)) {bytes_read = read(att->fd, att->buf, att->mtu);/* Attribute PDUs have one of six types:* Commands: PDUs sent to a server by a client that do not invoke a response.* Requests:PDUs sent to a server by a client that invoke a response.* Responses:PDUs sent to a client by a server in response to a request* Notifications:Unsolicited PDUs sent to a client by a server that do not invoke a confirmation.* Indications:Unsolicited PDUs sent to a client by a server that invoke a confirmation.* Confirmations:PDUs sent to a server by a client to confirm receipt of an indication.*/case ATT_OP_TYPE_RSP:handle_rsp(att, opcode, pdu + 1, bytes_read - 1);struct att_send_op *op = att->pending_req; //rsp 则表示主机有发送过req正在等待回应req_opcode = get_req_opcode(opcode)if (req_opcode != op->opcode) //匹配则对应的RSP回应来了goto fail;if (op->callback)op->callback(rsp_opcode, rsp_pdu, rsp_pdu_len, op->user_data);att->pending_req = NULL;wakeup_writer(att); //继续处理队列里面的其它请求case ATT_OP_TYPE_CONF:handle_conf(chan, pdu + 1, bytes_read - 1);case ATT_OP_TYPE_REQ: //表示对端发来了一个请求// If a request is currently pending, then the sequential protocol was violated. Disconnect the bearer, // which will promptly notify the upper layer via disconnect handlers.if (att->in_req)io_shutdown(att->io); //目前只允许req/rsp串行化,不允许并行bt_att_unref(att);att->in_req = true; //表示该请求正在处理中,在rsp发出之后要置为falsecase ATT_OP_TYPE_CMD:case ATT_OP_TYPE_NFY:case ATT_OP_TYPE_UNKNOWN:case ATT_OP_TYPE_IND:handle_notify(chan, opcode, pdu + 1, bytes_read - 1);//从之前注册的notify_list取出对应的req的处理函数const struct queue_entry *entry = queue_get_entries(att->notify_list); while (entry) {struct att_notify *notify = entry->data;entry = entry->next;opcode_match(notify->opcode, opcode) //进行匹配if (notify->callback) //匹配到之后调用对应的处理函数notify->callback(chan, opcode, pdu, pdu_len, notify->user_data);//举例
/* Find By Type Value */
find_by_type_val_cbstruct bt_gatt_server *server = user_data;uint16_t mtu = bt_att_get_mtu(server->att);struct find_by_type_val_data data;data.pdu = rsp_pdu; //uint8_t rsp_pdu[mtu];data.len = 0;data.mtu = mtu;data.ecode = 0;//解析start = get_le16(pdu);end = get_le16(pdu + 2);uuid16 = get_le16(pdu + 4);ehandle = start;bt_uuid16_create(&uuid, uuid16);//从db数据库中查找对应的内容gatt_db_find_by_type_value(server->db, start, end, &uuid, pdu + 6,length - 6,find_by_type_val_att_cb,&data);//找到后发出去,目前采用的是队列,所有只是挂到相应的队列而已bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_RSP, data.pdu,data.len, NULL, NULL, NULL);struct att_send_op *op;op = create_att_send_op(att, opcode, pdu, length, callback, user_data,destroy);op->id = att->next_send_id++;/* Add the op to the correct queue based on its type */switch (op->type) {case ATT_OP_TYPE_REQ:// 它是本地主机发出request请求,会挂在这个队列,它需要等待rsp,对应上面的can_read_data ATT_OP_TYPE_RSP/* Queued ATT protocol requests */ queue_push_tail(att->req_queue, op); case ATT_OP_TYPE_IND:/* Queued ATT protocol indications */result = queue_push_tail(att->ind_queue, op); case ATT_OP_TYPE_CMD:case ATT_OP_TYPE_NOT:case ATT_OP_TYPE_UNKNOWN:case ATT_OP_TYPE_RSP: //case ATT_OP_TYPE_CONF:default:/* Queue of PDUs ready to send *///它是本地主机响应对端的的请求,无需等待rspresult = queue_push_tail(att->write_queue, op);wakeup_writer(att); //继续处理队列的任务if (att->writer_active) //调用一次即可return; /* Set the write handler only if there is anything that can be sent* at all. * 从三个队列里面找请求*/if (queue_isempty(att->write_queue)) {if ((att->pending_req || queue_isempty(att->req_queue)) &&(att->pending_ind || queue_isempty(att->ind_queue)))return;}//设置写回调io_set_write_handler(att->io, can_write_data, att, write_watch_destroy)events = io->events | EPOLLOUT;io->write_callback = can_write_data;io->write_data = att;mainloop_modify_fd(io->fd, events)io->events = events;att->writer_active = true;//当io可写时调用该函数(底层有空闲资源时,可以发送)
can_write_datastruct bt_att *att = user_data;op = pick_next_send_op(att);struct att_send_op *op;/* See if any operations are already in the write queue *///优先发送对端请求的响应op = queue_pop_head(att->write_queue); /* If there is no pending request, pick an operation from the* request queue.*///其次发送本地发出的请求if (!att->pending_req) {op = queue_pop_head(att->req_queue);if (op)return op;}/* There is either a request pending or no requests queued. If there is* no pending indication, pick an operation from the indication queue.*/if (!att->pending_ind) {op = queue_pop_head(att->ind_queue);if (op)return op;}ret = io_send(io, &iov, 1);switch (op->type) {case ATT_OP_TYPE_REQ:att->pending_req = op; //发出请求后,需要等待对端响应,所有要挂到pending_req队列里面去break;case ATT_OP_TYPE_IND:att->pending_ind = op; //同上break;case ATT_OP_TYPE_RSP:/* Set in_req to false to indicate that no request is pending */att->in_req = false; //发送完RSP后要置为false/* fall through */case ATT_OP_TYPE_CMD:case ATT_OP_TYPE_NOT:case ATT_OP_TYPE_CONF:case ATT_OP_TYPE_UNKNOWN:default:destroy_att_send_op(op);return true;}//举一个server主动发出一个通知notify
g_dbus_proxy_set_property_watch(chrc->proxy, property_changed_cb, chrc) //chrc是上层注册的,当上层发出一个change property时,则会调用这个函数
property_changed_cbsend_notification_to_devices(chrc->service->app->database,gatt_db_attribute_get_handle(chrc->attrib),value, len,gatt_db_attribute_get_handle(chrc->ccc),chrc->props & BT_GATT_CHRC_PROP_INDICATE ? conf_cb : NULL, //注意这里,由于INDICATE需要rsp,所有需要有个callbackproxy);send_notification_to_device()bt_gatt_server_send_notification()bt_gatt_server_send_indication()conf_cb //gatt-database //它会在can_read_data的RSP里面被调用DBG("GATT server received confirmation"); //indicate需要一个responseg_dbus_proxy_method_call(proxy, "Confirm", NULL, NULL, NULL, NULL);bt_gatt_server_send_notification(server,notify->handle, notify->value,notify->len);//最终调用到bt_att_send,最终会挂到att->write_queue队列,因为这个队列不需要等待响应bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_NOT, pdu,pdu_len, NULL, NULL, NULL)
bt_gatt_server_send_indicationstruct ind_data *data;data->callback = conf_cb;data->destroy = destroy;data->user_data = user_data;put_le16(handle, pdu);memcpy(pdu + 2, value, pdu_len - 2);//最终调用到bt_att_send,挂在到这个队列:att->ind_queue /* Queued ATT protocol indications */bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_IND, pdu,pdu_len, conf_cb, //gatt-server.c的conf_cbdata, destroy_ind_data)//ATT 指令转换
static const struct {uint8_t opcode;enum att_op_type type;
} att_opcode_type_table[] = {{ BT_ATT_OP_ERROR_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_MTU_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_MTU_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_FIND_INFO_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_FIND_INFO_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_FIND_BY_TYPE_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_FIND_BY_TYPE_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_READ_BY_TYPE_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_READ_BY_TYPE_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_READ_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_READ_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_READ_BLOB_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_READ_BLOB_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_READ_MULT_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_READ_MULT_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_READ_BY_GRP_TYPE_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_READ_BY_GRP_TYPE_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_WRITE_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_WRITE_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_WRITE_CMD, ATT_OP_TYPE_CMD },{ BT_ATT_OP_SIGNED_WRITE_CMD, ATT_OP_TYPE_CMD },{ BT_ATT_OP_PREP_WRITE_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_PREP_WRITE_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_EXEC_WRITE_REQ, ATT_OP_TYPE_REQ },{ BT_ATT_OP_EXEC_WRITE_RSP, ATT_OP_TYPE_RSP },{ BT_ATT_OP_HANDLE_VAL_NOT, ATT_OP_TYPE_NOT },{ BT_ATT_OP_HANDLE_VAL_IND, ATT_OP_TYPE_IND },{ BT_ATT_OP_HANDLE_VAL_CONF, ATT_OP_TYPE_CONF },{ }
};get_ccc_stateget_device_statestruct device_state *dev_state;get_dst_info(att, &bdaddr, &bdaddr_type)dev_state = device_state_create(database, &bdaddr, bdaddr_type);dev_state = new0(struct device_state, 1);dev_state->db = db;dev_state->ccc_states = queue_new();bacpy(&dev_state->bdaddr, bdaddr);dev_state->bdaddr_type = bdaddr_type;queue_push_tail(database->device_states, dev_state);struct ccc_state *ccc;ccc = new0(struct ccc_state, 1);ccc->handle = handle;queue_push_tail(dev_state->ccc_states, ccc);src/adapter.c: connected_callback() hci0 device 50:C7:D8:2C:1C:1A connected eir_len 0
src/device.c:device_create() dst 50:C7:D8:2C:1C:1A
src/device.c:device_new() address 50:C7:D8:2C:1C:1A
src/device.c:device_new() Creating device /org/bluez/hci0/dev_50_C7_D8_2C_1C_1Asrc/gatt-database.c: connect_cb() New incoming LE ATT connection
attrib/gattrib.c: g_attrib_ref() 0x22ec3580: g_attrib_ref=1src/device.c:load_gatt_db() Restoring 50:C7:D8:2C:1C:1A gatt database from file
src/gatt-client.c:btd_gatt_client_connected() Device connected.src/device.c:gatt_debug() Client MTU exchange complete, with MTU: 517
src/device.c:gatt_debug() Read By Grp Type - start: 0x0001 end: 0xffffsrc/device.c:gatt_debug() Primary services found: 2
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
