From 5dcec198694e4bbe1b90aa8509e62dcc9a6bd134 Mon Sep 17 00:00:00 2001 From: Micah Elizabeth Scott <micah@scanlime.org> Date: Wed, 24 Jul 2013 09:58:00 -0700 Subject: [PATCH] Device management and hotplug --- server/fcdevice.cpp | 61 ++++++++++++++++++++++ server/fcdevice.h | 19 ++++++- server/fcserver.cpp | 122 ++++++++++++++++++++++++++++++++++++++++++-- server/fcserver.h | 5 ++ 4 files changed, 203 insertions(+), 4 deletions(-) diff --git a/server/fcdevice.cpp b/server/fcdevice.cpp index 6bcbddf..069c34f 100644 --- a/server/fcdevice.cpp +++ b/server/fcdevice.cpp @@ -22,3 +22,64 @@ */ #include "fcdevice.h" + + +FCDevice::FCDevice(libusb_device *device, bool verbose) + : mVerbose(verbose), + mDevice(libusb_ref_device(device)), + mHandle(0), + mConfig(0) +{ + mSerial[0] = '\0'; +} + +FCDevice::~FCDevice() +{ + if (mHandle) { + libusb_close(mHandle); + } + if (mDevice) { + libusb_unref_device(mDevice); + } +} + +bool FCDevice::isFadecandy() +{ + libusb_device_descriptor dd; + + if (libusb_get_device_descriptor(mDevice, &dd) < 0) { + // Can't access descriptor? + return false; + } + + return dd.idVendor == 0x1d50 && dd.idProduct == 0x607a; +} + +int FCDevice::open() +{ + int r = libusb_open(mDevice, &mHandle); + if (r < 0) { + return r; + } + + libusb_device_descriptor dd; + r = libusb_get_device_descriptor(mDevice, &dd); + if (r < 0) { + return r; + } + + return libusb_get_string_descriptor_ascii(mHandle, dd.iSerialNumber, (uint8_t*)mSerial, sizeof mSerial); +} + +void FCDevice::setConfiguration(const Value *config) +{ + mConfig = config; +} + +void FCDevice::writeColorCorrection(const Value &color) +{ +} + +void FCDevice::writeMessage(const OPCSink::Message &msg) +{ +} diff --git a/server/fcdevice.h b/server/fcdevice.h index 85368b0..283e8b3 100644 --- a/server/fcdevice.h +++ b/server/fcdevice.h @@ -22,16 +22,33 @@ */ #pragma once +#include "rapidjson/document.h" +#include "opcsink.h" #include <libusb.h> class FCDevice { public: - FCDevice(libusb_device *device); + typedef rapidjson::Value Value; + FCDevice(libusb_device *device, bool verbose = false); + ~FCDevice(); + + libusb_device *getDevice() { return mDevice; }; + bool isFadecandy(); int open(); + // Valid after open(): + + const char *getSerial() { return mSerial; } + void setConfiguration(const Value *config); + void writeColorCorrection(const Value &color); + void writeMessage(const OPCSink::Message &msg); + private: + bool mVerbose; libusb_device *mDevice; libusb_device_handle *mHandle; + const Value *mConfig; + char mSerial[256]; }; diff --git a/server/fcserver.cpp b/server/fcserver.cpp index f44867f..3f760e7 100644 --- a/server/fcserver.cpp +++ b/server/fcserver.cpp @@ -23,6 +23,7 @@ #include "util.h" #include "fcserver.h" +#include "fcdevice.h" #include <netdb.h> #include <ctype.h> #include <iostream> @@ -70,6 +71,14 @@ FCServer::FCServer(rapidjson::Document &config) } else { mError << "The required 'listen' configuration key must be a [host, port] list.\n"; } + + /* + * Minimal validation on 'devices' + */ + + if (!mDevices.IsArray()) { + mError << "The required 'devices' configuration key must be an array.\n"; + } } FCServer::~FCServer() @@ -108,9 +117,16 @@ void FCServer::startUSB(struct ev_loop *loop) void FCServer::cbMessage(OPCSink::Message &msg, void *context) { + /* + * Broadcast the OPC message to all configured devices. + */ + FCServer *self = static_cast<FCServer*>(context); - printf("Msg %d bytes\n", msg.length()); + for (std::vector<FCDevice*>::iterator i = self->mFCDevices.begin(), e = self->mFCDevices.end(); i != e; ++i) { + FCDevice *fcd = *i; + fcd->writeMessage(msg); + } } int FCServer::cbHotplug(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data) @@ -129,10 +145,110 @@ int FCServer::cbHotplug(libusb_context *ctx, libusb_device *device, libusb_hotpl void FCServer::usbDeviceArrived(libusb_device *device) { - // New USB device. Is this a device we recognize? + /* + * New USB device. Is this a device we recognize? + * + * Right now we're only looking for FCDevices, but in the future + * we can look for other types of USB devices here too. + */ + + FCDevice *fcd = new FCDevice(device); + if (!fcd->isFadecandy()) { + // Not a recognized device. + delete fcd; + return; + } + + int r = fcd->open(); + if (r < 0) { + if (mVerbose) { + std::clog << "Error opening Fadecandy USB device: " << libusb_strerror(libusb_error(r)) << "\n"; + } + delete fcd; + return; + } + + // Look for a matching device in the JSON + const Value *fcjson = matchFCDevice(fcd->getSerial()); + + if (mVerbose) { + std::clog << "USB Fadecandy attached, serial: \"" << fcd->getSerial() << "\""; + if (fcjson) { + std::clog << " (configuration found)\n"; + } else { + std::clog << " (not matched in config file)\n"; + } + } + + if (fcjson) { + // Store the configuration, use it for future messages + fcd->setConfiguration(fcjson); + } else { + delete fcd; + return; + } + + // Send the default color lookup table + fcd->writeColorCorrection(mColor); + + // Remember this device for later. It's now active, and we should broadcast messages to it. + mFCDevices.push_back(fcd); } void FCServer::usbDeviceLeft(libusb_device *device) { - + /* + * Is this a device we recognize? If so, delete it. + */ + + for (std::vector<FCDevice*>::iterator i = mFCDevices.begin(), e = mFCDevices.end(); i != e; ++i) { + FCDevice *fcd = *i; + if (fcd->getDevice() == device) { + if (mVerbose) { + std::clog << "USB Fadecandy removed, serial: \"" << fcd->getSerial() << "\"\n"; + } + mFCDevices.erase(i); + delete fcd; + break; + } + } +} + +const FCServer::Value *FCServer::matchFCDevice(const char *serial) +{ + /* + * Look for a record in mDevices that matches a Fadecandy board with the given serial number. + * Returns 0 if nothing matches. + */ + + for (unsigned i = 0; i < mDevices.Size(); ++i) { + const Value &v = mDevices[i]; + const Value &vtype = v["type"]; + const Value &vserial = v["serial"]; + + if (!vtype.IsString() || strcmp(vtype.GetString(), "fadecandy")) { + // Wrong type + continue; + } + + if (!vserial.IsNull()) { + // Not a wildcard serial number? + // If a serial was not specified, it matches any device. + + if (!vserial.IsString()) { + // Non-string serial number. Bad form. + continue; + } + + if (strcmp(vserial.GetString(), serial)) { + // Not a match + continue; + } + } + + // Match + return &v; + } + + return 0; } diff --git a/server/fcserver.h b/server/fcserver.h index 3a7178b..1a6921a 100644 --- a/server/fcserver.h +++ b/server/fcserver.h @@ -24,9 +24,11 @@ #pragma once #include "rapidjson/document.h" #include "opcsink.h" +#include "fcdevice.h" #include "libusbev.h" #include <libusb.h> #include <sstream> +#include <vector> #include <ev.h> #include <netinet/in.h> #include <netdb.h> @@ -58,10 +60,13 @@ private: libusb_context *mUSB; LibUSBEventBridge mUSBEvent; + std::vector<FCDevice*> mFCDevices; + static void cbMessage(OPCSink::Message &msg, void *context); static int cbHotplug(libusb_context *ctx, libusb_device *device, libusb_hotplug_event event, void *user_data); void startUSB(struct ev_loop *loop); void usbDeviceArrived(libusb_device *device); void usbDeviceLeft(libusb_device *device); + const Value *matchFCDevice(const char *serial); }; -- GitLab