]> mj.ucw.cz Git - misc.git/blob - ursaryd/ut.c
Ursary: More PA pieces
[misc.git] / ursaryd / ut.c
1 #define LOCAL_DEBUG
2
3 #include <ucw/lib.h>
4 #include <ucw/bitops.h>
5 #include <ucw/clists.h>
6 #include <ucw/gary.h>
7 #include <ucw/mainloop.h>
8 #include <ucw/string.h>
9
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <sys/poll.h>
14 #include <sys/time.h>
15
16 #include <libusb.h>
17 #include <pulse/pulseaudio.h>
18
19 #include "ursaryd.h"
20
21 /*
22  *  Interface to Novation Nocturn
23  *
24  *  Protocol reverse-engineered by De Wet van Niekerk <dewert@gmail.com>,
25  *  see https://github.com/dewert/nocturn-linux-midi for inspiration.
26  */
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_init[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 static void usb_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_init[i] + 1, noct_init[i][0], &done, 5000)) < 0)
343         die("Cannot send init packets: error %d", err);
344       if (done != noct_init[i][0])
345         die("Partial send of init packet: %d < %d", done, noct_init[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 }
371
372 /*
373  *  Interface to PulseAudio
374  *
375  *  FIXME
376  */
377
378 static pa_context *pulse_ctx;
379
380 enum pulse_state {
381   PS_OFFLINE,
382   PS_SUBSCRIBE,
383   PS_GET_CLIENTS,
384   PS_GET_SINKS,
385   PS_GET_SINK_INPUTS,
386   PS_ONLINE,
387 };
388
389 static enum pulse_state pulse_state;
390 #define PULSE_STATE(s) do { pulse_state = s; DBG("Pulse: " #s); } while (0)
391
392 // Tracking of currently running asynchronous operations
393 struct pulse_op {
394   cnode n;
395   pa_operation *o;
396   bool is_init;
397 };
398
399 static clist pulse_op_list;
400
401 static struct pulse_op *pulse_op_new(void)
402 {
403   struct pulse_op *op = xmalloc_zero(sizeof(*op));
404   clist_add_tail(&pulse_op_list, &op->n);
405   return op;
406 }
407
408 static void pulse_op_done(struct pulse_op *op)
409 {
410   if (op->o)
411     pa_operation_unref(op->o);
412   clist_remove(&op->n);
413   xfree(op);
414 }
415
416 static void pulse_op_cancel_all(void)
417 {
418   struct pulse_op *op;
419   while (op = (struct pulse_op *) clist_head(&pulse_op_list))
420     {
421       DBG("Pulse: Cancelling pending operation");
422       pa_operation_cancel(op->o);
423       pulse_op_done(op);
424     }
425 }
426
427 #define PULSE_ASYNC_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->o = name(__VA_ARGS__, _op); } while (0)
428 #define PULSE_ASYNC_INIT_RUN(name, ...) do { struct pulse_op *_op = pulse_op_new(); _op->is_init = 1; _op->o = name(__VA_ARGS__, _op); } while (0)
429
430 static void pulse_sink_input_cb(pa_context *ctx UNUSED, const pa_sink_input_info *i, int eol, void *userdata)
431 {
432   struct pulse_op *op = userdata;
433
434   if (eol)
435     {
436       if (op->is_init)
437         PULSE_STATE(PS_ONLINE);
438       pulse_op_done(op);
439       return;
440     }
441
442   DBG("Pulse: SINK INPUT #%u: %s client=%d sink=%d", i->index, i->name, i->client, i->sink);
443 }
444
445 static void pulse_sink_cb(pa_context *ctx, const pa_sink_info *i, int eol, void *userdata)
446 {
447   struct pulse_op *op = userdata;
448
449   if (eol)
450     {
451       if (op->is_init)
452         {
453           PULSE_STATE(PS_GET_SINK_INPUTS);
454           PULSE_ASYNC_INIT_RUN(pa_context_get_sink_input_info_list, ctx, pulse_sink_input_cb);
455         }
456       pulse_op_done(op);
457       return;
458     }
459
460   DBG("Pulse: SINK #%u: %s (%s)", i->index, i->name, i->description);
461 }
462
463 static void pulse_client_cb(pa_context *ctx, const pa_client_info *i, int eol, void *userdata)
464 {
465   struct pulse_op *op = userdata;
466
467   if (eol)
468     {
469       if (op->is_init)
470         {
471           PULSE_STATE(PS_GET_SINKS);
472           PULSE_ASYNC_INIT_RUN(pa_context_get_sink_info_list, ctx, pulse_sink_cb);
473         }
474       pulse_op_done(op);
475       return;
476     }
477
478   DBG("Pulse: CLIENT #%u: %s mod=%u drv=%s", i->index, i->name, i->owner_module, i->driver);
479 }
480
481 static void pulse_subscribe_done_cb(pa_context *ctx, int success, void *userdata)
482 {
483   pulse_op_done(userdata);
484
485   if (!success)
486     msg(L_ERROR, "pa_context_subscribe failed: success=%d", success);
487
488   PULSE_STATE(PS_GET_CLIENTS);
489   PULSE_ASYNC_INIT_RUN(pa_context_get_client_info_list, ctx, pulse_client_cb);
490 }
491
492 static void pulse_event_cb(pa_context *ctx, pa_subscription_event_type_t type, uint32_t idx, void *userdata UNUSED)
493 {
494   DBG("Pulse: SUBSCRIBE EVENT type=%08x idx=%u", type, idx);
495
496   uns object = type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
497   uns action = type & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
498   switch (object)
499     {
500     case PA_SUBSCRIPTION_EVENT_CLIENT:
501       if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE)
502         PULSE_ASYNC_RUN(pa_context_get_client_info, ctx, idx, pulse_client_cb);
503       else if (action == PA_SUBSCRIPTION_EVENT_REMOVE)
504         DBG("Pulse: REMOVE CLIENT #%u", idx);
505       break;
506     case PA_SUBSCRIPTION_EVENT_SINK:
507       if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE)
508         PULSE_ASYNC_RUN(pa_context_get_sink_info_by_index, ctx, idx, pulse_sink_cb);
509       else if (action == PA_SUBSCRIPTION_EVENT_REMOVE)
510         DBG("Pulse: REMOVE SINK #%u", idx);
511       break;
512     case PA_SUBSCRIPTION_EVENT_SINK_INPUT:
513       if (action == PA_SUBSCRIPTION_EVENT_NEW || action == PA_SUBSCRIPTION_EVENT_CHANGE)
514         PULSE_ASYNC_RUN(pa_context_get_sink_input_info, ctx, idx, pulse_sink_input_cb);
515       else if (action == PA_SUBSCRIPTION_EVENT_REMOVE)
516         DBG("Pulse: REMOVE SINK INPUT #%u", idx);
517       break;
518     }
519 }
520
521 static void pulse_state_cb(pa_context *ctx, void *userdata UNUSED)
522 {
523   int state = pa_context_get_state(ctx);
524   DBG("Pulse: State callback, new state = %d", state);
525   if (state == PA_CONTEXT_READY)
526     {
527       if (pulse_state == PS_OFFLINE)
528         {
529           PULSE_STATE(PS_SUBSCRIBE);
530           pa_context_set_subscribe_callback(ctx, pulse_event_cb, NULL);
531           PULSE_ASYNC_INIT_RUN(pa_context_subscribe, ctx, PA_SUBSCRIPTION_MASK_ALL, pulse_subscribe_done_cb);
532         }
533     }
534   else
535     {
536       if (pulse_state != PS_OFFLINE)
537         {
538           PULSE_STATE(PS_OFFLINE);
539           pulse_op_cancel_all();
540         }
541     }
542 }
543
544 static void pulse_init(void)
545 {
546   pmain_init();
547   clist_init(&pulse_op_list);
548   pulse_ctx = pa_context_new(&pmain_api, "ursaryd");
549   pa_context_set_state_callback(pulse_ctx, pulse_state_cb, NULL);
550   pa_context_connect(pulse_ctx, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL);
551 }
552
553 int main(int argc UNUSED, char **argv)
554 {
555   log_init(argv[0]);
556   main_init();
557
558   // msg(L_INFO, "Initializing USB");
559   // usb_init();
560
561   msg(L_INFO, "Initializing PulseAudio");
562   pulse_init();
563
564   msg(L_INFO, "Entering main loop");
565   main_loop();
566
567   return 0;
568 }