diff --git a/server/fcdevice.cpp b/server/fcdevice.cpp index 82d9d5944f1d8f0909e66b5dd34c2d7ee37f1115..5d51a0397a1994ce202f17fbc06672551a75665d 100644 --- a/server/fcdevice.cpp +++ b/server/fcdevice.cpp @@ -28,9 +28,9 @@ #include <stdio.h> -FCDevice::Transfer::Transfer(FCDevice *device, void *buffer, int length) +FCDevice::Transfer::Transfer(FCDevice *device, void *buffer, int length, PacketType type) : transfer(libusb_alloc_transfer(0)), - device(device) + device(device), type(type) { libusb_fill_bulk_transfer(transfer, device->mHandle, OUT_ENDPOINT, (uint8_t*) buffer, length, FCDevice::completeTransfer, this, 2000); @@ -43,7 +43,7 @@ FCDevice::Transfer::~Transfer() FCDevice::FCDevice(libusb_device *device, bool verbose) : USBDevice(device, verbose), - mConfigMap(0) + mConfigMap(0), mNumFramesPending(0), mFrameWaitingForSubmit(false) { mSerial[0] = '\0'; @@ -143,7 +143,7 @@ void FCDevice::configureDevice(const Value &config) writeFirmwareConfiguration(); } -void FCDevice::submitTransfer(Transfer *fct) +bool FCDevice::submitTransfer(Transfer *fct) { /* * Submit a new USB transfer. The Transfer object is guaranteed to be freed eventually. @@ -157,8 +157,11 @@ void FCDevice::submitTransfer(Transfer *fct) std::clog << "Error submitting USB transfer: " << libusb_strerror(libusb_error(r)) << "\n"; } delete fct; + return false; + } else { mPending.insert(fct); + return true; } } @@ -173,6 +176,19 @@ void FCDevice::completeTransfer(struct libusb_transfer *transfer) FCDevice *self = fct->device; if (self) { + switch (fct->type) { + + case FRAME: + self->mNumFramesPending--; + if (self->mFrameWaitingForSubmit) { + self->writeFramebuffer(); + } + break; + + default: + break; + } + self->mPending.erase(fct); } @@ -268,11 +284,21 @@ void FCDevice::writeFramebuffer() * Asynchronously write the current framebuffer. * Note that the OS will copy our framebuffer at submit-time. * - * XXX: To-do, flow control. If more than one frame is pending, we need to be able to - * tell clients that we're going too fast, *or* we need to drop frames. + * TODO: Currently if this gets ahead of what the USB device is capable of, + * we always drop frames. Alternatively, it would be nice to have end-to-end + * flow control so that the client can produce frames slower. */ - submitTransfer(new Transfer(this, &mFramebuffer, sizeof mFramebuffer)); + if (mNumFramesPending >= 2) { + // Too many outstanding frames. Wait to submit until a previous frame completes. + mFrameWaitingForSubmit = true; + return; + } + + if (submitTransfer(new Transfer(this, &mFramebuffer, sizeof mFramebuffer, FRAME))) { + mFrameWaitingForSubmit = false; + mNumFramesPending++; + } } void FCDevice::writeMessage(const OPCSink::Message &msg) diff --git a/server/fcdevice.h b/server/fcdevice.h index 4f2ae18fa70b8c3dea8349eeb5f8ec0314d7a20a..a042c4c6a14a77d42640b5379b0181b55741fd5b 100644 --- a/server/fcdevice.h +++ b/server/fcdevice.h @@ -75,15 +75,23 @@ private: uint8_t data[63]; }; + enum PacketType { + OTHER = 0, + FRAME, + }; + struct Transfer { - Transfer(FCDevice *device, void *buffer, int length); + Transfer(FCDevice *device, void *buffer, int length, PacketType type = OTHER); ~Transfer(); libusb_transfer *transfer; FCDevice *device; + PacketType type; }; const Value *mConfigMap; std::set<Transfer*> mPending; + int mNumFramesPending; + bool mFrameWaitingForSubmit; char mSerial[256]; libusb_device_descriptor mDD; @@ -91,7 +99,7 @@ private: Packet mColorLUT[LUT_PACKETS]; Packet mFirmwareConfig; - void submitTransfer(Transfer *fct); + bool submitTransfer(Transfer *fct); void configureDevice(const Value &config); void writeFirmwareConfiguration(); static void completeTransfer(struct libusb_transfer *transfer);