static void populate_hr_service(struct server *server) { bt_uuid_t uuid; struct gatt_db_attribute *service, *hr_msrmt, *body; uint8_t body_loc = 1; /* "Chest" */ /* Add Heart Rate Service */ bt_uuid16_create(&uuid, UUID_HEART_RATE); service = gatt_db_add_service(server->db, &uuid, true, 8); server->hr_handle = gatt_db_attribute_get_handle(service); /* HR Measurement Characteristic */ bt_uuid16_create(&uuid, UUID_HEART_RATE_MSRMT); hr_msrmt = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_NONE, BT_GATT_CHRC_PROP_NOTIFY, NULL, NULL, NULL); server->hr_msrmt_handle = gatt_db_attribute_get_handle(hr_msrmt); bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, hr_msrmt_ccc_read_cb, hr_msrmt_ccc_write_cb, server); /* * Body Sensor Location Characteristic. Make reads obtain the value from * the database. */ bt_uuid16_create(&uuid, UUID_HEART_RATE_BODY); body = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, NULL, NULL, server); gatt_db_attribute_write(body, 0, (void *) &body_loc, sizeof(body_loc), BT_ATT_OP_WRITE_REQ, NULL, confirm_write, NULL); /* HR Control Point Characteristic */ bt_uuid16_create(&uuid, UUID_HEART_RATE_CTRL); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_WRITE, NULL, hr_control_point_write_cb, server); if (server->hr_visible) gatt_db_service_set_active(service, true); }
static void read_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct async_read_op *op = user_data; struct bt_gatt_server *server = op->server; uint8_t rsp_opcode; uint16_t mtu; uint16_t handle; if (!server) { async_read_op_destroy(op); return; } mtu = bt_att_get_mtu(server->att); handle = gatt_db_attribute_get_handle(attr); if (err) { bt_att_send_error_rsp(server->att, op->opcode, handle, err); async_read_op_destroy(op); return; } rsp_opcode = get_read_rsp_opcode(op->opcode); bt_att_send(server->att, rsp_opcode, len ? value : NULL, MIN((unsigned) mtu - 1, len), NULL, NULL, NULL); async_read_op_destroy(op); }
static void print_desc(struct gatt_db_attribute *attr, void *user_data) { printf("\t\t " COLOR_MAGENTA "descr" COLOR_OFF " - handle: 0x%04x, uuid: ", gatt_db_attribute_get_handle(attr)); print_uuid(gatt_db_attribute_get_type(attr)); }
static void populate_gatt_service(struct server *server) { bt_uuid_t uuid; struct gatt_db_attribute *service, *svc_chngd; /* Add the GATT service */ bt_uuid16_create(&uuid, UUID_GATT); service = gatt_db_add_service(server->db, &uuid, true, 4); bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); svc_chngd = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_INDICATE, gatt_service_changed_cb, NULL, server); server->gatt_svc_chngd_handle = gatt_db_attribute_get_handle(svc_chngd); bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, gatt_svc_chngd_ccc_read_cb, gatt_svc_chngd_ccc_write_cb, server); gatt_db_service_set_active(service, true); }
static struct descriptor *descriptor_create(struct gatt_db_attribute *attr, struct characteristic *chrc) { struct descriptor *desc; desc = new0(struct descriptor, 1); desc->chrc = chrc; desc->attr = attr; desc->handle = gatt_db_attribute_get_handle(attr); bt_uuid_to_uuid128(gatt_db_attribute_get_type(attr), &desc->uuid); desc->path = g_strdup_printf("%s/desc%04x", chrc->path, desc->handle); if (!g_dbus_register_interface(btd_get_dbus_connection(), desc->path, GATT_DESCRIPTOR_IFACE, descriptor_methods, NULL, descriptor_properties, desc, descriptor_free)) { error("Unable to register GATT descriptor with handle 0x%04x", desc->handle); descriptor_free(desc); return NULL; } DBG("Exported GATT characteristic descriptor: %s", desc->path); if (uuid_cmp(&desc->uuid, GATT_CHARAC_EXT_PROPER_UUID)) chrc->ext_props_handle = desc->handle; return desc; }
static void process_read_by_type(struct async_read_op *op) { struct bt_gatt_server *server = op->server; uint8_t ecode; struct gatt_db_attribute *attr; attr = queue_pop_head(op->db_data); if (op->done || !attr) { bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, op->pdu_len, NULL, NULL, NULL); async_read_op_destroy(op); return; } ecode = check_permissions(server, attr, BT_ATT_PERM_READ | BT_ATT_PERM_READ_AUTHEN | BT_ATT_PERM_READ_ENCRYPT); if (ecode) goto error; if (gatt_db_attribute_read(attr, 0, op->opcode, server->att, read_by_type_read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); }
static void exec_write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle = gatt_db_attribute_get_handle(attr); exec_next_prep_write(server, handle, err); }
static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q, uint16_t mtu, uint8_t *pdu, uint16_t *len) { uint16_t handle; struct gatt_db_attribute *attr; const bt_uuid_t *type; int uuid_len, cur_uuid_len; int iter = 0; *len = 0; while (queue_peek_head(q)) { attr = queue_pop_head(q); handle = gatt_db_attribute_get_handle(attr); type = gatt_db_attribute_get_type(attr); if (!handle || !type) return false; cur_uuid_len = bt_uuid_len(type); if (iter == 0) { switch (cur_uuid_len) { case 2: uuid_len = 2; pdu[0] = 0x01; break; case 4: case 16: uuid_len = 16; pdu[0] = 0x02; break; default: return false; } iter++; } else if (cur_uuid_len != uuid_len) break; if (iter + uuid_len + 2 > mtu - 1) break; put_le16(handle, pdu + iter); bt_uuid_to_le(type, pdu + iter + 2); iter += uuid_len + 2; } *len = iter; return true; }
static void read_by_type_read_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct async_read_op *op = user_data; struct bt_gatt_server *server = op->server; uint16_t mtu; uint16_t handle; if (!server) { async_read_op_destroy(op); return; } mtu = bt_att_get_mtu(server->att); handle = gatt_db_attribute_get_handle(attr); /* Terminate the operation if there was an error */ if (err) { bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, handle, err); async_read_op_destroy(op); return; } if (op->pdu_len == 0) { op->value_len = MIN(MIN((unsigned) mtu - 4, 253), len); op->pdu[0] = op->value_len + 2; op->pdu_len++; } else if (len != op->value_len) { op->done = true; goto done; } /* Stop if this would surpass the MTU */ if (op->pdu_len + op->value_len + 2 > (unsigned) mtu - 1) { op->done = true; goto done; } /* Encode the current value */ put_le16(handle, op->pdu + op->pdu_len); memcpy(op->pdu + op->pdu_len + 2, value, op->value_len); op->pdu_len += op->value_len + 2; if (op->pdu_len == (unsigned) mtu - 1) op->done = true; done: process_read_by_type(op); }
static void process_read_by_type(struct async_read_op *op) { struct bt_gatt_server *server = op->server; uint8_t ecode; struct gatt_db_attribute *attr; uint32_t perm; attr = queue_pop_head(op->db_data); if (op->done || !attr) { bt_att_send(server->att, BT_ATT_OP_READ_BY_TYPE_RSP, op->pdu, op->pdu_len, NULL, NULL, NULL); async_read_op_destroy(op); return; } perm = gatt_db_attribute_get_permissions(attr); /* * Check for the READ access permission. Encryption, * authentication, and authorization permissions need to be * checked by the read handler, since bt_att is agnostic to * connection type and doesn't have security information on it. */ if (perm && !(perm & BT_ATT_PERM_READ)) { ecode = BT_ATT_ERROR_READ_NOT_PERMITTED; goto error; } if (gatt_db_attribute_read(attr, 0, op->opcode, server->att, read_by_type_read_complete_cb, op)) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, BT_ATT_OP_READ_BY_TYPE_REQ, gatt_db_attribute_get_handle(attr), ecode); async_read_op_destroy(op); }
static void write_complete_cb(struct gatt_db_attribute *attr, int err, void *user_data) { struct async_write_op *op = user_data; struct bt_gatt_server *server = op->server; uint16_t handle; if (!server || op->opcode == BT_ATT_OP_WRITE_CMD) { async_write_op_destroy(op); return; } handle = gatt_db_attribute_get_handle(attr); if (err) bt_att_send_error_rsp(server->att, op->opcode, handle, err); else bt_att_send(server->att, BT_ATT_OP_WRITE_RSP, NULL, 0, NULL, NULL, NULL); async_write_op_destroy(op); }
static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err, const uint8_t *value, size_t len, void *user_data) { struct read_multiple_resp_data *data = user_data; struct gatt_db_attribute *next_attr; uint32_t perm; uint16_t handle = gatt_db_attribute_get_handle(attr); if (err != 0) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, err); read_multiple_resp_data_free(data); return; } perm = gatt_db_attribute_get_permissions(attr); if (perm && !(perm & BT_ATT_PERM_READ)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, handle, BT_ATT_ERROR_READ_NOT_PERMITTED); read_multiple_resp_data_free(data); return; } len = MIN(len, data->mtu - data->length - 1); memcpy(data->rsp_data + data->length, value, len); data->length += len; data->cur_handle++; if ((data->length >= data->mtu - 1) || (data->cur_handle == data->num_handles)) { bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP, data->rsp_data, data->length, NULL, NULL, NULL); read_multiple_resp_data_free(data); return; } util_debug(data->server->debug_callback, data->server->debug_data, "Read Multiple Req - #%zu of %zu: 0x%04x", data->cur_handle + 1, data->num_handles, data->handles[data->cur_handle]); next_attr = gatt_db_get_attribute(data->server->db, data->handles[data->cur_handle]); if (!next_attr) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_INVALID_HANDLE); read_multiple_resp_data_free(data); return; } if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ, data->server->att, read_multiple_complete_cb, data)) { bt_att_send_error_rsp(data->server->att, BT_ATT_OP_READ_MULT_REQ, data->handles[data->cur_handle], BT_ATT_ERROR_UNLIKELY); read_multiple_resp_data_free(data); } }