Files
raman/app/src/main/cpp/SerialPort.c
2026-02-28 12:45:29 +09:00

150 lines
4.7 KiB
C

#include "SerialPort.h"
#include <termios.h>
#include <pthread.h>
#include <unistd.h>
#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);
}