1 /*
2 * Copyright 2003-2016, Björn Ståhl
3 * License: 3-Clause BSD, see COPYING file in arcan source repository.
4 * Reference: http://arcan-fe.com
5 * Description: LED controller interface,
6 *
7 * By defining LED_STANDALONE it can be built separately from the
8 * main engine to allow other projects to just re-use the LED control
9 * interface.
10 *
11 * Setting the 'ext_led' database config value to a path name pointing
12 * to an existing named pipe will have the engine map that as an
13 * external led controller that speaks the protocol mentioned in the
14 * header.
15 */
16 #include <stdlib.h>
17 #include <stdint.h>
18 #include <stdio.h>
19 #include <stdbool.h>
20 #include <string.h>
21 #include <errno.h>
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27
28 #ifdef USB_SUPPORT
29 #include <hidapi/hidapi.h>
30 #else
31 typedef struct hid_device hid_device;
32 #endif
33
34 #include "arcan_led.h"
35 #ifndef LED_STANDALONE
36 #include "arcan_math.h"
37 #include "arcan_general.h"
38 #include "arcan_db.h"
39 #else
40 #define COUNT_OF(x) \
41 ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
42 #endif
43
44 /* to add more device types,
45 * 1. patch in the type in the controller_types enum.
46 * 2. for usb-devices, add a vid/pid to type mapping in the usb_tbl
47 * or alter the init function to actually scan for it and if found,
48 * forcecontroller (get the returned index and patch type).
49 * 3. modify the places marked as PROTOCOL INSERTION POINT
50 */
51 enum controller_types {
52 PACDRIVE = 1,
53 ARCAN_LEDCTRL = 2
54 };
55
56 struct led_controller {
57 enum controller_types type;
58 struct led_capabilities caps;
59 uint64_t devid;
60 int errc;
61
62 union {
63 hid_device* handle;
64 int fd;
65 };
66
67 /* some need a big table, others do fine with a simple bitmask */
68 uint32_t table[256];
69 uint16_t ledmask;
70
71 /* for FIFOs, we may need to try open the path during writes */
72 char* path;
73 bool no_close;
74 };
75
76 struct usb_ent {
77 uint16_t vid, pid;
78 enum controller_types type;
79 struct led_capabilities caps;
80 char label[16];
81 };
82
83 /* one of the C fuglyness, static const struct led_capabilities paccap would
84 * still yield that the initialization is not constant */
85 #define paccap { .variable_brightness = false, .rgb = false, .nleds = 32 }
86
87 /*
88 * nleds is just max, not all of these will map due to layout variations
89 * that don't necessarily (?) has a different vid/pid pair
90 */
91 static const struct usb_ent usb_tbl[] =
92 {
93 {.vid = 0xd209, .pid = 0x1500, .type = PACDRIVE,
94 .caps = paccap, .label = "Pacdrive"},
95 {.vid = 0xd209, .pid = 0x1501, .type = PACDRIVE,
96 .caps = paccap, .label = "Pacdrive"},
97 {.vid = 0xd209, .pid = 0x1502, .type = PACDRIVE,
98 .caps = paccap, .label = "Pacdrive"},
99 {.vid = 0xd209, .pid = 0x1503, .type = PACDRIVE,
100 .caps = paccap, .label = "Pacdrive"},
101 {.vid = 0xd209, .pid = 0x1504, .type = PACDRIVE,
102 .caps = paccap, .label = "Pacdrive"},
103 {.vid = 0xd209, .pid = 0x1505, .type = PACDRIVE,
104 .caps = paccap, .label = "Pacdrive"},
105 {.vid = 0xd209, .pid = 0x1506, .type = PACDRIVE,
106 .caps = paccap, .label = "Pacdrive"},
107 {.vid = 0xd209, .pid = 0x1507, .type = PACDRIVE,
108 .caps = paccap, .label = "Pacdrive"},
109 {.vid = 0xd209, .pid = 0x1508, .type = PACDRIVE,
110 .caps = paccap, .label = "Pacdrive"}
111 };
112 #undef paccap
113
114 /*
115 * not worth spending time on dynamic array management,
116 * just limit to 64, have index % 64 to skip overflow checks
117 */
118 #define MAX_LED_CONTROLLERS 64
119 static struct led_controller controllers[MAX_LED_CONTROLLERS] = {0};
120 static uint64_t ctrl_mask;
121 static int n_controllers = 0;
122
find_free_ind()123 static int find_free_ind()
124 {
125 for (size_t i = 0; i < MAX_LED_CONTROLLERS; i++)
126 if ((ctrl_mask & (1 << i)) == 0)
127 return i;
128 return -1;
129 }
130
get_device(uint8_t devind)131 static struct led_controller* get_device(uint8_t devind)
132 {
133 if (devind > MAX_LED_CONTROLLERS ||
134 (ctrl_mask & (1 << devind)) == 0)
135 return NULL;
136
137 return &controllers[devind];
138 }
139
find_devid(uint64_t devid)140 static struct led_controller* find_devid(uint64_t devid)
141 {
142 if (devid == 0)
143 return NULL;
144
145 for (size_t i = 0; i < MAX_LED_CONTROLLERS; i++)
146 if ((ctrl_mask & ((size_t)1 << i)) && controllers[i].devid == devid)
147 return &controllers[i];
148
149 return NULL;
150 }
151
forcecontroller(const struct usb_ent * ent)152 static void forcecontroller(const struct usb_ent* ent)
153 {
154 int ind = find_free_ind();
155 if (-1 == ind)
156 return;
157
158 /* the USB standard doesn't mandate a serial number (facepalm)
159 * and hidraw doesn't really have something akin to an instance id */
160
161 uint64_t devid = (
162 (uint64_t)(ent->vid & 0xffff) << 16) | ((uint64_t)ent->pid & 0xffff);
163 /* other option would be to close and reopen the device as an attempt
164 * to reset to a possibly safer state */
165 if (find_devid(devid))
166 return;
167
168 #ifdef USB_SUPPORT
169 controllers[ind].handle = hid_open(ent->vid, ent->pid, NULL);
170 controllers[ind].type = ent->type;
171 controllers[ind].caps = ent->caps;
172 if (controllers[ind].handle == NULL)
173 return;
174
175 /* PROTOCOL_INSERTION_POINT */
176 switch(ent->type){
177 default:
178 break;
179 }
180 #else
181 return;
182 #endif
183
184 ctrl_mask |= 1 << ind;
185 n_controllers++;
186 arcan_led_added(ind, -1, ent->label);
187 }
188
arcan_led_register(int cmd_ch,int devref,const char * label,struct led_capabilities caps)189 int8_t arcan_led_register(int cmd_ch, int devref,
190 const char* label, struct led_capabilities caps)
191 {
192 int8_t id = find_free_ind();
193 if (-1 == id)
194 return -1;
195
196 ctrl_mask |= 1 << id;
197 controllers[id].devid = id;
198 controllers[id].fd = cmd_ch;
199 controllers[id].type = ARCAN_LEDCTRL;
200 controllers[id].ledmask = 0;
201 controllers[id].caps = caps;
202 controllers[id].errc = 0;
203 controllers[id].no_close = false;
204
205 n_controllers++;
206 arcan_led_added(id, devref, label);
207 return id;
208 }
209
write_leddev(struct led_controller * dev,int ind,uint8_t * buf,size_t sz)210 static int write_leddev(
211 struct led_controller* dev, int ind, uint8_t* buf, size_t sz)
212 {
213 int rv;
214
215 if (-1 == dev->fd){
216 dev->fd = open(dev->path, O_NONBLOCK | O_WRONLY);
217 if (-1 == dev->fd)
218 return 0;
219 }
220
221 while ((rv = write(dev->fd, buf, sz)) == -1){
222 if (errno == EAGAIN)
223 return 0;
224
225 if (errno == EPIPE){
226 if (!dev->no_close){
227 arcan_led_remove(ind);
228 return -1;
229 }
230 else
231 break;
232 }
233 }
234
235 if (rv == sz)
236 return 1;
237
238 return 0;
239 }
240
arcan_led_remove(uint8_t device)241 bool arcan_led_remove(uint8_t device)
242 {
243 struct led_controller* leddev = get_device(device);
244 if (!leddev)
245 return false;
246
247 switch(leddev->type){
248 case ARCAN_LEDCTRL:
249 break;
250 default:
251 #ifdef USB_SUPPORT
252 if (leddev->handle)
253 hid_close(leddev->handle);
254 #endif
255 break;
256 }
257 ctrl_mask &= ~(1 << device);
258 n_controllers--;
259 leddev->handle = NULL;
260 arcan_led_removed(device);
261
262 return true;
263 }
264
arcan_led_known(uint16_t vid,uint16_t pid)265 bool arcan_led_known(uint16_t vid, uint16_t pid)
266 {
267 for (size_t i = 0; i < COUNT_OF(usb_tbl); i++)
268 if (usb_tbl[i].vid == vid && usb_tbl[i].pid == pid)
269 return true;
270
271 return false;
272 }
273
274 #ifndef LED_STANDALONE
register_fifo(char * path,int ind)275 static bool register_fifo(char* path, int ind)
276 {
277 if (ind < 0 || ind > 99)
278 return false;
279
280 /* label will only be used for the event to the scripting layer, no ref */
281 char buf[ sizeof("(led-fifo 10)") ];
282 snprintf(buf, sizeof(buf), "(led-fifo %d)", ind);
283 int ledid = arcan_led_register(-1, -1, buf,
284 (struct led_capabilities){
285 .nleds = 255,
286 .variable_brightness = true,
287 .rgb = true
288 }
289 );
290
291 /* delete- protect the LED so we won't close on EPIPE */
292 if (-1 != ledid){
293 controllers[ledid].no_close = true;
294 controllers[ledid].path = path;
295 return true;
296 }
297 return false;
298 }
299 #endif
300
arcan_led_init()301 void arcan_led_init()
302 {
303
304 /* register up to 10 custom LED devices that follow our FIFO protocol.
305 * leddev- takes control over appl_val dynamic string ownership */
306 #ifndef LED_STANDALONE
307 const char* appl;
308 struct arcan_dbh* dbh = arcan_db_get_shared(&appl);
309
310 char* kv = arcan_db_appl_val(dbh, appl, "ext_led");
311 if (kv && register_fifo(kv, 1)){
312 char work[sizeof("ext_led_1")];
313
314 for (size_t i = 2; i < 10; i++){
315 snprintf(work, sizeof(work), "ext_led_%zu", i);
316 kv = arcan_db_appl_val(dbh, appl, work);
317 if (!kv || !register_fifo(kv, i)){
318 free(kv);
319 }
320 }
321 }
322 else
323 free(kv);
324 #endif
325
326 for (size_t i = 0; i < sizeof(usb_tbl) / sizeof(usb_tbl[0]); i++)
327 forcecontroller(&usb_tbl[i]);
328 }
329
arcan_led_controllers()330 uint64_t arcan_led_controllers()
331 {
332 return ctrl_mask;
333 }
334
ultimarc_update(uint8_t device,struct led_controller * ctrl)335 static void ultimarc_update(uint8_t device, struct led_controller* ctrl)
336 {
337 #ifdef USB_SUPPORT
338 uint8_t dbuf[] = {
339 0x00, 0x00, 0xdd, (char)ctrl->ledmask, (char)(ctrl->ledmask >> 8) & 0xff};
340 int val = hid_write(ctrl->handle, dbuf, sizeof(dbuf) / sizeof(dbuf[0]));
341 if (-1 == val){
342 ctrl->errc++;
343 if (ctrl->errc > 10){
344 arcan_led_remove(device);
345 }
346 }
347 #endif
348 }
349
arcan_led_intensity(uint8_t device,int16_t led,uint8_t intensity)350 int arcan_led_intensity(uint8_t device, int16_t led, uint8_t intensity)
351 {
352 bool rv = false;
353 struct led_controller* leddev = get_device(device);
354 uint8_t di = led < 0 ? 255 : led;
355 if (!leddev)
356 return false;
357
358 if (di > leddev->caps.nleds-1 && di != 255)
359 return false;
360
361 if (!leddev->caps.variable_brightness)
362 intensity = intensity > 0 ? 255 : 0;
363
364 /* PROTOCOL INSERTION POINT */
365 switch (controllers[device].type) {
366 case ARCAN_LEDCTRL:
367 if (led < 0)
368 return write_leddev(leddev, device, (uint8_t[]){
369 'A', '\0', 'i', intensity, 'c', '\0'}, 6);
370 else
371 return write_leddev(leddev, device, (uint8_t[]){
372 'a', (uint8_t)led, 'i', intensity, 'c', '\0'}, 6);
373 break;
374 case PACDRIVE:
375 if (intensity)
376 leddev->ledmask |= 1 << led;
377 else
378 leddev->ledmask &= ~(1 << led);
379 ultimarc_update(device, leddev);
380 break;
381 default:
382 arcan_warning("Warning: arcan_led_intensity(), unknown LED / "
383 "unsupported mode for device type: %i\n", controllers[device].type);
384 return false;
385 }
386
387 return true;
388 }
389
arcan_led_rgb(uint8_t device,int16_t led,uint8_t r,uint8_t g,uint8_t b,bool buffer)390 int arcan_led_rgb(uint8_t device,
391 int16_t led, uint8_t r, uint8_t g, uint8_t b, bool buffer)
392 {
393 struct led_controller* leddev = get_device(device);
394 if (!leddev || !leddev->caps.rgb || led > (int)leddev->caps.nleds)
395 return -1;
396
397 switch (leddev->type){
398 case ARCAN_LEDCTRL:{
399 int v;
400 if (led < 0)
401 v = write_leddev(leddev, device, (uint8_t[]){'A', '\0'}, 2);
402 else
403 v = write_leddev(leddev, device, (uint8_t[]){'a', (uint8_t)led}, 2);
404
405 if (1 == v)
406 return write_leddev(leddev, device, (uint8_t[]){
407 'r', r, 'g', g, 'b', b, 'c', buffer ? 255 : 0}, 8);
408 else
409 return v;
410 }
411 break;
412 default:
413 arcan_warning("Warning: arcan_led_rgb(), unknown LED / unsupported mode"
414 " for device type: %i\n", leddev->type);
415 return -1;
416 }
417 }
418
arcan_led_capabilities(uint8_t device)419 struct led_capabilities arcan_led_capabilities(uint8_t device)
420 {
421 struct led_capabilities rv = {0};
422 struct led_controller* leddev = get_device(device);
423 if (!leddev)
424 return rv;
425 return leddev->caps;
426 }
427
arcan_led_shutdown()428 void arcan_led_shutdown()
429 {
430 for (int i = 0; i < MAX_LED_CONTROLLERS && n_controllers > 0; i++)
431 if ((ctrl_mask & (1 << i)) > 0){
432 n_controllers--;
433 switch(controllers[i].type){
434 case PACDRIVE:
435 #ifdef USB_SUPPORT
436 hid_close(controllers[i].handle);
437 #endif
438 break;
439 case ARCAN_LEDCTRL:{
440 if (-1 == write(controllers[i].fd, (char[]){'o', '\0'}, 2))
441 arcan_warning("arcan_led_shutdown(), error sending shutdown\n");
442 close(controllers[i].fd);
443 free(controllers[i].path);
444 controllers[i].path = NULL;
445 }
446 break;
447 }
448 }
449
450 ctrl_mask = 0;
451 }
452