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 DBusMessage *descriptor_write_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct descriptor *desc = user_data; struct bt_gatt_client *gatt = desc->chrc->service->client->gatt; uint8_t *value = NULL; size_t value_len = 0; if (!gatt) return btd_error_failed(msg, "Not connected"); if (desc->write_id) return btd_error_in_progress(msg); if (!parse_value_arg(msg, &value, &value_len)) return btd_error_invalid_args(msg); /* * Don't allow writing to Client Characteristic Configuration * descriptors. We achieve this through the StartNotify and StopNotify * methods on GattCharacteristic1. */ if (uuid_cmp(&desc->uuid, GATT_CLIENT_CHARAC_CFG_UUID)) return btd_error_not_permitted(msg, "Write not permitted"); /* * Based on the value length and the MTU, either use a write or a long * write. */ if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3) desc->write_id = start_write_request(msg, desc->handle, gatt, value, value_len, desc, desc_write_complete); else desc->write_id = start_long_write(msg, desc->handle, gatt, false, value, value_len, desc, desc_write_complete); if (!desc->write_id) return btd_error_failed(msg, "Failed to initiate write"); return NULL; }
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 DBusMessage *characteristic_write_value(DBusConnection *conn, DBusMessage *msg, void *user_data) { struct characteristic *chrc = user_data; struct bt_gatt_client *gatt = chrc->service->client->gatt; uint8_t *value = NULL; size_t value_len = 0; bool supported = false; if (chrc->write_id) return btd_error_in_progress(msg); if (!parse_value_arg(msg, &value, &value_len)) return btd_error_invalid_args(msg); /* * Decide which write to use based on characteristic properties. For now * we don't perform signed writes since gatt-client doesn't support them * and the user can always encrypt the through pairing. The procedure to * use is determined based on the following priority: * * * "reliable-write" property set -> reliable long-write. * * "write" property set -> write request. * - If value is larger than MTU - 3: long-write * * "write-without-response" property set -> write command. */ if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) { supported = true; chrc->write_id = start_long_write(msg, chrc->value_handle, gatt, true, value, value_len, chrc, chrc_write_complete); if (chrc->write_id) return NULL; } if (chrc->props & BT_GATT_CHRC_PROP_WRITE) { uint16_t mtu; supported = true; mtu = bt_gatt_client_get_mtu(gatt); if (!mtu) return btd_error_failed(msg, "No ATT transport"); if (value_len <= (unsigned) mtu - 3) chrc->write_id = start_write_request(msg, chrc->value_handle, gatt, value, value_len, chrc, chrc_write_complete); else chrc->write_id = start_long_write(msg, chrc->value_handle, gatt, false, value, value_len, chrc, chrc_write_complete); if (chrc->write_id) return NULL; } if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)) goto fail; supported = true; chrc->write_id = bt_gatt_client_write_without_response(gatt, chrc->value_handle, false, value, value_len); if (chrc->write_id) return dbus_message_new_method_return(msg); fail: if (supported) return btd_error_failed(msg, "Failed to initiate write"); return btd_error_not_supported(msg); }