2 * DMX512 over USB (custom USB peripheral)
4 * (c) 2020 Martin Mares <mj@ucw.cz>
10 #include <ucw/mainloop.h>
11 #include <ucw/stkstring.h>
18 #include "dmx-interface.h"
20 static libusb_device_handle *dmx_dev;
21 static bool dmx_iface_claimed;
23 static void dmx_error(int usb_err, char *text);
25 static struct libusb_transfer *dmx_write_xfer;
26 static bool dmx_write_pending;
27 static void dmx_sched_write(void);
29 #define DMX_NUM_CHANNELS 4
30 static byte dmx_pwm_state[DMX_NUM_CHANNELS];
31 static bool dmx_pwm_dirty;
33 static void dmx_write_done(struct libusb_transfer *xfer)
35 int len = xfer->actual_length;
36 DBG("DMX: Write done: status %d, length %d", xfer->status, len);
38 if (xfer->status != LIBUSB_TRANSFER_COMPLETED)
39 return dmx_error(0, stk_printf("DMX write failed with status %d", xfer->status));
40 if (len < xfer->length)
41 msg(L_ERROR, "DMX partial write: %d out of %d", len, xfer->length);
43 dmx_write_pending = 0;
47 static void dmx_sched_write(void)
49 if (!dmx_dev || dmx_write_pending || !dmx_pwm_dirty)
52 DBG("DMX: Submitting write");
53 dmx_write_pending = 1;
56 struct libusb_transfer *xfer = dmx_write_xfer;
57 byte *pkt = xfer->buffer;
59 memcpy(pkt+1, dmx_pwm_state, DMX_NUM_CHANNELS);
60 xfer->length = 1 + DMX_NUM_CHANNELS;
63 if ((err = libusb_submit_transfer(xfer)) < 0)
64 dmx_error(err, "Cannot submit transfer");
67 void dmx_set_pwm(uint index, uint val)
69 ASSERT(index < DMX_NUM_CHANNELS);
71 if (dmx_pwm_state[index] != val)
73 dmx_pwm_state[index] = val;
79 static void dmx_write_init(void)
81 DBG("DMX: Write init");
83 dmx_write_xfer = libusb_alloc_transfer(0);
84 libusb_fill_bulk_transfer(dmx_write_xfer, dmx_dev, 0x01, xmalloc(1+DMX_NUM_CHANNELS), 0, dmx_write_done, NULL, 1000);
90 static struct main_timer dmx_connect_timer;
91 static struct main_hook dmx_error_hook;
93 static void dmx_connect(struct main_timer *t)
96 msg(L_DEBUG, "Looking for DMX interface");
99 libusb_device **dev_list;
100 libusb_device *found_dev = NULL;
101 ssize_t len = libusb_get_device_list(usb_ctx, &dev_list);
102 for (ssize_t i=0; i < len; i++)
104 libusb_device *dev = dev_list[i];
105 struct libusb_device_descriptor desc;
106 if (libusb_get_device_descriptor(dev, &desc) >= 0 &&
107 desc.idVendor == DMX_USB_VENDOR &&
108 desc.idProduct == DMX_USB_PRODUCT)
110 msg(L_INFO, "DMX found at bus %d, addr %d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
113 msg(L_ERROR, "Multiple DMX devices found. Using the first one.");
116 found_dev = libusb_ref_device(dev);
119 libusb_free_device_list(dev_list, 1);
123 msg(L_INFO, "No DMX device found");
124 timer_add_rel(t, 5000);
128 DBG("Initializing DMX");
130 if ((err = libusb_open(found_dev, &dmx_dev)) < 0)
131 return dmx_error(err, "libusb_open failed");
133 if ((err = libusb_claim_interface(dmx_dev, 0)) < 0)
134 return dmx_error(err, "libusb_claim_interface failed");
135 dmx_iface_claimed = 1;
140 static int dmx_error_handler(struct main_hook *h)
142 DBG("DMX: Entered error handling hook");
149 if (dmx_write_pending)
151 DBG("DMX: Cancelling pending write");
152 libusb_cancel_transfer(dmx_write_xfer);
153 dmx_write_pending = 0;
155 DBG("DMX: Tearing down write xfer");
156 xfree(dmx_write_xfer->buffer);
157 libusb_free_transfer(dmx_write_xfer);
158 dmx_write_xfer = NULL;
160 if (dmx_iface_claimed)
162 DBG("DMX: Unclaiming interface");
163 libusb_release_interface(dmx_dev, 0);
164 dmx_iface_claimed = 0;
166 DBG("DMX: Resetting device");
167 libusb_reset_device(dmx_dev);
168 libusb_close(dmx_dev);
172 DBG("DMX: Scheduling rescan after error");
173 timer_add_rel(&dmx_connect_timer, 3000);
178 static void dmx_error(int usb_err, char *text)
181 msg(L_ERROR, "DMX: %s: error %d (%s)", text, usb_err, libusb_error_name(usb_err));
183 msg(L_ERROR, "DMX: %s", text);
185 DBG("DMX: Scheduling error handling hook");
186 hook_add(&dmx_error_hook);
189 bool dmx_is_ready(void)
196 // Prepare error handling hook
197 dmx_error_hook.handler = dmx_error_handler;
199 // Schedule search for DMX USB device
200 dmx_connect_timer.handler = dmx_connect;
201 timer_add_rel(&dmx_connect_timer, 100);