+++ /dev/null
-/*
- * DMX512 over USB (custom USB peripheral)
- *
- * (c) 2020 Martin Mares <mj@ucw.cz>
- */
-
-#undef LOCAL_DEBUG
-
-#include <ucw/lib.h>
-#include <ucw/mainloop.h>
-#include <ucw/stkstring.h>
-
-#include <stdio.h>
-#include <string.h>
-
-#include "ursaryd.h"
-#include "usb.h"
-#include "dmx-interface.h"
-
-static libusb_device_handle *dmx_dev;
-static bool dmx_iface_claimed;
-
-static void dmx_error(int usb_err, char *text);
-
-static struct libusb_transfer *dmx_write_xfer;
-static bool dmx_write_pending;
-static void dmx_sched_write(void);
-
-#define DMX_NUM_CHANNELS 4
-static byte dmx_pwm_state[DMX_NUM_CHANNELS];
-static bool dmx_pwm_dirty;
-
-static void dmx_write_done(struct libusb_transfer *xfer)
-{
- int len = xfer->actual_length;
- DBG("DMX: Write done: status %d, length %d", xfer->status, len);
-
- if (xfer->status != LIBUSB_TRANSFER_COMPLETED)
- return dmx_error(0, stk_printf("DMX write failed with status %d", xfer->status));
- if (len < xfer->length)
- msg(L_ERROR, "DMX partial write: %d out of %d", len, xfer->length);
-
- dmx_write_pending = 0;
- dmx_sched_write();
-}
-
-static void dmx_sched_write(void)
-{
- if (!dmx_dev || dmx_write_pending || !dmx_pwm_dirty)
- return;
-
- DBG("DMX: Submitting write");
- dmx_write_pending = 1;
- dmx_pwm_dirty = 0;
-
- struct libusb_transfer *xfer = dmx_write_xfer;
- byte *pkt = xfer->buffer;
- pkt[0] = 0;
- memcpy(pkt+1, dmx_pwm_state, DMX_NUM_CHANNELS);
- xfer->length = 1 + DMX_NUM_CHANNELS;
-
- int err;
- if ((err = libusb_submit_transfer(xfer)) < 0)
- dmx_error(err, "Cannot submit transfer");
-}
-
-void dmx_set_pwm(uint index, uint val)
-{
- ASSERT(index < DMX_NUM_CHANNELS);
- ASSERT(val < 256);
- if (dmx_pwm_state[index] != val)
- {
- dmx_pwm_state[index] = val;
- dmx_pwm_dirty = 1;
- dmx_sched_write();
- }
-}
-
-static void dmx_write_init(void)
-{
- DBG("DMX: Write init");
-
- dmx_write_xfer = libusb_alloc_transfer(0);
- libusb_fill_bulk_transfer(dmx_write_xfer, dmx_dev, 0x01, xmalloc(1+DMX_NUM_CHANNELS), 0, dmx_write_done, NULL, 1000);
-
- dmx_pwm_dirty = 1;
- dmx_sched_write();
-}
-
-static struct main_timer dmx_connect_timer;
-static struct main_hook dmx_error_hook;
-
-static void dmx_connect(struct main_timer *t)
-{
- timer_del(t);
- msg(L_DEBUG, "Looking for DMX interface");
- int err;
-
- libusb_device **dev_list;
- libusb_device *found_dev = NULL;
- ssize_t len = libusb_get_device_list(usb_ctx, &dev_list);
- for (ssize_t i=0; i < len; i++)
- {
- libusb_device *dev = dev_list[i];
- struct libusb_device_descriptor desc;
- if (libusb_get_device_descriptor(dev, &desc) >= 0 &&
- desc.idVendor == DMX_USB_VENDOR &&
- desc.idProduct == DMX_USB_PRODUCT)
- {
- msg(L_INFO, "DMX found at bus %d, addr %d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
- if (found_dev)
- {
- msg(L_ERROR, "Multiple DMX devices found. Using the first one.");
- break;
- }
- found_dev = libusb_ref_device(dev);
- }
- }
- libusb_free_device_list(dev_list, 1);
-
- if (!found_dev)
- {
- msg(L_INFO, "No DMX device found");
- timer_add_rel(t, 5000);
- return;
- }
-
- DBG("Initializing DMX");
-
- if ((err = libusb_open(found_dev, &dmx_dev)) < 0)
- return dmx_error(err, "libusb_open failed");
-
- if ((err = libusb_claim_interface(dmx_dev, 0)) < 0)
- return dmx_error(err, "libusb_claim_interface failed");
- dmx_iface_claimed = 1;
-
- dmx_write_init();
-}
-
-static int dmx_error_handler(struct main_hook *h)
-{
- DBG("DMX: Entered error handling hook");
- hook_del(h);
-
- if (dmx_dev)
- {
- if (dmx_write_xfer)
- {
- if (dmx_write_pending)
- {
- DBG("DMX: Cancelling pending write");
- libusb_cancel_transfer(dmx_write_xfer);
- dmx_write_pending = 0;
- }
- DBG("DMX: Tearing down write xfer");
- xfree(dmx_write_xfer->buffer);
- libusb_free_transfer(dmx_write_xfer);
- dmx_write_xfer = NULL;
- }
- if (dmx_iface_claimed)
- {
- DBG("DMX: Unclaiming interface");
- libusb_release_interface(dmx_dev, 0);
- dmx_iface_claimed = 0;
- }
- DBG("DMX: Resetting device");
- libusb_reset_device(dmx_dev);
- libusb_close(dmx_dev);
- dmx_dev = NULL;
- }
-
- DBG("DMX: Scheduling rescan after error");
- timer_add_rel(&dmx_connect_timer, 3000);
-
- return HOOK_IDLE;
-}
-
-static void dmx_error(int usb_err, char *text)
-{
- if (usb_err)
- msg(L_ERROR, "DMX: %s: error %d (%s)", text, usb_err, libusb_error_name(usb_err));
- else
- msg(L_ERROR, "DMX: %s", text);
-
- DBG("DMX: Scheduling error handling hook");
- hook_add(&dmx_error_hook);
-}
-
-bool dmx_is_ready(void)
-{
- return !!dmx_dev;
-}
-
-void dmx_init(void)
-{
- // Prepare error handling hook
- dmx_error_hook.handler = dmx_error_handler;
-
- // Schedule search for DMX USB device
- dmx_connect_timer.handler = dmx_connect;
- timer_add_rel(&dmx_connect_timer, 100);
-}