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); }
static void cmd_write_value(struct client *cli, char *cmd_str) { int opt, i; char *argvbuf[516]; char **argv = argvbuf; int argc = 1; uint16_t handle; char *endptr = NULL; int length; uint8_t *value = NULL; bool without_response = false; if (!bt_gatt_client_is_ready(cli->gatt)) { printf("GATT client not initialized\n"); return; } if (!parse_args(cmd_str, 514, argv + 1, &argc)) { printf("Too many arguments\n"); write_value_usage(); return; } optind = 0; argv[0] = "write-value"; while ((opt = getopt_long(argc, argv, "+w", write_value_options, NULL)) != -1) { switch (opt) { case 'w': without_response = true; break; default: write_value_usage(); return; } } argc -= optind; argv += optind; if (argc < 1) { write_value_usage(); return; } handle = strtol(argv[0], &endptr, 16); if (!endptr || *endptr != '\0' || !handle) { printf("Invalid handle: %s\n", argv[0]); return; } length = argc - 1; if (length > 0) { if (length > UINT16_MAX) { printf("Write value too long\n"); return; } value = malloc(length); if (!value) { printf("Failed to construct write value\n"); return; } for (i = 1; i < argc; i++) { if (strlen(argv[i]) != 2) { printf("Invalid value byte: %s\n", argv[i]); goto done; } value[i-1] = strtol(argv[i], &endptr, 16); if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) { printf("Invalid value byte: %s\n", argv[i]); goto done; } } } if (without_response) { if (!bt_gatt_client_write_without_response(cli->gatt, handle, false, value, length)) { printf("Failed to initiate write without response " "procedure\n"); goto done; } printf("Write command sent\n"); goto done; } if (!bt_gatt_client_write_value(cli->gatt, handle, value, length, write_cb, NULL, NULL)) printf("Failed to initiate write procedure\n"); done: free(value); }