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