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