]> mj.ucw.cz Git - ursary.git/blob - nocturn.c
8ed7b1835eb51ae2b96180a8482915f48892adfa
[ursary.git] / nocturn.c
1 /*
2  *      Interface to Novation Nocturn
3  *
4  *      (c) 2014 Martin Mares <mj@ucw.cz>
5  *
6  *      Protocol reverse-engineered by De Wet van Niekerk <dewert@gmail.com>,
7  *      see https://github.com/dewert/nocturn-linux-midi for inspiration.
8  */
9
10 #define LOCAL_DEBUG
11
12 #include <ucw/lib.h>
13 #include <ucw/bitops.h>
14 #include <ucw/clists.h>
15 #include <ucw/gary.h>
16 #include <ucw/mainloop.h>
17 #include <ucw/string.h>
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <stdlib.h>
22 #include <sys/poll.h>
23
24 #include <libusb.h>
25
26 #include "ursaryd.h"
27
28 static libusb_context *usb_ctx;
29 static libusb_device_handle *usb_dev;
30
31 static struct main_file **usb_fds;
32
33 static int usb_fd_ready(struct main_file *f UNUSED)
34 {
35   DBG("USB: Handling events (ready on fd %d)", f->fd);
36   struct timeval tv = { 0, 0 };
37   int comp = 0;
38   int err = libusb_handle_events_timeout_completed(usb_ctx, &tv, &comp);
39   if (err < 0)
40     msg(L_ERROR, "libusb_handle_events: error %d", err);
41   return HOOK_IDLE;
42 }
43
44 static void usb_added_fd(int fd, short events, void *user_data UNUSED)
45 {
46   if (fd >= (int) GARY_SIZE(usb_fds))
47     GARY_RESIZE(usb_fds, fd + 1);
48
49   struct main_file *f = usb_fds[fd];
50   if (!f)
51     {
52       f = xmalloc_zero(sizeof(*f));
53       usb_fds[fd] = f;
54     }
55   else if (file_is_active(f))
56     {
57       DBG("USB: Releasing fd %d", fd);
58       file_del(f);
59     }
60
61   DBG("USB: Adding fd %d with event mask %u", fd, events);
62   f->fd = fd;
63   f->read_handler = (events & POLLIN) ? usb_fd_ready : NULL;
64   f->write_handler = (events & POLLOUT) ? usb_fd_ready : NULL;
65   file_add(f);
66 }
67
68 static void usb_removed_fd(int fd, void *user_data UNUSED)
69 {
70   DBG("USB: Releasing fd %d", fd);
71   ASSERT(fd < (int) GARY_SIZE(usb_fds));
72   struct main_file *f = usb_fds[fd];
73   ASSERT(f);
74   ASSERT(file_is_active(f));
75   file_del(f);
76 }
77
78 static const char noct_magic[4][9] = {
79   { 3, 0xb0, 0x00, 0x00 },
80   { 8, 0x28, 0x00, 0x2b, 0x4a, 0x2c, 0x00, 0x2e, 0x35 },
81   { 6, 0x2a, 0x02, 0x2c, 0x72, 0x2e, 0x30 },
82   { 2, 0x7f, 0x00 },
83 };
84
85 static void noct_read_done(struct libusb_transfer *xfer)
86 {
87   byte *pkt = xfer->buffer;
88   int len = xfer->actual_length;
89   DBG("USB: Read done: status %d, length %d", xfer->status, len);
90
91   if (xfer->status != LIBUSB_TRANSFER_COMPLETED)
92     {
93       msg(L_ERROR, "USB read failed with status %d, not submitting again", xfer->status);
94       return;
95     }
96
97 #ifdef LOCAL_DEBUG
98   char buf[256];
99   mem_to_hex(buf, pkt, len, ' ');
100   DBG("USB: Read <%s>", buf);
101 #endif
102
103   int i = 0;
104   while (i < len)
105     {
106       if (i + 3 > len)
107         {
108           msg(L_ERROR, "Unknown USB packet: length %d not divisible by 3", len);
109           break;
110         }
111       if (pkt[i] != 0xb0)
112         {
113           msg(L_ERROR, "Unknown USB packet: expected 0xb0 at position %d", i);
114           break;
115         }
116       int cmd = pkt[i+1];
117       int arg = pkt[i+2];
118       i += 3;
119       switch (cmd)
120         {
121         case 0x30:
122           // Unknown packet sent during init
123           continue;
124         case 0x40 ... 0x47:
125           if (arg < 0x80)
126             {
127               int r = cmd - 0x40;
128               int delta = (arg < 0x40 ? arg : arg - 0x80);
129               DBG("Noct: Rotary %d = %d", r, delta);
130               continue;
131             }
132           break;
133         case 0x48:
134           if (arg < 0x80)
135             {
136               DBG("Noct: Slider value = %d", arg);
137               continue;
138             }
139           break;
140         case 0x49:
141           // Unknown packet, maybe least significant bit of slider
142           continue;
143         case 0x4a:
144           if (arg < 0x80)
145             {
146               int delta = (arg < 0x40 ? arg : arg - 0x80);
147               DBG("Noct: Center = %d", delta);
148               continue;
149             }
150           break;
151         case 0x52:
152           if (arg == 0x00 || arg == 0x7f)
153             {
154               int state = !!arg;
155               DBG("Noct: Center touch = %d", state);
156               continue;
157             }
158           break;
159         case 0x53:
160           if (arg == 0x00 || arg == 0x7f)
161             {
162               int state = !!arg;
163               DBG("Noct: Slider touch = %d", state);
164               continue;
165             }
166           break;
167         case 0x60 ... 0x67:
168           if (arg == 0x00 || arg == 0x7f)
169             {
170               int r = cmd - 0x60;
171               int state = !!arg;
172               DBG("Noct: Rotary %d touch = %d", r, state);
173               continue;
174             }
175           break;
176         case 0x70 ... 0x7f:
177           if (arg == 0x00 || arg == 0x7f)
178             {
179               int b = cmd - 0x70;
180               int state = !!arg;
181               DBG("Noct: Button %d = %d", b, state);
182               continue;
183             }
184           break;
185         }
186       msg(L_ERROR, "Unknown USB packet: unrecognized cmd=%02x arg=%02x", cmd, arg);
187     }
188
189   int err;
190   if ((err = libusb_submit_transfer(xfer)) < 0)
191     die("Cannot submit transfer: error %d", err);
192 }
193
194 static void noct_read_init(void)
195 {
196   DBG("Noct: Read init");
197
198   struct libusb_transfer *xfer = libusb_alloc_transfer(0);
199   libusb_fill_interrupt_transfer(xfer, usb_dev, 0x81, xmalloc(8), 8, noct_read_done, NULL, 0);
200
201   int err;
202   if ((err = libusb_submit_transfer(xfer)) < 0)
203     die("Cannot submit transfer: error %d", err);
204 }
205
206 static byte noct_button_state[16];
207 static byte noct_ring_mode[8];          // 0=from-min, 1=from-max, 2=from-mid-right, 3=from-mid-both, 4=single-on, 5=single-off
208 static byte noct_ring_val[9];
209
210 static uns noct_dirty_button;
211 static uns noct_dirty_ring_mode;
212 static uns noct_dirty_ring_val;
213
214 static struct libusb_transfer *noct_write_xfer;
215 static uns noct_write_pending;
216 static void noct_sched_write(void);
217
218 static void noct_write_done(struct libusb_transfer *xfer)
219 {
220   int len = xfer->actual_length;
221   DBG("USB: Write done: status %d, length %d", xfer->status, len);
222
223   if (xfer->status != LIBUSB_TRANSFER_COMPLETED)
224     {
225       msg(L_ERROR, "USB write failed with status %d", xfer->status);
226       return;
227     }
228
229   noct_write_pending = 0;
230   noct_sched_write();
231 }
232
233 static void noct_do_write(uns cmd, uns arg)
234 {
235   DBG("USB: Submitting write %02x %02x", cmd, arg);
236   ASSERT(!noct_write_pending);
237   noct_write_pending = 1;
238
239   struct libusb_transfer *xfer = noct_write_xfer;
240   byte *pkt = xfer->buffer;
241   pkt[0] = cmd;
242   pkt[1] = arg;
243   xfer->length = 2;
244
245   int err;
246   if ((err = libusb_submit_transfer(xfer)) < 0)
247     die("Cannot submit transfer: error %d", err);
248 }
249
250 static void noct_sched_write(void)
251 {
252   if (noct_write_pending)
253     return;
254
255   if (noct_dirty_button)
256     {
257       int i = bit_ffs(noct_dirty_button);
258       noct_dirty_button ^= 1U << i;
259       noct_do_write(0x70 + i, noct_button_state[i]);
260     }
261   else if (noct_dirty_ring_mode)
262     {
263       int i = bit_ffs(noct_dirty_ring_mode);
264       noct_dirty_ring_mode ^= 1U << i;
265       noct_do_write(0x48 + i, noct_ring_mode[i] << 4);
266     }
267   else if (noct_dirty_ring_val)
268     {
269       int i = bit_ffs(noct_dirty_ring_val);
270       noct_dirty_ring_val ^= 1U << i;
271       if (i == 8)
272         noct_do_write(0x50 + i, noct_ring_val[i]);
273       else
274         noct_do_write(0x40 + i, noct_ring_val[i]);
275     }
276 }
277
278 static void noct_write_init(void)
279 {
280   DBG("Noct: Write init");
281
282   noct_write_xfer = libusb_alloc_transfer(0);
283   libusb_fill_interrupt_transfer(noct_write_xfer, usb_dev, 0x02, xmalloc(8), 0, noct_write_done, NULL, 1000);
284
285 #if 0 // FIXME
286   noct_button_state[2] = 1;
287   noct_ring_mode[0] = 4;
288   noct_ring_val[0] = 0x40;
289 #endif
290
291   noct_dirty_button = 0xffff;
292   noct_dirty_ring_mode = 0xff;
293   noct_dirty_ring_val = 0x1ff;
294   noct_sched_write();
295 }
296
297 void noct_init(void)
298 {
299   int err;
300
301   if ((err = libusb_init(&usb_ctx)) < 0)
302     die("libusb_init failed: error %d", err);
303   libusb_set_debug(usb_ctx, 3);
304
305   libusb_device **dev_list;
306   libusb_device *found_dev = NULL;
307   ssize_t len = libusb_get_device_list(usb_ctx, &dev_list);
308   for (ssize_t i=0; i < len; i++)
309     {
310       libusb_device *dev = dev_list[i];
311       struct libusb_device_descriptor desc;
312       if (libusb_get_device_descriptor(dev, &desc) >= 0 &&
313           desc.idVendor == 0x1235 &&
314           desc.idProduct == 0x000a)
315         {
316           msg(L_DEBUG, "Found device: bus %d, addr %d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
317           if (found_dev)
318             die("Multiple Nocturn devices found. Please fix me to handle it.");
319           found_dev = libusb_ref_device(dev);
320         }
321     }
322   libusb_free_device_list(dev_list, 1);
323
324   if (!found_dev)
325     die("No Nocturn device found");
326
327   msg(L_DEBUG, "Initializing device");
328
329   if ((err = libusb_open(found_dev, &usb_dev)) < 0)
330     die("libusb_open failed: error %d", err);
331
332   // There exist configurations 1 (high brightness) and 2 (power-save)
333   if ((err = libusb_set_configuration(usb_dev, 1)) < 0)
334     die("libusb_set_configuration: error %d", err);
335
336   if ((err = libusb_claim_interface(usb_dev, 0)) < 0)
337     die("libusb_claim_interface: error %d", err);
338
339   for (int i=0; i<4; i++)
340     {
341       int done;
342       if ((err = libusb_interrupt_transfer(usb_dev, 0x02, (byte *) noct_magic[i] + 1, noct_magic[i][0], &done, 5000)) < 0)
343         die("Cannot send init packets: error %d", err);
344       if (done != noct_magic[i][0])
345         die("Partial send of init packet: %d < %d", done, noct_magic[i][0]);
346     }
347
348 #if 0
349   byte xxx[] = { 0x7f, 0x01 };
350   int done;
351   libusb_interrupt_transfer(usb_dev, 0x02, xxx, 2, &done, 5000);
352 #endif
353
354   DBG("USB: Connecting libusb to mainloop");
355
356   if (!libusb_pollfds_handle_timeouts(usb_ctx))
357     die("Unsupported version of libusb, please fix me");
358
359   GARY_INIT_ZERO(usb_fds, 0);
360   libusb_set_pollfd_notifiers(usb_ctx, usb_added_fd, usb_removed_fd, NULL);
361
362   const struct libusb_pollfd **fds = libusb_get_pollfds(usb_ctx);
363   ASSERT(fds);
364   for (int i=0; fds[i]; i++)
365     usb_added_fd(fds[i]->fd, fds[i]->events, NULL);
366   free(fds);
367
368   noct_read_init();
369   noct_write_init();
370 }