void TransferPool::onTransferComplete(TransferPool::Transfer* t)
{
  if(t->transfer->status == LIBUSB_TRANSFER_CANCELLED)
  {
    t->setStopped(true);
    return;
  }

  // process data
  processTransfer(t->transfer);

  if(!enable_submit_)
  {
    t->setStopped(true);
    return;
  }

  // resubmit self
  int r = libusb_submit_transfer(t->transfer);

  if(r != LIBUSB_SUCCESS)
  {
    LOG_ERROR << "failed to submit transfer: " << WRITE_LIBUSB_ERROR(r);
    t->setStopped(true);
  }
}
bool TransferPool::submit()
{
  if(!enable_submit_)
  {
    LOG_WARNING << "transfer submission disabled!";
    return false;
  }

  size_t failcount = 0;
  for(size_t i = 0; i < transfers_.size(); ++i)
  {
    libusb_transfer *transfer = transfers_[i].transfer;
    transfers_[i].setStopped(false);

    int r = libusb_submit_transfer(transfer);

    if(r != LIBUSB_SUCCESS)
    {
      LOG_ERROR << "failed to submit transfer: " << WRITE_LIBUSB_ERROR(r);
      transfers_[i].setStopped(true);
      failcount++;
    }
  }

  if (failcount == transfers_.size())
  {
    LOG_ERROR << "all submissions failed. Try debugging with environment variable: LIBUSB_DEBUG=3.";
    return false;
  }

  return true;
}
UsbControl::ResultCode UsbControl::enablePowerStates()
{
  UsbControl::ResultCode code;
  int r;

  r = libusb_ext::set_feature(handle_, timeout_, libusb_ext::U1_ENABLE);
  CHECK_LIBUSB_RESULT(code, r) << "failed to enable power states U1! " << WRITE_LIBUSB_ERROR(r);

  if(code == Success)
  {
    r = libusb_ext::set_feature(handle_, timeout_, libusb_ext::U2_ENABLE);
    CHECK_LIBUSB_RESULT(code, r) << "failed to enable power states U2! " << WRITE_LIBUSB_ERROR(r);
  }

  return code;
}
UsbControl::ResultCode UsbControl::setPowerStateLatencies()
{
  int r = libusb_ext::set_sel(handle_, timeout_, 0x55, 0, 0x55, 0);

  UsbControl::ResultCode code;
  CHECK_LIBUSB_RESULT(code, r) << "failed to set power state latencies! " << WRITE_LIBUSB_ERROR(r);
  return code;
}
UsbControl::ResultCode UsbControl::setIsochronousDelay()
{
  int r = libusb_ext::set_isochronous_delay(handle_, timeout_);

  UsbControl::ResultCode code;
  CHECK_LIBUSB_RESULT(code, r) << "failed to set isochronous delay! " << WRITE_LIBUSB_ERROR(r);
  return code;
}
UsbControl::ResultCode UsbControl::setIrInterfaceState(UsbControl::State state)
{
  int alternate_setting = state == Enabled ? 1 : 0;
  int r = libusb_set_interface_alt_setting(handle_, IrInterfaceId, alternate_setting);

  UsbControl::ResultCode code;
  CHECK_LIBUSB_RESULT(code, r) << "failed to set ir interface state! " << WRITE_LIBUSB_ERROR(r);
  return code;
}
UsbControl::ResultCode UsbControl::setVideoTransferFunctionState(UsbControl::State state)
{
  bool suspend = state == Enabled ? false : true;
  int r = libusb_ext::set_feature_function_suspend(handle_, timeout_, suspend, suspend);

  UsbControl::ResultCode code;
  CHECK_LIBUSB_RESULT(code, r) << "failed to set video transfer function state! " << WRITE_LIBUSB_ERROR(r);
  return code;
}
UsbControl::ResultCode UsbControl::setConfiguration()
{
  UsbControl::ResultCode code = Success;
  int desired_config_id = 1;
  int current_config_id = -1;
  int r;

  r = libusb_get_configuration(handle_, &current_config_id);
  CHECK_LIBUSB_RESULT(code, r) << "failed to get configuration! " << WRITE_LIBUSB_ERROR(r);

  if(code == Success)
  {
    if(current_config_id != desired_config_id)
    {
      r = libusb_set_configuration(handle_, desired_config_id);
      CHECK_LIBUSB_RESULT(code, r) << "failed to set configuration! " << WRITE_LIBUSB_ERROR(r);
    }
  }

  return code;
}
void CommandTransaction::receive(CommandTransaction::Result& result)
{
  result.code = Success;
  result.length = 0;

  int r = libusb_bulk_transfer(handle_, inbound_endpoint_, result.data, result.capacity, &result.length, timeout_);

  if(r != LIBUSB_SUCCESS)
  {
    LOG_ERROR << "bulk transfer failed: " << WRITE_LIBUSB_ERROR(r);
    result.code = Error;
  }
}
UsbControl::ResultCode UsbControl::getIrMaxIsoPacketSize(int &size)
{
  size = 0;
  libusb_device *dev = libusb_get_device(handle_);
  int r = libusb_ext::get_max_iso_packet_size(dev, 1, 1, 0x84);

  if(r > LIBUSB_SUCCESS)
  {
    size = r;
    r = LIBUSB_SUCCESS;
  }

  UsbControl::ResultCode code;
  CHECK_LIBUSB_RESULT(code, r) << "failed to get max iso packet size! " << WRITE_LIBUSB_ERROR(r);
  return code;
}
CommandTransaction::ResultCode CommandTransaction::send(const CommandBase& command)
{
  ResultCode code = Success;

  int transferred_bytes = 0;
  int r = libusb_bulk_transfer(handle_, outbound_endpoint_, const_cast<uint8_t *>(command.data()), command.size(), &transferred_bytes, timeout_);

  if(r != LIBUSB_SUCCESS)
  {
    LOG_ERROR << "bulk transfer failed: " << WRITE_LIBUSB_ERROR(r);
    code = Error;
  }

  if((size_t)transferred_bytes != command.size())
  {
    LOG_ERROR << "sent number of bytes differs from expected number! expected: " << command.size() << " got: " << transferred_bytes;
    code = Error;
  }

  return code;
}
void TransferPool::cancel()
{
  for(TransferQueue::iterator it = transfers_.begin(); it != transfers_.end(); ++it)
  {
    int r = libusb_cancel_transfer(it->transfer);

    if(r != LIBUSB_SUCCESS && r != LIBUSB_ERROR_NOT_FOUND)
    {
      LOG_ERROR << "failed to cancel transfer: " << WRITE_LIBUSB_ERROR(r);
    }
  }

  for(;;)
  {
    libfreenect2::this_thread::sleep_for(libfreenect2::chrono::milliseconds(100));
    size_t stopped_transfers = 0;
    for(TransferQueue::iterator it = transfers_.begin(); it != transfers_.end(); ++it)
      stopped_transfers += it->getStopped();
    if (stopped_transfers == transfers_.size())
      break;
    LOG_INFO << "waiting for transfer cancellation";
    libfreenect2::this_thread::sleep_for(libfreenect2::chrono::milliseconds(1000));
  }
}
UsbControl::ResultCode UsbControl::releaseInterfaces()
{
  UsbControl::ResultCode code = Success;
  int r;

  r = libusb_release_interface(handle_, ControlAndRgbInterfaceId);
  CHECK_LIBUSB_RESULT(code, r) << "failed to release interface with ControlAndRgbInterfaceId(="<< ControlAndRgbInterfaceId << ")! " << WRITE_LIBUSB_ERROR(r);

  if(code == Success)
  {
    r = libusb_release_interface(handle_, IrInterfaceId);
    CHECK_LIBUSB_RESULT(code, r) << "failed to release interface with IrInterfaceId(="<< IrInterfaceId << ")! " << WRITE_LIBUSB_ERROR(r);
  }

  return code;
}