1 /*-
2  * Copyright (c) 2017 Hans Petter Selasky <hselasky@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <pthread.h>
34 #include <err.h>
35 #include <jni.h>
36 
37 #include "umidi20.h"
38 
39 struct umidi20_parse {
40 	uint8_t *temp_cmd;
41 	uint8_t	temp_0[4];
42 	uint8_t	temp_1[4];
43 	uint8_t	state;
44 #define	UMIDI20_ST_UNKNOWN   0		/* scan for command */
45 #define	UMIDI20_ST_1PARAM    1
46 #define	UMIDI20_ST_2PARAM_1  2
47 #define	UMIDI20_ST_2PARAM_2  3
48 #define	UMIDI20_ST_SYSEX_0   4
49 #define	UMIDI20_ST_SYSEX_1   5
50 #define	UMIDI20_ST_SYSEX_2   6
51 };
52 
53 struct umidi20_android {
54 	int	read_fd[2];
55 	int	write_fd[2];
56 	struct umidi20_parse parse;
57 };
58 
59 struct umidi20_class_recv {
60   	jclass class;
61 };
62 
63 struct umidi20_class_main {
64   	jclass class;
65 	jmethodID constructor;
66 };
67 
68 struct umidi20_class {
69 	void *activity;
70 	struct umidi20_class_recv recv;
71 	struct umidi20_class_main main;
72 };
73 
74 static struct umidi20_class umidi20_class;
75 
76 static pthread_mutex_t umidi20_android_mtx;
77 static pthread_cond_t umidi20_android_cv;
78 static pthread_t umidi20_android_thread;
79 static struct umidi20_android umidi20_android[UMIDI20_N_DEVICES];
80 static int umidi20_android_init_done;
81 static int umidi20_android_register_done;
82 static const char *umidi20_android_name;
83 static int umidi20_action_current;
84 static int umidi20_action_busy;
85 
86 enum {
87 	UMIDI20_CMD_SCAN_RX = 0,
88 	UMIDI20_CMD_SCAN_TX = 1,
89 	UMIDI20_CMD_SEND_MIDI = 2,
90 	UMIDI20_CMD_OPEN_TX = 3,
91 	UMIDI20_CMD_OPEN_RX = 4,
92 	UMIDI20_CMD_CLOSE_TX = 5,
93 	UMIDI20_CMD_CLOSE_RX = 6,
94 	UMIDI20_CMD_INITIALIZE = 7,
95 };
96 
97 #define	UMIDI20_MTOD(env,name, ...)	\
98 	env[0]->name(env,## __VA_ARGS__)
99 
100 #define	UMIDI20_STRING_LENGTH(env, obj)	\
101 	UMIDI20_MTOD(env, GetStringUTFLength, obj)
102 
103 #define	UMIDI20_STRING_COPY(env, obj, start, len, buf)	\
104 	UMIDI20_MTOD(env, GetStringUTFRegion, obj, start, len, (char *)(buf))
105 
106 #ifdef HAVE_DEBUG
107 #define	DPRINTF(fmt, ...) \
108     printf("%s:%d: " fmt, __FUNCTION__, __LINE__,## __VA_ARGS__)
109 #else
110 #define	DPRINTF(fmt, ...) do { } while (0)
111 #endif
112 
113 static char *
umidi20_dup_jstring(JNIEnv * env,jstring str)114 umidi20_dup_jstring(JNIEnv *env, jstring str)
115 {
116 	char *ptr;
117 	jsize len = UMIDI20_STRING_LENGTH(env, str) + 1;
118 
119 	ptr = malloc(len);
120 	if (ptr == NULL)
121 		return (NULL);
122 	UMIDI20_STRING_COPY(env, str, 0, len - 1, ptr);
123 	ptr[len - 1] = 0;
124 	return (ptr);
125 }
126 
127 static void
umidi20_android_lock(void)128 umidi20_android_lock(void)
129 {
130 	pthread_mutex_lock(&umidi20_android_mtx);
131 }
132 
133 static void
umidi20_android_unlock(void)134 umidi20_android_unlock(void)
135 {
136 	pthread_mutex_unlock(&umidi20_android_mtx);
137 }
138 
139 static void
umidi20_android_wait(void)140 umidi20_android_wait(void)
141 {
142 	pthread_cond_wait(&umidi20_android_cv, &umidi20_android_mtx);
143 }
144 
145 static void
umidi20_android_wakeup(void)146 umidi20_android_wakeup(void)
147 {
148 	pthread_cond_broadcast(&umidi20_android_cv);
149 }
150 
151 static void
umidi20_action_locked(int a,int b)152 umidi20_action_locked(int a, int b)
153 {
154 	while (umidi20_action_busy != 0)
155 		umidi20_android_wait();
156 
157 	umidi20_action_busy = 1;
158 	umidi20_action_current = a;
159 	umidi20_android_wakeup();
160 
161 	while (umidi20_action_busy != 3)
162 		umidi20_android_wait();
163 
164 	if ((a & 0xFF) == UMIDI20_CMD_SEND_MIDI) {
165 	  	umidi20_action_busy = 1;
166 		umidi20_action_current = b;
167 		umidi20_android_wakeup();
168 
169 		while (umidi20_action_busy != 3)
170 			umidi20_android_wait();
171 	}
172 
173 	umidi20_action_busy = 0;
174 	umidi20_android_wakeup();
175 }
176 
177 static void
umidi20_android_onSendNative(JNIEnv * env,jobject obj,jobject msg,int offset,int count,int device)178 umidi20_android_onSendNative(JNIEnv *env, jobject obj, jobject msg, int offset,
179     int count, int device)
180 {
181 	struct umidi20_android *puj;
182 	uint8_t buffer[count];
183 	uint8_t x;
184 
185 	UMIDI20_MTOD(env, GetByteArrayRegion, msg, offset, count, buffer);
186 
187 	umidi20_android_lock();
188 	puj = &umidi20_android[device];
189 	if (puj->write_fd[1] >= 0)
190 		write(puj->write_fd[1], buffer, count);
191 	umidi20_android_unlock();
192 }
193 
194 static jobject
umidi20_android_getActivity(JNIEnv * env,jobject obj)195 umidi20_android_getActivity(JNIEnv *env, jobject obj)
196 {
197 	return (umidi20_class.activity);
198 }
199 
200 static jint
umidi20_android_getAction(JNIEnv * env,jobject obj)201 umidi20_android_getAction(JNIEnv *env, jobject obj)
202 {
203 	jint retval;
204 
205 	umidi20_android_lock();
206 	if (umidi20_action_busy == 2) {
207 		umidi20_action_busy = 3;
208 		umidi20_android_wakeup();
209 	}
210 	while (umidi20_action_busy != 1)
211 		umidi20_android_wait();
212 	retval = umidi20_action_current;
213 	umidi20_action_busy = 2;
214 	umidi20_android_unlock();
215 
216 	return (retval);
217 }
218 
219 static char **umidi20_rx_dev_ptr;
220 
221 static void
umidi20_android_setRxDevices(JNIEnv * env,jobject obj,int num)222 umidi20_android_setRxDevices(JNIEnv *env, jobject obj, int num)
223 {
224 	umidi20_android_free_inputs(umidi20_rx_dev_ptr);
225 	umidi20_rx_dev_ptr = calloc(num + 1, sizeof(void *));
226 }
227 
228 static char **umidi20_tx_dev_ptr;
229 
230 static void
umidi20_android_setTxDevices(JNIEnv * env,jobject obj,int num)231 umidi20_android_setTxDevices(JNIEnv *env, jobject obj, int num)
232 {
233 	umidi20_android_free_outputs(umidi20_tx_dev_ptr);
234 	umidi20_tx_dev_ptr = calloc(num + 1, sizeof(void *));
235 }
236 
237 static void
umidi20_android_storeRxDevice(JNIEnv * env,jobject obj,int num,jstring desc)238 umidi20_android_storeRxDevice(JNIEnv *env, jobject obj, int num, jstring desc)
239 {
240 	umidi20_rx_dev_ptr[num] = umidi20_dup_jstring(env, desc);
241 }
242 
243 static void
umidi20_android_storeTxDevice(JNIEnv * env,jobject obj,int num,jstring desc)244 umidi20_android_storeTxDevice(JNIEnv *env, jobject obj, int num, jstring desc)
245 {
246 	umidi20_tx_dev_ptr[num] = umidi20_dup_jstring(env, desc);
247 }
248 
249 static const uint8_t umidi20_cmd_to_len[16] = {
250 	[0x0] = 0,			/* reserved */
251 	[0x1] = 0,			/* reserved */
252 	[0x2] = 2,			/* bytes */
253 	[0x3] = 3,			/* bytes */
254 	[0x4] = 3,			/* bytes */
255 	[0x5] = 1,			/* bytes */
256 	[0x6] = 2,			/* bytes */
257 	[0x7] = 3,			/* bytes */
258 	[0x8] = 3,			/* bytes */
259 	[0x9] = 3,			/* bytes */
260 	[0xA] = 3,			/* bytes */
261 	[0xB] = 3,			/* bytes */
262 	[0xC] = 2,			/* bytes */
263 	[0xD] = 2,			/* bytes */
264 	[0xE] = 3,			/* bytes */
265 	[0xF] = 1,			/* bytes */
266 };
267 
268 /*
269  * The following statemachine, that converts MIDI commands to
270  * USB MIDI packets, derives from Linux's usbmidi.c, which
271  * was written by "Clemens Ladisch":
272  *
273  * Returns:
274  *    0: No command
275  * Else: Command is complete
276  */
277 static uint8_t
umidi20_convert_to_usb(struct umidi20_android * puj,uint8_t cn,uint8_t b)278 umidi20_convert_to_usb(struct umidi20_android *puj, uint8_t cn, uint8_t b)
279 {
280 	uint8_t p0 = (cn << 4);
281 
282 	if (b >= 0xf8) {
283 		puj->parse.temp_0[0] = p0 | 0x0f;
284 		puj->parse.temp_0[1] = b;
285 		puj->parse.temp_0[2] = 0;
286 		puj->parse.temp_0[3] = 0;
287 		puj->parse.temp_cmd = puj->parse.temp_0;
288 		return (1);
289 
290 	} else if (b >= 0xf0) {
291 		switch (b) {
292 		case 0xf0:		/* system exclusive begin */
293 			puj->parse.temp_1[1] = b;
294 			puj->parse.state = UMIDI20_ST_SYSEX_1;
295 			break;
296 		case 0xf1:		/* MIDI time code */
297 		case 0xf3:		/* song select */
298 			puj->parse.temp_1[1] = b;
299 			puj->parse.state = UMIDI20_ST_1PARAM;
300 			break;
301 		case 0xf2:		/* song position pointer */
302 			puj->parse.temp_1[1] = b;
303 			puj->parse.state = UMIDI20_ST_2PARAM_1;
304 			break;
305 		case 0xf4:		/* unknown */
306 		case 0xf5:		/* unknown */
307 			puj->parse.state = UMIDI20_ST_UNKNOWN;
308 			break;
309 		case 0xf6:		/* tune request */
310 			puj->parse.temp_1[0] = p0 | 0x05;
311 			puj->parse.temp_1[1] = 0xf6;
312 			puj->parse.temp_1[2] = 0;
313 			puj->parse.temp_1[3] = 0;
314 			puj->parse.temp_cmd = puj->parse.temp_1;
315 			puj->parse.state = UMIDI20_ST_UNKNOWN;
316 			return (1);
317 
318 		case 0xf7:		/* system exclusive end */
319 			switch (puj->parse.state) {
320 			case UMIDI20_ST_SYSEX_0:
321 				puj->parse.temp_1[0] = p0 | 0x05;
322 				puj->parse.temp_1[1] = 0xf7;
323 				puj->parse.temp_1[2] = 0;
324 				puj->parse.temp_1[3] = 0;
325 				puj->parse.temp_cmd = puj->parse.temp_1;
326 				puj->parse.state = UMIDI20_ST_UNKNOWN;
327 				return (1);
328 			case UMIDI20_ST_SYSEX_1:
329 				puj->parse.temp_1[0] = p0 | 0x06;
330 				puj->parse.temp_1[2] = 0xf7;
331 				puj->parse.temp_1[3] = 0;
332 				puj->parse.temp_cmd = puj->parse.temp_1;
333 				puj->parse.state = UMIDI20_ST_UNKNOWN;
334 				return (1);
335 			case UMIDI20_ST_SYSEX_2:
336 				puj->parse.temp_1[0] = p0 | 0x07;
337 				puj->parse.temp_1[3] = 0xf7;
338 				puj->parse.temp_cmd = puj->parse.temp_1;
339 				puj->parse.state = UMIDI20_ST_UNKNOWN;
340 				return (1);
341 			}
342 			puj->parse.state = UMIDI20_ST_UNKNOWN;
343 			break;
344 		}
345 	} else if (b >= 0x80) {
346 		puj->parse.temp_1[1] = b;
347 		if ((b >= 0xc0) && (b <= 0xdf)) {
348 			puj->parse.state = UMIDI20_ST_1PARAM;
349 		} else {
350 			puj->parse.state = UMIDI20_ST_2PARAM_1;
351 		}
352 	} else {			/* b < 0x80 */
353 		switch (puj->parse.state) {
354 		case UMIDI20_ST_1PARAM:
355 			if (puj->parse.temp_1[1] < 0xf0) {
356 				p0 |= puj->parse.temp_1[1] >> 4;
357 			} else {
358 				p0 |= 0x02;
359 				puj->parse.state = UMIDI20_ST_UNKNOWN;
360 			}
361 			puj->parse.temp_1[0] = p0;
362 			puj->parse.temp_1[2] = b;
363 			puj->parse.temp_1[3] = 0;
364 			puj->parse.temp_cmd = puj->parse.temp_1;
365 			return (1);
366 		case UMIDI20_ST_2PARAM_1:
367 			puj->parse.temp_1[2] = b;
368 			puj->parse.state = UMIDI20_ST_2PARAM_2;
369 			break;
370 		case UMIDI20_ST_2PARAM_2:
371 			if (puj->parse.temp_1[1] < 0xf0) {
372 				p0 |= puj->parse.temp_1[1] >> 4;
373 				puj->parse.state = UMIDI20_ST_2PARAM_1;
374 			} else {
375 				p0 |= 0x03;
376 				puj->parse.state = UMIDI20_ST_UNKNOWN;
377 			}
378 			puj->parse.temp_1[0] = p0;
379 			puj->parse.temp_1[3] = b;
380 			puj->parse.temp_cmd = puj->parse.temp_1;
381 			return (1);
382 		case UMIDI20_ST_SYSEX_0:
383 			puj->parse.temp_1[1] = b;
384 			puj->parse.state = UMIDI20_ST_SYSEX_1;
385 			break;
386 		case UMIDI20_ST_SYSEX_1:
387 			puj->parse.temp_1[2] = b;
388 			puj->parse.state = UMIDI20_ST_SYSEX_2;
389 			break;
390 		case UMIDI20_ST_SYSEX_2:
391 			puj->parse.temp_1[0] = p0 | 0x04;
392 			puj->parse.temp_1[3] = b;
393 			puj->parse.temp_cmd = puj->parse.temp_1;
394 			puj->parse.state = UMIDI20_ST_SYSEX_0;
395 			return (1);
396 		default:
397 			break;
398 		}
399 	}
400 	return (0);
401 }
402 
403 static void *
umidi20_write_process(void * arg)404 umidi20_write_process(void *arg)
405 {
406 	uint8_t data[1];
407 	uint8_t len;
408 	int n;
409 
410 	while (1) {
411 		umidi20_android_lock();
412 		for (n = 0; n != UMIDI20_N_DEVICES; n++) {
413 			struct umidi20_android *puj = umidi20_android + n;
414 
415 			while (puj->read_fd[0] > -1 &&
416 			       read(puj->read_fd[0], data, sizeof(data)) == sizeof(data)) {
417 
418 				/* parse MIDI stream */
419 				if (umidi20_convert_to_usb(puj, 0, data[0]) == 0)
420 					continue;
421 
422 				len = umidi20_cmd_to_len[puj->parse.temp_cmd[0] & 0xF];
423 				if (len == 0)
424 					continue;
425 
426 				umidi20_action_locked(UMIDI20_CMD_SEND_MIDI |
427 						      (n << 8) | (len << 12),
428 						      (puj->parse.temp_cmd[1]) |
429 						      (puj->parse.temp_cmd[2] << 8) |
430 						      (puj->parse.temp_cmd[3] << 16) |
431 						      (puj->parse.temp_cmd[4] << 24));
432 			}
433 		}
434 		umidi20_android_unlock();
435 
436 		usleep(1000);
437 	}
438 	return (NULL);
439 }
440 
441 static void
umidi20_android_uniq_inputs(char ** ptr)442 umidi20_android_uniq_inputs(char **ptr)
443 {
444 	unsigned long x;
445 	unsigned long y;
446 	unsigned long z;
447 	unsigned long n;
448 	char *pstr;
449 
450 	/* remove any hashes from device names */
451 	for (n = 0; ptr[n]; n++) {
452 		pstr = strchr(ptr[n], '#');
453 		if (pstr != NULL)
454 			*pstr = 0;
455 	}
456 
457 	/* make all device names uniqe */
458 	for (x = 0; x != n; x++) {
459 		for (z = 0, y = x + 1; y != n; y++) {
460 			if (strcmp(ptr[x], ptr[y]) == 0) {
461 				size_t s = strlen(ptr[y]) + 16;
462 				pstr = ptr[y];
463 				ptr[y] = malloc(s);
464 				if (ptr[y] == NULL) {
465 					ptr[y] = pstr;
466 					return;
467 				}
468 				z++;
469 				snprintf(ptr[y], s, "%s#%d", pstr, (int)z);
470 				free(pstr);
471 			}
472 		}
473 	}
474 }
475 
476 const char **
umidi20_android_dup_io(const char ** ppstr)477 umidi20_android_dup_io(const char **ppstr)
478 {
479 	unsigned long n = 0;
480 	char **retval;
481 
482 
483 	for (n = 0; ppstr[n] != NULL; n++)
484 		;
485 
486 	retval = calloc(n + 1, sizeof(void *));
487 	if (retval != NULL) {
488 		for (n = 0; ppstr[n] != NULL; n++)
489 			retval[n] = strdup(ppstr[n]);
490 	}
491 	return (retval);
492 }
493 
494 const char **
umidi20_android_alloc_outputs(void)495 umidi20_android_alloc_outputs(void)
496 {
497 	if (umidi20_android_init_done == 0)
498 		return (NULL);
499 
500 	umidi20_android_lock();
501 	umidi20_action_locked(UMIDI20_CMD_SCAN_TX, 0);
502 	umidi20_android_unlock();
503 
504 	umidi20_android_uniq_inputs(umidi20_tx_dev_ptr);
505 
506 	return (umidi20_android_dup_io(umidi20_tx_dev_ptr));
507 }
508 
509 const char **
umidi20_android_alloc_inputs(void)510 umidi20_android_alloc_inputs(void)
511 {
512 	if (umidi20_android_init_done == 0)
513 		return (NULL);
514 
515 	umidi20_android_lock();
516 	umidi20_action_locked(UMIDI20_CMD_SCAN_RX, 0);
517 	umidi20_android_unlock();
518 
519 	umidi20_android_uniq_inputs(umidi20_rx_dev_ptr);
520 
521 	return (umidi20_android_dup_io(umidi20_rx_dev_ptr));
522 }
523 
524 void
umidi20_android_free_outputs(const char ** ports)525 umidi20_android_free_outputs(const char **ports)
526 {
527 	unsigned long n;
528 
529 	if (ports == NULL)
530 		return;
531 
532 	for (n = 0; ports[n] != NULL; n++)
533 		free(ports[n]);
534 
535 	free(ports);
536 }
537 
538 void
umidi20_android_free_inputs(const char ** ports)539 umidi20_android_free_inputs(const char **ports)
540 {
541 	unsigned long n;
542 
543 	if (ports == NULL)
544 		return;
545 
546 	for (n = 0; ports[n] != NULL; n++)
547 		free(ports[n]);
548 
549 	free(ports);
550 }
551 
552 int
umidi20_android_rx_open(uint8_t n,const char * name)553 umidi20_android_rx_open(uint8_t n, const char *name)
554 {
555 	struct umidi20_android *puj;
556 	unsigned long x;
557 	int error;
558 
559 	if (n >= UMIDI20_N_DEVICES || umidi20_android_init_done == 0)
560 		return (-1);
561 
562 	puj = &umidi20_android[n];
563 
564 	/* check if already opened */
565 	if (puj->write_fd[1] > -1 || puj->write_fd[0] > -1 || umidi20_rx_dev_ptr == NULL)
566 		return (-1);
567 
568 	for (x = 0; umidi20_rx_dev_ptr[x] != NULL; x++) {
569 		if (strcmp(umidi20_rx_dev_ptr[x], name) == 0)
570 			break;
571 	}
572 
573 	/* check if device not found */
574 	if (umidi20_rx_dev_ptr[x] == NULL)
575 		return (-1);
576 
577 	umidi20_android_lock();
578 	umidi20_action_locked(UMIDI20_CMD_OPEN_RX | (n << 8) | (x << 12), 0);
579 	/* create looback pipe */
580 	error = umidi20_pipe(puj->write_fd);
581 	/* check for error */
582 	if (error != 0) {
583 		umidi20_action_locked(UMIDI20_CMD_CLOSE_RX | (n << 8) | (x << 12), 0);
584 		puj->write_fd[0] = -1;
585 	}
586 	umidi20_android_unlock();
587 
588 	return (puj->write_fd[0]);
589 }
590 
591 int
umidi20_android_tx_open(uint8_t n,const char * name)592 umidi20_android_tx_open(uint8_t n, const char *name)
593 {
594 	struct umidi20_android *puj;
595 	unsigned long x;
596 	int error;
597 
598 	if (n >= UMIDI20_N_DEVICES || umidi20_android_init_done == 0)
599 		return (-1);
600 
601 	puj = &umidi20_android[n];
602 
603 	/* check if already opened */
604 	if (puj->read_fd[1] > -1 || puj->read_fd[0] > -1 || umidi20_tx_dev_ptr == NULL)
605 		return (-1);
606 
607 	for (x = 0; umidi20_tx_dev_ptr[x] != NULL; x++) {
608 		if (strcmp(umidi20_tx_dev_ptr[x], name) == 0)
609 			break;
610 	}
611 
612 	/* check if device not found */
613 	if (umidi20_tx_dev_ptr[x] == NULL)
614 		return (-1);
615 
616 	umidi20_android_lock();
617 	umidi20_action_locked(UMIDI20_CMD_OPEN_TX | (n << 8) | (x << 12), 0);
618 
619 	/* create looback pipe */
620 	error = umidi20_pipe(puj->read_fd);
621 	if (error == 0) {
622 		fcntl(puj->read_fd[0], F_SETFL, (int)O_NONBLOCK);
623 		memset(&puj->parse, 0, sizeof(puj->parse));
624 	} else {
625 		umidi20_action_locked(UMIDI20_CMD_CLOSE_TX | (n << 8) | (x << 12), 0);
626 		puj->read_fd[1] = -1;
627 	}
628 	umidi20_android_unlock();
629 
630 	return (puj->read_fd[1]);
631 }
632 
633 int
umidi20_android_rx_close(uint8_t n)634 umidi20_android_rx_close(uint8_t n)
635 {
636 	struct umidi20_android *puj;
637 
638 	if (n >= UMIDI20_N_DEVICES || umidi20_android_init_done == 0)
639 		return (-1);
640 
641 	puj = &umidi20_android[n];
642 
643 	umidi20_android_lock();
644 	close(puj->write_fd[0]);
645 	close(puj->write_fd[1]);
646 	puj->write_fd[0] = -1;
647 	puj->write_fd[1] = -1;
648 	umidi20_action_locked(UMIDI20_CMD_CLOSE_RX | (n << 8), 0);
649 	umidi20_android_unlock();
650 
651 	return (0);
652 }
653 
654 int
umidi20_android_tx_close(uint8_t n)655 umidi20_android_tx_close(uint8_t n)
656 {
657 	struct umidi20_android *puj;
658 
659 	if (n >= UMIDI20_N_DEVICES || umidi20_android_init_done == 0)
660 		return (-1);
661 
662 	puj = &umidi20_android[n];
663 
664 	umidi20_android_lock();
665 	close(puj->read_fd[0]);
666 	close(puj->read_fd[1]);
667 	puj->read_fd[0] = -1;
668 	puj->read_fd[1] = -1;
669 	umidi20_action_locked(UMIDI20_CMD_CLOSE_TX | (n << 8), 0);
670 	umidi20_android_unlock();
671 
672 	return (0);
673 }
674 
675 static jclass
umidi20_android_find_class(JNIEnv * env,const char * name)676 umidi20_android_find_class(JNIEnv *env, const char *name)
677 {
678 	jclass class;
679 
680 	class = UMIDI20_MTOD(env, FindClass, name);
681 	if (class == NULL) {
682 		DPRINTF("Class %s not found\n");
683 	} else {
684 		jclass nclass;
685 		nclass = UMIDI20_MTOD(env, NewGlobalRef, class);
686 		UMIDI20_MTOD(env, DeleteLocalRef, class);
687 		class = nclass;
688 	}
689 	return (class);
690 }
691 
692 #define	UMIDI20_RESOLVE_CLASS(env, name, str)	\
693 	(umidi20_class.name.class = umidi20_android_find_class(env, str))
694 
695 JNIEXPORT jint
JNI_OnLoad(JavaVM * jvm,void * reserved)696 JNI_OnLoad(JavaVM *jvm, void *reserved)
697 {
698 	static const JNINativeMethod recv[] = {
699 		{ "onSendNative", "([BIII)V", (void *)&umidi20_android_onSendNative },
700 	};
701 	static const JNINativeMethod main[] = {
702 		{ "getAction", "()I", (void *)&umidi20_android_getAction },
703 		{ "getActivity", "()Landroid/app/Activity;", (void *)&umidi20_android_getActivity },
704 		{ "setRxDevices", "(I)V", (void *)&umidi20_android_setRxDevices },
705 		{ "setTxDevices", "(I)V", (void *)&umidi20_android_setTxDevices },
706 		{ "storeRxDevice", "(ILjava/lang/String;)V", (void *)&umidi20_android_storeRxDevice },
707 		{ "storeTxDevice", "(ILjava/lang/String;)V", (void *)&umidi20_android_storeTxDevice },
708 	};
709 	JNIEnv *env;
710 	jobject obj;
711 
712 	if (jvm[0]->GetEnv(jvm, &env, JNI_VERSION_1_6) != JNI_OK)
713 		return (JNI_ERR);
714 
715 	pthread_mutex_init(&umidi20_android_mtx, NULL);
716 	pthread_cond_init(&umidi20_android_cv, NULL);
717 
718 	if (UMIDI20_RESOLVE_CLASS(env, recv, "org/selasky/umidi20/UMidi20Recv") == NULL ||
719 	    UMIDI20_RESOLVE_CLASS(env, main, "org/selasky/umidi20/UMidi20Main") == NULL)
720 		return (JNI_ERR);
721 
722 	if (UMIDI20_MTOD(env, RegisterNatives, umidi20_class.recv.class,
723 			 &recv[0], sizeof(recv) / sizeof(recv[0])))
724 		return (JNI_ERR);
725 
726 	if (UMIDI20_MTOD(env, RegisterNatives, umidi20_class.main.class,
727 			 &main[0], sizeof(main) / sizeof(main[0])))
728 		return (JNI_ERR);
729 
730 	umidi20_class.main.constructor = UMIDI20_MTOD(env, GetMethodID,
731 	    umidi20_class.main.class, "<init>", "()V");
732 	if (umidi20_class.main.constructor == NULL)
733 		return (JNI_ERR);
734 
735 	obj = UMIDI20_MTOD(env, NewObject, umidi20_class.main.class,
736 	    umidi20_class.main.constructor);
737 	if (obj == NULL)
738 		return (JNI_ERR);
739 
740 	UMIDI20_MTOD(env, NewGlobalRef, obj);
741 	UMIDI20_MTOD(env, DeleteLocalRef, obj);
742 
743 	umidi20_android_register_done = 1;
744 
745 	return (JNI_VERSION_1_6);
746 }
747 
748 int
umidi20_android_init(const char * name,void * activity)749 umidi20_android_init(const char *name, void *activity)
750 {
751 	struct umidi20_android *puj;
752 	char devname[64];
753 	uint8_t n;
754 
755 	umidi20_android_name = strdup(name);
756 
757 	if (umidi20_android_name == NULL || activity == NULL ||
758 	    umidi20_android_register_done == 0)
759 		return (-1);
760 
761 	for (n = 0; n != UMIDI20_N_DEVICES; n++) {
762 		puj = &umidi20_android[n];
763 		puj->read_fd[0] = -1;
764 		puj->read_fd[1] = -1;
765 		puj->write_fd[0] = -1;
766 		puj->write_fd[1] = -1;
767 	}
768 
769 	if (pthread_create(&umidi20_android_thread, NULL,
770 	    &umidi20_write_process, NULL))
771 		return (-1);
772 
773 	umidi20_class.activity = activity;
774 
775 	umidi20_android_lock();
776 	umidi20_action_locked(UMIDI20_CMD_INITIALIZE, 0);
777 	umidi20_android_unlock();
778 
779 	umidi20_android_init_done = 1;
780 
781 	return (0);
782 }
783