#include "SerialPort.h" #include #include #include #define MAX_THREAD_LIMIT 32 static speed_t getBaudrate(jint baudrate) { switch(baudrate) { case 0: return B0; case 50: return B50; case 75: return B75; case 110: return B110; case 134: return B134; case 150: return B150; case 200: return B200; case 300: return B300; case 600: return B600; case 1200: return B1200; case 1800: return B1800; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 500000: return B500000; case 576000: return B576000; case 921600: return B921600; case 1000000: return B1000000; case 1152000: return B1152000; case 1500000: return B1500000; case 2000000: return B2000000; case 2500000: return B2500000; case 3000000: return B3000000; case 3500000: return B3500000; case 4000000: return B4000000; default: return -1; } } struct { pthread_t tid; jobject clazz; } threads[MAX_THREAD_LIMIT]; JavaVM *vm; jfieldID mFD; jmethodID onNativeData; void *comm_read() { JNIEnv *env; (*vm)->AttachCurrentThread(vm, &env, NULL); jclass clazz = NULL; for (int i = 0; i < MAX_THREAD_LIMIT; i++) { if (pthread_equal(pthread_self(), threads[i].tid)) { clazz = threads[i].clazz; if (mFD == NULL || onNativeData == NULL) { jclass obj = (*env)->GetObjectClass(env, clazz); mFD = (*env)->GetFieldID(env, obj, "mFD", "I"); onNativeData = (*env)->GetMethodID(env, obj, "onNativeData", "([B)V"); } break; } } if (clazz != NULL) { int fd; while ((fd = (*env)->GetIntField(env, clazz, mFD)) != -1) { int size = 1024; jbyte data[size]; int len = read(fd, data, size); if (len > 0) { jbyteArray bytes = (*env)->NewByteArray(env, len); (*env)->SetByteArrayRegion(env, bytes, 0, len, data); (*env)->CallVoidMethod(env, clazz, onNativeData, bytes); } else if (len < 0) { // DETECT: Hardware level error (e.g. unplugged) // Send a specific error byte like 0x0E to trigger the Error result in Repository jbyteArray errorBytes = (*env)->NewByteArray(env, 1); jbyte err = 0x0E; (*env)->SetByteArrayRegion(env, errorBytes, 0, 1, &err); (*env)->CallVoidMethod(env, clazz, onNativeData, errorBytes); } usleep(10000); } } if (vm != NULL) { int isFree = 1; for (int i = 0; i < MAX_THREAD_LIMIT; i++) { if (threads[i].clazz != NULL) { if (threads[i].clazz == clazz) { (*env)->DeleteGlobalRef(env, clazz); threads[i].clazz = clazz = NULL; } else { isFree = 0; if (clazz == NULL) break; } } } (*vm)->DetachCurrentThread(vm); if (isFree) { onNativeData = NULL; mFD = NULL; vm = NULL; } } return 0; } JNIEXPORT void JNICALL Java_com_laseroptek_raman_data_source_serial_SerialPort_open(JNIEnv *env, jclass clazz, int fd, int baudrate) { tcflush(fd, TCIOFLUSH); struct termios cfg; tcgetattr(fd, &cfg); cfmakeraw(&cfg); speed_t speed = getBaudrate(baudrate); cfsetispeed(&cfg, speed); cfsetospeed(&cfg, speed); tcsetattr(fd, TCSANOW, &cfg); for (int i = 0; i < MAX_THREAD_LIMIT; i++) { if (threads[i].clazz == NULL) { if (vm == NULL) { (*env)->GetJavaVM(env, &vm); } threads[i].clazz = (*env)->NewGlobalRef(env, clazz); pthread_create(&threads[i].tid, NULL, comm_read, NULL); break; } } } JNIEXPORT void JNICALL Java_com_laseroptek_raman_data_source_serial_SerialPort_write(JNIEnv *env, __unused jclass _, int fd, jbyteArray bytes) { jbyte *data = (*env)->GetByteArrayElements(env, bytes, NULL); if (data != NULL) { write(fd, data, (*env)->GetArrayLength(env, bytes)); (*env)->ReleaseByteArrayElements(env, bytes, data, JNI_ABORT); } } JNIEXPORT void JNICALL Java_com_laseroptek_raman_data_source_serial_SerialPort_close(__unused JNIEnv *_, __unused jclass __, int fd) { close(fd); }