static void write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; struct gatt_db_attribute *attr; uint16_t handle = 0; struct async_write_op *op = NULL; uint8_t ecode; if (length < 2) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } handle = get_le16(pdu); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Write %s - handle: 0x%04x", (opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd", handle); ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN | BT_ATT_PERM_WRITE_ENCRYPT); if (ecode) goto error; if (server->pending_write_op) { ecode = BT_ATT_ERROR_UNLIKELY; goto error; } op = new0(struct async_write_op, 1); op->server = server; op->opcode = opcode; server->pending_write_op = op; if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode, server->att, write_complete_cb, op)) return; if (op) async_write_op_destroy(op); ecode = BT_ATT_ERROR_UNLIKELY; error: if (opcode == BT_ATT_OP_WRITE_CMD) return; bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length, void *user_data) { struct bt_gatt_server *server = user_data; uint16_t handle = 0; uint16_t offset; struct gatt_db_attribute *attr; struct prep_write_complete_data *pwcd; uint8_t ecode, status; if (length < 4) { ecode = BT_ATT_ERROR_INVALID_PDU; goto error; } if (queue_length(server->prep_queue) >= server->max_prep_queue_len) { ecode = BT_ATT_ERROR_PREPARE_QUEUE_FULL; goto error; } handle = get_le16(pdu); offset = get_le16(pdu + 2); attr = gatt_db_get_attribute(server->db, handle); if (!attr) { ecode = BT_ATT_ERROR_INVALID_HANDLE; goto error; } util_debug(server->debug_callback, server->debug_data, "Prep Write Req - handle: 0x%04x", handle); ecode = check_permissions(server, attr, BT_ATT_PERM_WRITE | BT_ATT_PERM_WRITE_AUTHEN | BT_ATT_PERM_WRITE_ENCRYPT); if (ecode) goto error; pwcd = new0(struct prep_write_complete_data, 1); pwcd->pdu = malloc(length); memcpy(pwcd->pdu, pdu, length); pwcd->length = length; pwcd->server = server; status = gatt_db_attribute_write(attr, offset, NULL, 0, BT_ATT_OP_PREP_WRITE_REQ, server->att, prep_write_complete_cb, pwcd); if (status) return; ecode = BT_ATT_ERROR_UNLIKELY; error: bt_att_send_error_rsp(server->att, opcode, handle, ecode); }
static void desc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct descriptor *desc = op->data; struct service *service = desc->chrc->service; DBusMessage *reply; if (!success) goto fail; if (!op->offset) gatt_db_attribute_reset(desc->attr); if (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL, write_descriptor_cb, desc)) { error("Failed to store attribute"); goto fail; } /* * If the value length is exactly MTU-1, then we may not have read the * entire value. Perform a long read to obtain the rest, otherwise, * we're done. */ if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { op->offset += length; desc->read_id = bt_gatt_client_read_long_value( service->client->gatt, desc->handle, op->offset, desc_read_cb, async_dbus_op_ref(op), async_dbus_op_unref); if (desc->read_id) return; } /* Read the stored data from db */ if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) { error("Failed to read database"); goto fail; } desc->read_id = 0; return; fail: reply = create_gatt_dbus_error(op->msg, att_ecode); desc->read_id = 0; g_dbus_send_message(btd_get_dbus_connection(), reply); return; }
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 populate_gap_service(struct server *server) { bt_uuid_t uuid; struct gatt_db_attribute *service, *tmp; uint16_t appearance; /* Add the GAP service */ bt_uuid16_create(&uuid, UUID_GAP); service = gatt_db_add_service(server->db, &uuid, true, 6); /* * Device Name characteristic. Make the value dynamically read and * written via callbacks. */ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME); gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, BT_GATT_CHRC_PROP_READ, gap_device_name_read_cb, gap_device_name_write_cb, server); bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID); gatt_db_service_add_descriptor(service, &uuid, BT_ATT_PERM_READ, gap_device_name_ext_prop_read_cb, NULL, server); /* * Appearance characteristic. Reads and writes should obtain the value * from the database. */ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE); tmp = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, NULL, NULL, server); /* * Write the appearance value to the database, since we're not using a * callback. */ put_le16(128, &appearance); gatt_db_attribute_write(tmp, 0, (void *) &appearance, sizeof(appearance), BT_ATT_OP_WRITE_REQ, NULL, confirm_write, NULL); gatt_db_service_set_active(service, true); }
static void notify_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct notify_client *client = op->data; struct characteristic *chrc = client->chrc; /* * Even if the value didn't change, we want to send a PropertiesChanged * signal so that we propagate the notification/indication to * applications. */ gatt_db_attribute_reset(chrc->attr); gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL, write_characteristic_cb, chrc); }
static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { struct async_dbus_op *op = user_data; struct characteristic *chrc = op->data; struct service *service = chrc->service; if (!success) { DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode); chrc->read_id = 0; g_dbus_send_message(btd_get_dbus_connection(), reply); return ; } if (!op->offset) gatt_db_attribute_reset(chrc->attr); gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL, write_characteristic_cb, chrc); /* * If the value length is exactly MTU-1, then we may not have read the * entire value. Perform a long read to obtain the rest, otherwise, * we're done. */ if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) { op->offset += length; chrc->read_id = bt_gatt_client_read_long_value( service->client->gatt, chrc->value_handle, op->offset, chrc_read_cb, async_dbus_op_ref(op), async_dbus_op_unref); if (chrc->read_id) return; } chrc->read_id = 0; /* Read the stored data from db */ gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op); }
static void exec_next_prep_write(struct bt_gatt_server *server, uint16_t ehandle, int err) { struct prep_write_data *next = NULL; struct gatt_db_attribute *attr; bool status; if (err) goto error; next = queue_pop_head(server->prep_queue); if (!next) { bt_att_send(server->att, BT_ATT_OP_EXEC_WRITE_RSP, NULL, 0, NULL, NULL, NULL); return; } attr = gatt_db_get_attribute(server->db, next->handle); if (!attr) { err = BT_ATT_ERROR_UNLIKELY; goto error; } status = gatt_db_attribute_write(attr, next->offset, next->value, next->length, BT_ATT_OP_EXEC_WRITE_REQ, server->att, exec_write_complete_cb, server); prep_write_data_destroy(next); if (status) return; err = BT_ATT_ERROR_UNLIKELY; error: queue_remove_all(server->prep_queue, NULL, NULL, prep_write_data_destroy); bt_att_send_error_rsp(server->att, BT_ATT_OP_EXEC_WRITE_REQ, ehandle, err); }