static int survive_config_submit(struct SurviveUSBInfo *usbInfo) { struct survive_config_packet *config_packet = usbInfo->cfg_user; SurviveContext *ctx = config_packet->sv->ctx; SV_VERBOSE(110, "Submitting config for %s %s at %f", survive_colorize(usbInfo->so ? usbInfo->so->codename : usbInfo->device_info->codename), survive_colorize(usbInfo->device_info->name), usbInfo->nextCfgSubmitTime); usbInfo->nextCfgSubmitTime = -1; int rc = survive_usb_transfer_submit(config_packet->tx); if (rc) { SV_WARN("Failed to submit transfer %s %s (%d)", survive_colorize_codename(usbInfo->so), survive_colorize(usbInfo->device_info->name), rc); return -6; } return 0; } static inline void setup_config_req(struct survive_config_packet *config_packet) { config_packet->state = SURVIVE_CONFIG_STATE_CONFIG; survive_usb_setup_get_feature_report(config_packet->tx, VIVE_REPORT_CONFIG_READMODE); } static inline void setup_version(struct survive_config_packet *packet) { packet->state = SURVIVE_CONFIG_STATE_VERSION; survive_usb_setup_get_feature_report(packet->tx, VIVE_REPORT_VERSION); } static inline void setup_config_imu_scales(struct survive_config_packet *packet) { packet->state = SURVIVE_CONFIG_STATE_IMU_SCALES; survive_usb_setup_get_feature_report(packet->tx, VIVE_REPORT_IMU_SCALES); } static inline void setup_magic(struct survive_config_packet *packet) { SurviveContext *ctx = packet->ctx; SurviveObject *so = packet->usbInfo->so; packet->state = SURVIVE_CONFIG_STATE_MAGICS; SV_VERBOSE(100, "Submitting magic %s at %f sec for %s - %s (length %d)", survive_colorize(packet->current_magic->name), survive_run_time(ctx) - packet->start_time, survive_colorize(so ? so->codename : "BRD"), survive_colorize(packet->usbInfo->device_info->name), (int)packet->current_magic->length); survive_usb_setup_update_feature_report(packet->tx, packet->current_magic->magic, packet->current_magic->length); do { packet->current_magic++; } while (packet->current_magic->magic && packet->current_magic->code == 0); } void handle_config_tx(survive_usb_transfer_t *transfer); static inline void setup_packet_state(struct survive_config_packet *packet) { switch (packet->state) { case SURVIVE_CONFIG_STATE_MAGICS: setup_magic(packet); break; case SURVIVE_CONFIG_STATE_VERSION: setup_version(packet); break; case SURVIVE_CONFIG_STATE_CONFIG: setup_config_req(packet); break; case SURVIVE_CONFIG_STATE_IMU_SCALES: setup_config_imu_scales(packet); return; case SURVIVE_CONFIG_STATE_DONE: return; } survive_usb_setup_control(packet->tx, packet->usbInfo, handle_config_tx, packet, 1000); } void survive_usb_feature_read(SurviveObject *so, const uint8_t *data, size_t length) { SurviveContext *ctx = so->ctx; switch (data[0]) { case VIVE_REPORT_CHANGE_MODE: SV_VERBOSE(100, "%s new mode %d", survive_colorize_codename(so), data[1]); break; case VIVE_REPORT_IMU_SCALES: { int gyro_scale_mode = data[1]; int acc_scale_mode = data[2]; survive_default_set_imu_scale_modes(so, gyro_scale_mode, acc_scale_mode); break; } case VIVE_REPORT_VERSION: parse_tracker_version_info(so, data + 1, length - 1); break; case VIVE_REPORT_CONFIG_READMODE: case VIVE_REPORT_CONFIG_READ: break; default: SV_VERBOSE(10, "============== Unknown feature %s read %x %d", survive_colorize_codename(so), data[0], (int)length); break; } } void handle_config_tx(survive_usb_transfer_t *transfer) { struct survive_config_packet *packet = transfer->user_data; SurviveContext *ctx = packet->ctx; const uint8_t *buffer = survive_usb_transfer_data(transfer); SurviveObject *so = packet->usbInfo->so; uint8_t cmd = buffer[0]; struct SurviveUSBInfo *usbInfo = packet->usbInfo; if (packet->usbInfo->interfaces[0].shutdown) { goto cleanup; } if (transfer->status == LIBUSB_TRANSFER_STALL || transfer->status == LIBUSB_TRANSFER_TIMED_OUT) { SV_VERBOSE(110, "Waiting, Transfer status %d at %f sec for %s (%d)", transfer->status, survive_run_time(ctx) - packet->start_time, survive_colorize(so ? so->codename : "unknown"), packet->usbInfo->request_close); packet->stall_counter++; if (packet->usbInfo->device_info->codename[0] == 0) { goto cleanup; } packet->usbInfo->nextCfgSubmitTime = survive_run_time(ctx) + .02; if (packet->stall_counter > 5) { packet->stall_counter = 0; packet->usbInfo->nextCfgSubmitTime++; } return; } packet->stall_counter = 0; if (transfer->status != LIBUSB_TRANSFER_COMPLETED) { SV_WARN("Transfer status %d at %f sec for %s", transfer->status, survive_run_time(ctx) - packet->start_time, survive_colorize(so ? so->codename : "unknown")); goto cleanup; } if (so == 0) { if (usbInfo->device_info->codename[0] != 0) { so = survive_create_device(ctx, "HTC", usbInfo, usbInfo->device_info->codename, survive_vive_send_haptic); survive_add_object(ctx, so); usbInfo->so = so; usbInfo->ownsObject = true; } } size_t tx_length = survive_usb_transfer_length(transfer); switch (packet->state) { case SURVIVE_CONFIG_STATE_MAGICS: if (tx_length < packet->current_magic->length) { SV_WARN("Magic for %s was not acked; length %d vs %d", so ? survive_colorize(so->codename) : "unknown", (int)tx_length, (int)packet->current_magic->length); goto resubmit; } SV_VERBOSE(110, "Magic inc in %f sec for %s %s %d", survive_run_time(ctx) - packet->start_time, so ? survive_colorize(so->codename) : "unknown", survive_colorize(packet->usbInfo->device_info->name), (int)tx_length) if (!packet->current_magic->magic) { SV_VERBOSE(100, "Magics done in %f sec for %s %s", survive_run_time(ctx) - packet->start_time, so ? survive_colorize(so->codename) : "unknown", survive_colorize(packet->usbInfo->device_info->name)); packet->usbInfo->lightcapMode = LightcapMode_raw0; if (packet->usbInfo->device_info->codename[0] == 0) { packet->state = SURVIVE_CONFIG_STATE_DONE; goto cleanup; } goto setup_next; } setup_magic(packet); goto resubmit; case SURVIVE_CONFIG_STATE_CONFIG: switch (cmd) { case VIVE_REPORT_CONFIG_READMODE: survive_usb_setup_get_feature_report(transfer, VIVE_REPORT_CONFIG_READ); packet->expected_cfg_length = (buffer[1] << 8 | buffer[2]); SV_VERBOSE(100, "Config readmode in %f sec for %s expected length: %d", survive_run_time(ctx) - packet->start_time, survive_colorize(so->codename), packet->expected_cfg_length); str_ensure_size(&packet->cfg, packet->expected_cfg_length); goto resubmit; case VIVE_REPORT_CONFIG_READ: { SV_VERBOSE(110, "Config response in %f sec for %s %d %d", survive_run_time(ctx) - packet->start_time, survive_colorize(so->codename), buffer[0], buffer[1]); size_t size = buffer[1]; str_append_n(&packet->cfg, (const char *)&buffer[2], size); if (size == 0) { // NOTE: Sometimes the actual length is 2 bytes longer than advertised; not sure why if (packet->expected_cfg_length > packet->cfg.length) { SV_WARN("Config request failed; trying again %f %s (%d > %d)", survive_run_time(ctx) - packet->start_time, survive_colorize(so->codename), packet->expected_cfg_length, (int)packet->cfg.length); str_clear(&packet->cfg); setup_config_req(packet); goto resubmit; } SV_VERBOSE(100, "Config done in %f sec for %s, len %ld", survive_run_time(ctx) - packet->start_time, survive_colorize(so->codename), packet->cfg.length); uint8_t uncompressed_data[65536]; int uncompressed_data_len = survive_simple_inflate(ctx, (uint8_t *)packet->cfg.d, packet->cfg.length, uncompressed_data, sizeof(uncompressed_data) - 1); if (uncompressed_data_len < 0) { SV_ERROR(SURVIVE_ERROR_INVALID_CONFIG, "Invalid config for %s", survive_colorize(so->codename)); goto cleanup; } packet->usbInfo->so->conf = SV_CALLOC(uncompressed_data_len + 1); packet->usbInfo->so->conf_cnt = uncompressed_data_len; memcpy(packet->usbInfo->so->conf, uncompressed_data, uncompressed_data_len); str_free(&packet->cfg); goto setup_next; } goto resubmit; } default: SV_ERROR(SURVIVE_ERROR_GENERAL, "Unknown config command 0x%x", cmd); } break; case SURVIVE_CONFIG_STATE_VERSION: { if (tx_length == 0) goto resubmit; parse_tracker_version_info(packet->usbInfo->so, &buffer[1], tx_length); SURVIVE_INVOKE_HOOK_SO(config, so, so->conf, so->conf_cnt); SV_VERBOSE(100, "Version done in %f sec for %s", survive_run_time(ctx) - packet->start_time, survive_colorize(so->codename)); goto setup_next; } case SURVIVE_CONFIG_STATE_IMU_SCALES: { int gyro_scale_mode = buffer[1]; int acc_scale_mode = buffer[2]; survive_default_set_imu_scale_modes(so, gyro_scale_mode, acc_scale_mode); goto setup_next; } default: SV_WARN("Config state machine saw packet of type %d at state %d; not sure how to proceed.", cmd, packet->state); goto cleanup; } return; setup_next : { packet->state++; setup_packet_state(packet); if (packet->state == SURVIVE_CONFIG_STATE_DONE) goto cleanup; else goto resubmit; }; resubmit : { SV_VERBOSE(110, "Resubmit startup packet for %s %s at %f", survive_colorize(packet->usbInfo->so->codename), survive_colorize(packet->usbInfo->device_info->name), packet->usbInfo->nextCfgSubmitTime); int submit_transfer_error = survive_config_submit(packet->usbInfo); if (submit_transfer_error != 0) { SV_WARN("Config state machine could not submit transfer %d\n", submit_transfer_error); goto cleanup; } return; } cleanup: SV_VERBOSE(100, "Cleanup config for %s %s at %f %d/%d", survive_colorize_codename(packet->usbInfo->so), survive_colorize(packet->usbInfo->device_info->name), survive_run_time(ctx), usbInfo->interfaces[0].shutdown, (int)usbInfo->active_transfers); if (!usbInfo->interfaces[0].shutdown) { for (const struct Endpoint_t *endpoint = packet->usbInfo->device_info->endpoints; endpoint->name; endpoint++) { int errorCode = AttachInterface(packet->sv, packet->usbInfo, endpoint, packet->usbInfo->handle, survive_data_cb); if (errorCode < 0) { SV_WARN("Could not attach interface %s: %d", endpoint->name, errorCode); } } } packet->usbInfo->ignoreCnt = 10; packet->usbInfo->nextCfgSubmitTime = -1; packet->usbInfo->cfg_user = 0; packet->usbInfo->active_transfers--; if (usbInfo->interfaces[0].shutdown && usbInfo->active_transfers == 0) { usbInfo->request_close = true; SV_VERBOSE(100, "Acking close for %s", survive_colorize_codename(usbInfo->so)); } survive_usb_transfer_free(transfer); str_free(&packet->cfg); free(packet); } static void survive_config_poll(struct SurviveUSBInfo *usbInfo) { FLT now = survive_run_time(usbInfo->viveData->ctx); bool active = true; while (active || usbInfo->nextCfgSubmitTime >= 0 && usbInfo->nextCfgSubmitTime < now) { active = false; if (usbInfo->nextCfgSubmitTime >= 0 && usbInfo->nextCfgSubmitTime < now) { if (survive_config_submit(usbInfo) == -6) { usbInfo->active_transfers--; survive_close_usb_device(usbInfo); } } #ifdef HIDAPI struct survive_config_packet *config_packet = usbInfo->cfg_user; if (config_packet == 0) return; void (*cb)(struct survive_usb_transfer_t *) = config_packet->tx->cb; if (!config_packet->tx->processed && cb) { config_packet->tx->processed = true; cb(config_packet->tx); } #else #endif } } static void survive_config_cancel(struct survive_config_packet *config_packet) { survive_usb_transfer_cancel(config_packet->tx); }