mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-07 09:46:02 +02:00

Also generate JNI header for serial component. This component does not really need it since the implementation is in one file only, but this would at least throw an error if the java part changes and the native part is not updated accordingly. Change-Id: Id598410c322580bdda37c905ed08627390c913ba Signed-off-by: Torbjörn Svensson <azoff@svenskalinuxforeningen.se>
569 lines
13 KiB
C
569 lines
13 KiB
C
/*******************************************************************************
|
|
* Copyright (c) 2015 QNX Software Systems and others.
|
|
*
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License 2.0
|
|
* which accompanies this distribution, and is available at
|
|
* https://www.eclipse.org/legal/epl-2.0/
|
|
*
|
|
* SPDX-License-Identifier: EPL-2.0
|
|
*
|
|
* Contributors:
|
|
* QNX Software Systems - initial API and implementation
|
|
* STMicroelectronics
|
|
*******************************************************************************/
|
|
#ifdef __APPLE__
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/uio.h>
|
|
#endif
|
|
#ifndef __MINGW32__
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <termios.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#ifndef __APPLE__
|
|
#include <linux/serial.h>
|
|
#endif
|
|
#else
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define UNICODE
|
|
#include <windows.h>
|
|
#endif
|
|
#include <jni.h>
|
|
#include <org_eclipse_cdt_serial_SerialPort.h>
|
|
|
|
#define FUNC(x) Java_org_eclipse_cdt_serial_SerialPort_ ## x
|
|
|
|
/**
|
|
* Use this method to throw an exception when open fails after the OS open
|
|
* stage. This method obtains the last error from OS to include in the
|
|
* IOException
|
|
*/
|
|
#ifndef __MINGW32__
|
|
static void closeAndthrowIOException(int fd, JNIEnv *env, const char *msg)
|
|
#else
|
|
static void closeAndthrowIOException(HANDLE handle, JNIEnv *env, const char *msg)
|
|
#endif
|
|
{
|
|
char buff[256];
|
|
#ifndef __MINGW32__
|
|
sprintf(buff, "%s: %s", msg, strerror(errno));
|
|
close(fd);
|
|
#else
|
|
sprintf_s(buff, sizeof(buff), "%s (%d)", msg, GetLastError());
|
|
CloseHandle(handle);
|
|
#endif
|
|
jclass cls = (*env)->FindClass(env, "java/io/IOException");
|
|
(*env)->ThrowNew(env, cls, buff);
|
|
}
|
|
|
|
static void throwIOException(JNIEnv *env, const char *msg)
|
|
{
|
|
char buff[256];
|
|
#ifndef __MINGW32__
|
|
sprintf(buff, "%s: %s", msg, strerror(errno));
|
|
#else
|
|
sprintf_s(buff, sizeof(buff), "%s (%d)", msg, GetLastError());
|
|
#endif
|
|
jclass cls = (*env)->FindClass(env, "java/io/IOException");
|
|
(*env)->ThrowNew(env, cls, buff);
|
|
}
|
|
|
|
JNIEXPORT jlong JNICALL FUNC(open0)(JNIEnv *env, jobject jobj, jstring portName, jint baudRate, jint byteSize, jint parity, jint stopBits)
|
|
{
|
|
#ifndef __MINGW32__
|
|
const char * cportName = (*env)->GetStringUTFChars(env, portName, NULL);
|
|
int fd = open(cportName, O_RDWR | O_NOCTTY | O_NDELAY);
|
|
if (fd < 0) {
|
|
char msg[256];
|
|
sprintf(msg, "Error opening %s", cportName);
|
|
(*env)->ReleaseStringUTFChars(env, portName, cportName);
|
|
throwIOException(env, msg);
|
|
return fd;
|
|
}
|
|
(*env)->ReleaseStringUTFChars(env, portName, cportName);
|
|
|
|
// Turn off all flags
|
|
fcntl(fd, F_SETFL, 0);
|
|
|
|
struct termios options;
|
|
tcgetattr(fd, &options);
|
|
options.c_cflag |= (CLOCAL | CREAD);
|
|
|
|
#ifndef __APPLE__
|
|
speed_t baud;
|
|
switch (baudRate) {
|
|
case 110:
|
|
baud = B110;
|
|
break;
|
|
case 300:
|
|
baud = B300;
|
|
break;
|
|
case 600:
|
|
baud = B600;
|
|
break;
|
|
case 1200:
|
|
baud = B1200;
|
|
break;
|
|
case 2400:
|
|
baud = B2400;
|
|
break;
|
|
case 4800:
|
|
baud = B4800;
|
|
break;
|
|
case 9600:
|
|
baud = B9600;
|
|
break;
|
|
case 19200:
|
|
baud = B19200;
|
|
break;
|
|
case 38400:
|
|
baud = B38400;
|
|
break;
|
|
case 57600:
|
|
baud = B57600;
|
|
break;
|
|
case 115200:
|
|
baud = B115200;
|
|
break;
|
|
case 230400:
|
|
baud = B230400;
|
|
break;
|
|
case 460800:
|
|
baud = B460800;
|
|
break;
|
|
case 500000:
|
|
baud = B500000;
|
|
break;
|
|
case 576000:
|
|
baud = B576000;
|
|
break;
|
|
case 921600:
|
|
baud = B921600;
|
|
break;
|
|
case 1000000:
|
|
baud = B1000000;
|
|
break;
|
|
case 1152000:
|
|
baud = B1152000;
|
|
break;
|
|
case 1500000:
|
|
baud = B1500000;
|
|
break;
|
|
case 2000000:
|
|
baud = B2000000;
|
|
break;
|
|
case 2500000:
|
|
baud = B2500000;
|
|
break;
|
|
case 3000000:
|
|
baud = B3000000;
|
|
break;
|
|
case 3500000:
|
|
baud = B3500000;
|
|
break;
|
|
case 4000000:
|
|
baud = B4000000;
|
|
break;
|
|
default:
|
|
baud = B0;
|
|
break;
|
|
}
|
|
|
|
if (baud == B0) {
|
|
// Use custom linux baud rates if possible: https://bugs.eclipse.org/bugs/show_bug.cgi?id=543122#c8
|
|
struct serial_struct serial_options;
|
|
options.c_cflag |= B38400;
|
|
|
|
if (ioctl(fd, TIOCGSERIAL, &serial_options) != 0) {
|
|
closeAndthrowIOException(fd, env, "Failed to use custom baud rate. Error using TIOCGSERIAL");
|
|
return -1;
|
|
}
|
|
serial_options.custom_divisor = serial_options.baud_base / baudRate;
|
|
if (serial_options.custom_divisor == 0) {
|
|
serial_options.custom_divisor = 1;
|
|
}
|
|
|
|
serial_options.flags &= ~ASYNC_SPD_MASK;
|
|
serial_options.flags |= ASYNC_SPD_CUST;
|
|
|
|
if (ioctl(fd, TIOCSSERIAL, &serial_options) != 0) {
|
|
closeAndthrowIOException(fd, env, "Failed to use custom baud rate. Error using TIOCSSERIAL");
|
|
return -1;
|
|
}
|
|
} else {
|
|
// Set baud rate
|
|
cfsetispeed(&options, baud);
|
|
cfsetospeed(&options, baud);
|
|
}
|
|
|
|
#else
|
|
// On OSX speed_t is simply the baud rate: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/cfsetispeed.3.html
|
|
cfsetispeed(&options, baudRate);
|
|
cfsetospeed(&options, baudRate);
|
|
#endif
|
|
|
|
// set data size
|
|
options.c_cflag &= ~CSIZE;
|
|
switch (byteSize) {
|
|
case 5:
|
|
options.c_cflag |= CS5;
|
|
break;
|
|
case 6:
|
|
options.c_cflag |= CS6;
|
|
break;
|
|
case 7:
|
|
options.c_cflag |= CS7;
|
|
break;
|
|
case 8:
|
|
options.c_cflag |= CS8;
|
|
break;
|
|
|
|
}
|
|
|
|
// set parity
|
|
switch (parity) {
|
|
case 0: // None
|
|
options.c_cflag &= ~PARENB;
|
|
break;
|
|
case 1: // Even
|
|
options.c_cflag |= PARENB;
|
|
options.c_cflag &= ~PARODD;
|
|
break;
|
|
case 2: // Odd
|
|
options.c_cflag |= (PARENB | PARODD);
|
|
break;
|
|
}
|
|
|
|
switch (stopBits) {
|
|
case 0: // 1
|
|
options.c_cflag &= ~CSTOPB;
|
|
break;
|
|
case 1: // 2
|
|
options.c_cflag |= CSTOPB;
|
|
break;
|
|
}
|
|
|
|
// raw input
|
|
options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
|
|
|
|
// ignore parity
|
|
options.c_iflag |= IGNPAR;
|
|
|
|
// turn off those bits in the input flag that fiddle with CR and NL
|
|
options.c_iflag &= ~(ICRNL | INLCR | IGNCR);
|
|
|
|
options.c_cc[VMIN] = 0; // min chars to read
|
|
options.c_cc[VTIME] = 2; // 10ths second timeout
|
|
|
|
tcflush(fd, TCIFLUSH);
|
|
tcsetattr(fd, TCSANOW, &options);
|
|
|
|
return fd;
|
|
#else // __MINGW32__
|
|
const wchar_t * cportName = (const wchar_t *)(*env)->GetStringChars(env, portName, NULL);
|
|
HANDLE handle = CreateFile(cportName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_FLAG_OVERLAPPED,
|
|
NULL);
|
|
(*env)->ReleaseStringChars(env, portName, cportName);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
char msg[256];
|
|
const char * name = (*env)->GetStringUTFChars(env, portName, NULL);
|
|
sprintf_s(msg, sizeof(msg), "Error opening %s", name);
|
|
(*env)->ReleaseStringUTFChars(env, portName, name);
|
|
throwIOException(env, msg);
|
|
return -1;
|
|
}
|
|
|
|
DCB dcb = { 0 };
|
|
|
|
if (!GetCommState(handle, &dcb)) {
|
|
closeAndthrowIOException(handle, env, "Error getting DCB");
|
|
return -1;
|
|
}
|
|
|
|
dcb.BaudRate = baudRate;
|
|
dcb.ByteSize = (BYTE)byteSize;
|
|
|
|
switch (parity) {
|
|
case 0: // None
|
|
dcb.fParity = FALSE;
|
|
dcb.Parity = NOPARITY;
|
|
break;
|
|
case 1: // Even
|
|
dcb.fParity = TRUE;
|
|
dcb.Parity = EVENPARITY;
|
|
break;
|
|
case 2: // Odd
|
|
dcb.fParity = TRUE;
|
|
dcb.Parity = ODDPARITY;
|
|
break;
|
|
}
|
|
|
|
switch (stopBits) {
|
|
case 0:
|
|
dcb.StopBits = ONESTOPBIT;
|
|
break;
|
|
case 1:
|
|
dcb.StopBits = TWOSTOPBITS;
|
|
break;
|
|
}
|
|
|
|
if (!SetCommState(handle, &dcb)) {
|
|
closeAndthrowIOException(handle, env, "Error setting DCB");
|
|
return -1;
|
|
}
|
|
|
|
COMMTIMEOUTS timeouts = { 0 };
|
|
timeouts.ReadIntervalTimeout = MAXDWORD;
|
|
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
|
|
timeouts.ReadTotalTimeoutConstant = 200;
|
|
if (!SetCommTimeouts(handle, &timeouts)) {
|
|
closeAndthrowIOException(handle, env, "Error setting timeouts");
|
|
return -1;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
return (jlong)handle;
|
|
#else
|
|
return (jlong)(unsigned)handle;
|
|
#endif
|
|
#endif // __MINGW32__
|
|
}
|
|
|
|
JNIEXPORT void JNICALL FUNC(close0)(JNIEnv *env, jobject jobj, jlong handle)
|
|
{
|
|
#ifndef __MINGW32__
|
|
close(handle);
|
|
#else
|
|
#ifdef _WIN64
|
|
CloseHandle((HANDLE)handle);
|
|
#else
|
|
CloseHandle((HANDLE)(unsigned)handle);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL FUNC(available0)(JNIEnv * env, jobject jobj, jlong jhandle)
|
|
{
|
|
#ifndef __MINGW32__
|
|
int result = 0;
|
|
if (ioctl(jhandle, FIONREAD, &result ) < 0) {
|
|
throwIOException(env, "Error calling ioctl");
|
|
return 0;
|
|
}
|
|
return result;
|
|
#else
|
|
COMSTAT stat;
|
|
DWORD errCode;
|
|
#ifdef _WIN64
|
|
HANDLE handle = (HANDLE)jhandle;
|
|
#else
|
|
HANDLE handle = (HANDLE)(unsigned)jhandle;
|
|
#endif
|
|
|
|
if (ClearCommError(handle, &errCode, &stat) == 0) {
|
|
throwIOException(env, "Error calling ClearCommError");
|
|
return -1;
|
|
}
|
|
return (int)stat.cbInQue;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT jint JNICALL FUNC(read1)(JNIEnv * env, jobject jobj, jlong jhandle, jbyteArray bytes, jint offset, jint size)
|
|
{
|
|
#ifndef __MINGW32__
|
|
jbyte buff[256];
|
|
int n = size < sizeof(buff) ? size : sizeof(buff);
|
|
n = read(jhandle, buff, n);
|
|
if (n > 0) {
|
|
(*env)->SetByteArrayRegion(env, bytes, offset, n, buff);
|
|
}
|
|
return n;
|
|
#else
|
|
OVERLAPPED olp = { 0 };
|
|
|
|
olp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (olp.hEvent == NULL) {
|
|
throwIOException(env, "Error creating event");
|
|
return -1;
|
|
}
|
|
|
|
char buff[256];
|
|
DWORD nread = sizeof(buff) < size ? sizeof(buff) : size;
|
|
#ifdef _WIN64
|
|
HANDLE handle = (HANDLE)jhandle;
|
|
#else
|
|
HANDLE handle = (HANDLE)(unsigned)jhandle;
|
|
#endif
|
|
|
|
if (!ReadFile(handle, buff, sizeof(buff), &nread, &olp)) {
|
|
if (GetLastError() != ERROR_IO_PENDING) {
|
|
throwIOException(env, "Error reading from port");
|
|
CloseHandle(olp.hEvent);
|
|
return -1;
|
|
} else {
|
|
switch (WaitForSingleObject(olp.hEvent, INFINITE)) {
|
|
case WAIT_OBJECT_0:
|
|
if (!GetOverlappedResult(handle, &olp, &nread, FALSE)) {
|
|
if (GetLastError() != ERROR_OPERATION_ABORTED) {
|
|
throwIOException(env, "Error waiting for read");
|
|
}
|
|
CloseHandle(olp.hEvent);
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nread > 0) {
|
|
(*env)->SetByteArrayRegion(env, bytes, offset, nread, (jbyte *)buff);
|
|
}
|
|
CloseHandle(olp.hEvent);
|
|
return nread;
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT void JNICALL FUNC(write0)(JNIEnv *env, jobject jobj, jlong jhandle, jint b)
|
|
{
|
|
#ifndef __MINGW32__
|
|
char buff = b;
|
|
write(jhandle, &buff, 1);
|
|
#else
|
|
OVERLAPPED olp = { 0 };
|
|
|
|
olp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (olp.hEvent == NULL) {
|
|
throwIOException(env, "Error creating event");
|
|
return;
|
|
}
|
|
|
|
char buff = (char)b;
|
|
DWORD nwritten;
|
|
#ifdef _WIN64
|
|
HANDLE handle = (HANDLE)jhandle;
|
|
#else
|
|
HANDLE handle = (HANDLE)(unsigned)jhandle;
|
|
#endif
|
|
|
|
if (!WriteFile(handle, &buff, sizeof(buff), &nwritten, &olp)) {
|
|
if (GetLastError() != ERROR_IO_PENDING) {
|
|
throwIOException(env, "Error writing to port");
|
|
}
|
|
else {
|
|
switch (WaitForSingleObject(olp.hEvent, INFINITE)) {
|
|
case WAIT_OBJECT_0:
|
|
if (!GetOverlappedResult(handle, &olp, &nwritten, FALSE)) {
|
|
throwIOException(env, "Error waiting for write");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(olp.hEvent);
|
|
#endif
|
|
}
|
|
|
|
JNIEXPORT void JNICALL FUNC(write1)(JNIEnv *env, jobject jobj, jlong jhandle, jbyteArray bytes, jint offset, jint size)
|
|
{
|
|
#ifndef __MINGW32__
|
|
while (size > 0) {
|
|
jbyte buff[256];
|
|
int n = size < sizeof(buff) ? size : sizeof(buff);
|
|
(*env)->GetByteArrayRegion(env, bytes, offset, n, buff);
|
|
n = write(jhandle, buff, n);
|
|
if (n < 0) {
|
|
return;
|
|
}
|
|
size -= n;
|
|
offset += n;
|
|
}
|
|
#else
|
|
OVERLAPPED olp = { 0 };
|
|
|
|
olp.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
if (olp.hEvent == NULL) {
|
|
throwIOException(env, "Error creating event");
|
|
return;
|
|
}
|
|
|
|
while (size > 0) {
|
|
char buff[256];
|
|
DWORD nwritten = sizeof(buff) < size ? sizeof(buff) : size;
|
|
(*env)->GetByteArrayRegion(env, bytes, offset, nwritten, (jbyte *)buff);
|
|
#ifdef _WIN64
|
|
HANDLE handle = (HANDLE)jhandle;
|
|
#else
|
|
HANDLE handle = (HANDLE)(unsigned)jhandle;
|
|
#endif
|
|
|
|
if (!WriteFile(handle, buff, nwritten, &nwritten, &olp)) {
|
|
if (GetLastError() != ERROR_IO_PENDING) {
|
|
throwIOException(env, "Error writing to port");
|
|
return;
|
|
}
|
|
else {
|
|
switch (WaitForSingleObject(olp.hEvent, INFINITE)) {
|
|
case WAIT_OBJECT_0:
|
|
if (!GetOverlappedResult(handle, &olp, &nwritten, FALSE)) {
|
|
throwIOException(env, "Error waiting for write");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
size -= nwritten;
|
|
offset += nwritten;
|
|
}
|
|
|
|
CloseHandle(olp.hEvent);
|
|
#endif
|
|
}
|
|
|
|
#ifdef __MINGW32__
|
|
JNIEXPORT jstring FUNC(getPortName)(JNIEnv *env, jclass cls, jint i)
|
|
{
|
|
HKEY key;
|
|
|
|
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_READ, &key) != ERROR_SUCCESS) {
|
|
// There are none
|
|
return NULL;
|
|
}
|
|
|
|
wchar_t name[256];
|
|
DWORD len = sizeof(name);
|
|
LONG rc = RegEnumValue(key, (DWORD)i, name, &len, NULL, NULL, NULL, NULL);
|
|
if (rc != ERROR_SUCCESS) {
|
|
if (rc != ERROR_NO_MORE_ITEMS) {
|
|
throwIOException(env, "Can not enum value");
|
|
}
|
|
RegCloseKey(key);
|
|
return NULL;
|
|
}
|
|
|
|
wchar_t value[256];
|
|
DWORD type;
|
|
len = sizeof(value);
|
|
if (RegQueryValueEx(key, name, NULL, &type, (BYTE *)value, &len) != ERROR_SUCCESS) {
|
|
throwIOException(env, "Can not query value");
|
|
RegCloseKey(key);
|
|
return NULL;
|
|
}
|
|
|
|
jstring result = (*env)->NewString(env, (jchar *)value, (jsize) wcslen(value));
|
|
RegCloseKey(key);
|
|
return result;
|
|
}
|
|
#endif
|