Complete rework of Windows native code

This commit is contained in:
Will Hedgecock 2021-11-17 01:20:30 -06:00
parent 20ea873390
commit 3ee680b155
5 changed files with 372 additions and 315 deletions

View File

@ -2,7 +2,7 @@
* SerialPort_Windows.c * SerialPort_Windows.c
* *
* Created on: Feb 25, 2012 * Created on: Feb 25, 2012
* Last Updated on: Nov 03, 2021 * Last Updated on: Nov 12, 2021
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2021 Fazecast, Inc. * Copyright (C) 2012-2021 Fazecast, Inc.
@ -36,7 +36,6 @@
#include <setupapi.h> #include <setupapi.h>
#include <devpkey.h> #include <devpkey.h>
#include "ftdi/ftd2xx.h" #include "ftdi/ftd2xx.h"
#include "../com_fazecast_jSerialComm_SerialPort.h"
#include "WindowsHelperFunctions.h" #include "WindowsHelperFunctions.h"
// Cached class, method, and field IDs // Cached class, method, and field IDs
@ -65,7 +64,7 @@ jfieldID readTimeoutField;
jfieldID writeTimeoutField; jfieldID writeTimeoutField;
jfieldID eventFlagsField; jfieldID eventFlagsField;
// Run-time loadable DLL function // Runtime-loadable DLL functions
typedef int (__stdcall *FT_CreateDeviceInfoListFunction)(LPDWORD); typedef int (__stdcall *FT_CreateDeviceInfoListFunction)(LPDWORD);
typedef int (__stdcall *FT_GetDeviceInfoListFunction)(FT_DEVICE_LIST_INFO_NODE*, LPDWORD); typedef int (__stdcall *FT_GetDeviceInfoListFunction)(FT_DEVICE_LIST_INFO_NODE*, LPDWORD);
typedef int (__stdcall *FT_GetComPortNumberFunction)(FT_HANDLE, LPLONG); typedef int (__stdcall *FT_GetComPortNumberFunction)(FT_HANDLE, LPLONG);
@ -73,6 +72,9 @@ typedef int (__stdcall *FT_SetLatencyTimerFunction)(FT_HANDLE, UCHAR);
typedef int (__stdcall *FT_OpenFunction)(int, FT_HANDLE*); typedef int (__stdcall *FT_OpenFunction)(int, FT_HANDLE*);
typedef int (__stdcall *FT_CloseFunction)(FT_HANDLE); typedef int (__stdcall *FT_CloseFunction)(FT_HANDLE);
// List of available serial ports
serialPortVector serialPorts = { NULL, 0 };
JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialComm) JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommPorts(JNIEnv *env, jclass serialComm)
{ {
HKEY keyHandle1, keyHandle2, keyHandle3, keyHandle4, keyHandle5; HKEY keyHandle1, keyHandle2, keyHandle3, keyHandle4, keyHandle5;
@ -82,7 +84,6 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
DWORD subKeyLength1, subKeyLength2, subKeyLength3, friendlyNameLength; DWORD subKeyLength1, subKeyLength2, subKeyLength3, friendlyNameLength;
// Enumerate serial ports on machine // Enumerate serial ports on machine
charTupleVector serialCommPorts = { (wchar_t**)malloc(sizeof(wchar_t*)), (wchar_t**)malloc(sizeof(wchar_t*)), (wchar_t**)malloc(sizeof(wchar_t*)), 0 };
if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &keyHandle1) == ERROR_SUCCESS) && if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DEVICEMAP\\SERIALCOMM", 0, KEY_QUERY_VALUE, &keyHandle1) == ERROR_SUCCESS) &&
(RegQueryInfoKeyW(keyHandle1, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, &maxValueLength, &maxComPortLength, NULL, NULL) == ERROR_SUCCESS)) (RegQueryInfoKeyW(keyHandle1, NULL, NULL, NULL, NULL, NULL, NULL, &numValues, &maxValueLength, &maxComPortLength, NULL, NULL) == ERROR_SUCCESS))
{ {
@ -106,8 +107,12 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
wchar_t* comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort; wchar_t* comPortString = (comPort[0] == L'\\') ? (wcsrchr(comPort, L'\\') + 1) : comPort;
wchar_t* descriptionString = wcsrchr(valueName, L'\\') ? (wcsrchr(valueName, L'\\') + 1) : valueName; wchar_t* descriptionString = wcsrchr(valueName, L'\\') ? (wcsrchr(valueName, L'\\') + 1) : valueName;
// Add new SerialComm object to vector // Check if port is already enumerated
pushBack(&serialCommPorts, comPortString, descriptionString, descriptionString); serialPort *port = fetchPort(&serialPorts, comPortString);
if (port)
port->enumerated = 1;
else
pushBack(&serialPorts, comPortString, descriptionString, descriptionString);
} }
} }
@ -177,13 +182,15 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
wchar_t* descriptionString = friendlyName; wchar_t* descriptionString = friendlyName;
// Update friendly name if COM port is actually connected and present in the port list // Update friendly name if COM port is actually connected and present in the port list
int i; for (int i = 0; i < serialPorts.length; ++i)
for (i = 0; i < serialCommPorts.length; ++i) if (wcscmp(serialPorts.ports[i]->portPath, comPortString) == 0)
if (wcscmp(serialCommPorts.first[i], comPortString) == 0)
{ {
free(serialCommPorts.second[i]); wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[i]->friendlyName, (wcslen(descriptionString)+1)*sizeof(wchar_t));
serialCommPorts.second[i] = (wchar_t*)malloc((wcslen(descriptionString)+1)*sizeof(wchar_t)); if (newMemory)
wcscpy(serialCommPorts.second[i], descriptionString); {
serialPorts.ports[i]->friendlyName = newMemory;
wcscpy(serialPorts.ports[i]->friendlyName, descriptionString);
}
break; break;
} }
} }
@ -250,14 +257,15 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
if (SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, (BYTE*)portDescription, valueLength, NULL, 0)) if (SetupDiGetDevicePropertyW(devList, &devInfoData, &DEVPKEY_Device_BusReportedDeviceDesc, &devInfoPropType, (BYTE*)portDescription, valueLength, NULL, 0))
{ {
// Update port description if COM port is actually connected and present in the port list // Update port description if COM port is actually connected and present in the port list
int i; for (int i = 0; i < serialPorts.length; ++i)
for (i = 0; i < serialCommPorts.length; ++i) if (wcscmp(serialPorts.ports[i]->portPath, comPortString) == 0)
if (wcscmp(serialCommPorts.first[i], comPortString) == 0)
{ {
free(serialCommPorts.third[i]); wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[i]->portDescription, (wcslen(portDescription)+1)*sizeof(wchar_t));
serialCommPorts.third[i] = (wchar_t*)malloc((wcslen(portDescription)+1)*sizeof(wchar_t)); if (newMemory)
wcscpy(serialCommPorts.third[i], portDescription); {
break; serialPorts.ports[i]->portDescription = newMemory;
wcscpy(serialPorts.ports[i]->portDescription, portDescription);
}
} }
} }
@ -287,9 +295,8 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
FT_DEVICE_LIST_INFO_NODE *devInfo = (FT_DEVICE_LIST_INFO_NODE*)malloc(sizeof(FT_DEVICE_LIST_INFO_NODE)*numDevs); FT_DEVICE_LIST_INFO_NODE *devInfo = (FT_DEVICE_LIST_INFO_NODE*)malloc(sizeof(FT_DEVICE_LIST_INFO_NODE)*numDevs);
if (FT_GetDeviceInfoList(devInfo, &numDevs) == FT_OK) if (FT_GetDeviceInfoList(devInfo, &numDevs) == FT_OK)
{ {
int i, j;
wchar_t comPortString[128]; wchar_t comPortString[128];
for (i = 0; i < numDevs; ++i) for (int i = 0; i < numDevs; ++i)
{ {
LONG comPortNumber = 0; LONG comPortNumber = 0;
if ((FT_Open(i, &devInfo[i].ftHandle) == FT_OK) && (FT_GetComPortNumber(devInfo[i].ftHandle, &comPortNumber) == FT_OK)) if ((FT_Open(i, &devInfo[i].ftHandle) == FT_OK) && (FT_GetComPortNumber(devInfo[i].ftHandle, &comPortNumber) == FT_OK))
@ -302,13 +309,16 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
// Update port description if COM port is actually connected and present in the port list // Update port description if COM port is actually connected and present in the port list
FT_Close(devInfo[i].ftHandle); FT_Close(devInfo[i].ftHandle);
swprintf(comPortString, sizeof(comPortString) / sizeof(wchar_t), L"COM%ld", comPortNumber); swprintf(comPortString, sizeof(comPortString) / sizeof(wchar_t), L"COM%ld", comPortNumber);
for (j = 0; j < serialCommPorts.length; ++j) for (int j = 0; j < serialPorts.length; ++j)
if (wcscmp(serialCommPorts.first[j], comPortString) == 0) if (wcscmp(serialPorts.ports[j]->portPath, comPortString) == 0)
{ {
size_t descLength = 8+strlen(devInfo[i].Description); size_t descLength = 8+strlen(devInfo[i].Description);
free(serialCommPorts.third[j]); wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[j]->portDescription, descLength*sizeof(wchar_t));
serialCommPorts.third[j] = (wchar_t*)malloc(descLength*sizeof(wchar_t)); if (newMemory)
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].Description, -1, serialCommPorts.third[j], descLength); {
serialPorts.ports[j]->portDescription = newMemory;
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].Description, -1, serialPorts.ports[j]->portDescription, descLength);
}
break; break;
} }
} }
@ -321,28 +331,21 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
} }
// Get relevant SerialComm methods and fill in com port array // Get relevant SerialComm methods and fill in com port array
jobjectArray arrayObject = env->NewObjectArray(serialCommPorts.length, serialCommClass, 0);
wchar_t systemPortName[128]; wchar_t systemPortName[128];
int i; jobjectArray arrayObject = env->NewObjectArray(serialPorts.length, serialCommClass, 0);
for (i = 0; i < serialCommPorts.length; ++i) for (int i = 0; i < serialPorts.length; ++i)
{ {
// Create new SerialComm object containing the enumerated values // Create new SerialComm object containing the enumerated values
jobject serialCommObject = env->NewObject(serialCommClass, serialCommConstructor); jobject serialCommObject = env->NewObject(serialCommClass, serialCommConstructor);
wcscpy(systemPortName, L"\\\\.\\"); wcscpy(systemPortName, L"\\\\.\\");
wcscat(systemPortName, serialCommPorts.first[i]); wcscat(systemPortName, serialPorts.ports[i]->portPath);
env->SetObjectField(serialCommObject, comPortField, env->NewString((jchar*)systemPortName, wcslen(systemPortName))); env->SetObjectField(serialCommObject, comPortField, env->NewString((jchar*)systemPortName, wcslen(systemPortName)));
env->SetObjectField(serialCommObject, friendlyNameField, env->NewString((jchar*)serialCommPorts.second[i], wcslen(serialCommPorts.second[i]))); env->SetObjectField(serialCommObject, friendlyNameField, env->NewString((jchar*)serialPorts.ports[i]->friendlyName, wcslen(serialPorts.ports[i]->friendlyName)));
env->SetObjectField(serialCommObject, portDescriptionField, env->NewString((jchar*)serialCommPorts.third[i], wcslen(serialCommPorts.third[i]))); env->SetObjectField(serialCommObject, portDescriptionField, env->NewString((jchar*)serialPorts.ports[i]->portDescription, wcslen(serialPorts.ports[i]->portDescription)));
free(serialCommPorts.first[i]);
free(serialCommPorts.second[i]);
free(serialCommPorts.third[i]);
// Add new SerialComm object to array // Add new SerialComm object to array
env->SetObjectArrayElement(arrayObject, i, serialCommObject); env->SetObjectArrayElement(arrayObject, i, serialCommObject);
} }
free(serialCommPorts.first);
free(serialCommPorts.second);
free(serialCommPorts.third);
return arrayObject; return arrayObject;
} }
@ -385,48 +388,63 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_uninitializeLibr
JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(JNIEnv *env, jobject obj) JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_openPortNative(JNIEnv *env, jobject obj)
{ {
// Retrieve the serial port parameter fields
jstring portNameJString = (jstring)env->GetObjectField(obj, comPortField); jstring portNameJString = (jstring)env->GetObjectField(obj, comPortField);
const char *portName = env->GetStringUTFChars(portNameJString, NULL); const wchar_t *portName = (wchar_t*)env->GetStringChars(portNameJString, NULL);
unsigned char disableAutoConfig = env->GetBooleanField(obj, disableConfigField);
// Try to open existing serial port with read/write access // Ensure that the serial port still exists and is not already open
HANDLE serialPortHandle = INVALID_HANDLE_VALUE; serialPort *port = fetchPort(&serialPorts, portName);
if ((serialPortHandle = CreateFile(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, NULL)) != INVALID_HANDLE_VALUE) if (!port)
{ {
// Configure the port parameters and timeouts // Create port representation and add to serial port listing
if (Java_com_fazecast_jSerialComm_SerialPort_configPort(env, obj, (jlong)serialPortHandle)) port = pushBack(&serialPorts, portName, L"User-Specified Port", L"User-Specified Port");
env->SetLongField(obj, serialPortHandleField, (jlong)serialPortHandle); }
else if (!port || (port->handle != INVALID_HANDLE_VALUE))
{ {
// Close the port if there was a problem setting the parameters env->ReleaseStringChars(portNameJString, (const jchar*)portName);
int numRetries = 10; return 0;
PurgeComm(serialPortHandle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
while (!CloseHandle(serialPortHandle) && (numRetries-- > 0));
serialPortHandle = INVALID_HANDLE_VALUE;
env->SetLongField(obj, serialPortHandleField, -1l);
}
} }
env->ReleaseStringUTFChars(portNameJString, portName); // Try to open the serial port with read/write access
return (jlong)serialPortHandle; if ((port->handle = CreateFileW(portName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_OVERLAPPED, NULL)) != INVALID_HANDLE_VALUE)
{
// Configure the port parameters and timeouts
if (!disableAutoConfig && !Java_com_fazecast_jSerialComm_SerialPort_configPort(env, obj, (jlong)(intptr_t)port))
{
// Close the port if there was a problem setting the parameters
PurgeComm(port->handle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
CancelIoEx(port->handle, NULL);
CloseHandle(port->handle);
port->handle = INVALID_HANDLE_VALUE;
}
}
else
{
port->errorLineNumber = __LINE__ - 14;
port->errorNumber = GetLastError();
}
// Return a pointer to the serial port data structure
env->ReleaseStringChars(portNameJString, (const jchar*)portName);
return (port->handle != INVALID_HANDLE_VALUE) ? (jlong)(intptr_t)port : 0;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD; // Retrieve port parameters from the Java class
if (serialPortHandle == INVALID_HANDLE_VALUE) serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
return JNI_FALSE;
DCB dcbSerialParams{};
dcbSerialParams.DCBlength = sizeof(DCB);
// Get port parameters from Java class
DWORD baudRate = (DWORD)env->GetIntField(obj, baudRateField); DWORD baudRate = (DWORD)env->GetIntField(obj, baudRateField);
BYTE byteSize = (BYTE)env->GetIntField(obj, dataBitsField); BYTE byteSize = (BYTE)env->GetIntField(obj, dataBitsField);
int stopBitsInt = env->GetIntField(obj, stopBitsField); int stopBitsInt = env->GetIntField(obj, stopBitsField);
int parityInt = env->GetIntField(obj, parityField); int parityInt = env->GetIntField(obj, parityField);
int flowControl = env->GetIntField(obj, flowControlField); int flowControl = env->GetIntField(obj, flowControlField);
int timeoutMode = env->GetIntField(obj, timeoutModeField);
int readTimeout = env->GetIntField(obj, readTimeoutField);
int writeTimeout = env->GetIntField(obj, writeTimeoutField);
int eventsToMonitor = env->GetIntField(obj, eventFlagsField);
DWORD sendDeviceQueueSize = (DWORD)env->GetIntField(obj, sendDeviceQueueSizeField); DWORD sendDeviceQueueSize = (DWORD)env->GetIntField(obj, sendDeviceQueueSizeField);
DWORD receiveDeviceQueueSize = (DWORD)env->GetIntField(obj, receiveDeviceQueueSizeField); DWORD receiveDeviceQueueSize = (DWORD)env->GetIntField(obj, receiveDeviceQueueSizeField);
BYTE configDisabled = (BYTE)env->GetBooleanField(obj, disableConfigField);
BYTE rs485ModeEnabled = (BYTE)env->GetBooleanField(obj, rs485ModeField); BYTE rs485ModeEnabled = (BYTE)env->GetBooleanField(obj, rs485ModeField);
BYTE isDtrEnabled = env->GetBooleanField(obj, isDtrEnabledField); BYTE isDtrEnabled = env->GetBooleanField(obj, isDtrEnabledField);
BYTE isRtsEnabled = env->GetBooleanField(obj, isRtsEnabledField); BYTE isRtsEnabled = env->GetBooleanField(obj, isRtsEnabledField);
@ -444,7 +462,9 @@ JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort(J
BOOL XonXoffOutEnabled = ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED) > 0); BOOL XonXoffOutEnabled = ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED) > 0);
// Retrieve existing port configuration // Retrieve existing port configuration
if (!configDisabled && (!SetupComm(serialPortHandle, receiveDeviceQueueSize, sendDeviceQueueSize) || !GetCommState(serialPortHandle, &dcbSerialParams))) DCB dcbSerialParams{};
dcbSerialParams.DCBlength = sizeof(DCB);
if ((!SetupComm(port->handle, receiveDeviceQueueSize, sendDeviceQueueSize) || !GetCommState(port->handle, &dcbSerialParams)))
return JNI_FALSE; return JNI_FALSE;
// Set updated port parameters // Set updated port parameters
@ -471,101 +491,79 @@ JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort(J
dcbSerialParams.XoffChar = (char)19; dcbSerialParams.XoffChar = (char)19;
// Apply changes // Apply changes
return (configDisabled ? Java_com_fazecast_jSerialComm_SerialPort_configEventFlags(env, obj, serialPortFD) : return (SetCommState(port->handle, &dcbSerialParams) && Java_com_fazecast_jSerialComm_SerialPort_configTimeouts(env, obj, serialPortPointer, timeoutMode, readTimeout, writeTimeout, eventsToMonitor));
(SetCommState(serialPortHandle, &dcbSerialParams) && Java_com_fazecast_jSerialComm_SerialPort_configEventFlags(env, obj, serialPortFD)));
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configTimeouts(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configTimeouts(JNIEnv *env, jobject obj, jlong serialPortPointer, jint timeoutMode, jint readTimeout, jint writeTimeout, jint eventsToMonitor)
{ {
// Get port timeouts from Java class
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
COMMTIMEOUTS timeouts{};
int timeoutMode = env->GetIntField(obj, timeoutModeField);
DWORD readTimeout = (DWORD)env->GetIntField(obj, readTimeoutField);
DWORD writeTimeout = (DWORD)env->GetIntField(obj, writeTimeoutField);
// Set updated port timeouts
timeouts.WriteTotalTimeoutMultiplier = 0;
switch (timeoutMode)
{
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING: // Read Semi-blocking
case (com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING | com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING): // Read Semi-blocking/Write Blocking
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = readTimeout ? readTimeout : 0x0FFFFFFF;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING: // Read Blocking
case (com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING | com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING): // Read/Write Blocking
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = readTimeout;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_SCANNER: // Scanner Mode
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 0x0FFFFFFF;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_NONBLOCKING: // Read Non-blocking
case (com_fazecast_jSerialComm_SerialPort_TIMEOUT_NONBLOCKING | com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING): // Read Non-blocking/Write Blocking
default:
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
}
// Apply changes
return SetCommTimeouts(serialPortHandle, &timeouts);
}
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configEventFlags(JNIEnv *env, jobject obj, jlong serialPortFD)
{
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
// Get event flags from Java class // Get event flags from Java class
int eventsToMonitor = env->GetIntField(obj, eventFlagsField);
int eventFlags = EV_ERR; int eventFlags = EV_ERR;
serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_AVAILABLE) > 0) || if (((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_AVAILABLE) > 0) ||
((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED) > 0)) ((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED) > 0))
eventFlags |= EV_RXCHAR; eventFlags |= EV_RXCHAR;
if ((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_WRITTEN) > 0) if ((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_WRITTEN) > 0)
eventFlags |= EV_TXEMPTY; eventFlags |= EV_TXEMPTY;
// Change read timeouts if we are monitoring data received // Set updated port timeouts
COMMTIMEOUTS timeouts{};
timeouts.WriteTotalTimeoutMultiplier = 0;
if ((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED) > 0) if ((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED) > 0)
{ {
COMMTIMEOUTS timeouts{}; // Force specific read timeouts if we are monitoring data received
timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD; timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 1000; timeouts.ReadTotalTimeoutConstant = 1000;
timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.WriteTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(serialPortHandle, &timeouts);
} }
else else
Java_com_fazecast_jSerialComm_SerialPort_configTimeouts(env, obj, serialPortFD); {
switch (timeoutMode)
{
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING: // Read Semi-blocking
case (com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING | com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING): // Read Semi-blocking/Write Blocking
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = readTimeout ? readTimeout : 0x0FFFFFFF;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING: // Read Blocking
case (com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING | com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING): // Read/Write Blocking
timeouts.ReadIntervalTimeout = 0;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = readTimeout;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_SCANNER: // Scanner Mode
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 0x0FFFFFFF;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
case com_fazecast_jSerialComm_SerialPort_TIMEOUT_NONBLOCKING: // Read Non-blocking
case (com_fazecast_jSerialComm_SerialPort_TIMEOUT_NONBLOCKING | com_fazecast_jSerialComm_SerialPort_TIMEOUT_WRITE_BLOCKING): // Read Non-blocking/Write Blocking
default:
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutConstant = writeTimeout;
break;
}
}
// Apply changes // Apply changes
return SetCommMask(serialPortHandle, eventFlags); return (SetCommTimeouts(port->handle, &timeouts) && SetCommMask(port->handle, eventFlags));
} }
JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return 0;
OVERLAPPED overlappedStruct{}; OVERLAPPED overlappedStruct{};
serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
overlappedStruct.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); overlappedStruct.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlappedStruct.hEvent == NULL) if (overlappedStruct.hEvent == NULL)
{ {
port->errorNumber = GetLastError();
port->errorLineNumber = __LINE__ - 4;
CloseHandle(overlappedStruct.hEvent); CloseHandle(overlappedStruct.hEvent);
return 0; return 0;
} }
@ -573,12 +571,12 @@ JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent(JNI
// Wait for a serial port event // Wait for a serial port event
BOOL listenerRunning = TRUE; BOOL listenerRunning = TRUE;
DWORD eventMask = 0, numBytesRead = 0, errorsMask, readResult = WAIT_FAILED; DWORD eventMask = 0, numBytesRead = 0, errorsMask, readResult = WAIT_FAILED;
if (WaitCommEvent(serialPortHandle, &eventMask, &overlappedStruct) == FALSE) if (WaitCommEvent(port->handle, &eventMask, &overlappedStruct) == FALSE)
{ {
if (GetLastError() != ERROR_IO_PENDING) // Problem occurred if (GetLastError() != ERROR_IO_PENDING) // Problem occurred
{ {
// Problem reading, close port port->errorLineNumber = __LINE__ - 4;
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD); port->errorNumber = GetLastError();
listenerRunning = FALSE; listenerRunning = FALSE;
} }
else else
@ -590,11 +588,14 @@ JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent(JNI
readResult = WaitForSingleObject(overlappedStruct.hEvent, 500); readResult = WaitForSingleObject(overlappedStruct.hEvent, 500);
else else
{ {
CancelIo(serialPortHandle); // Purge any outstanding port operations
PurgeComm(port->handle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
CancelIoEx(port->handle, NULL);
FlushFileBuffers(port->handle);
readResult = WaitForSingleObject(overlappedStruct.hEvent, INFINITE); readResult = WaitForSingleObject(overlappedStruct.hEvent, INFINITE);
} }
} while ((readResult == WAIT_TIMEOUT) && listenerRunning); } while ((readResult == WAIT_TIMEOUT) && listenerRunning);
if ((readResult != WAIT_OBJECT_0) || (GetOverlappedResult(serialPortHandle, &overlappedStruct, &numBytesRead, FALSE) == FALSE)) if ((readResult != WAIT_OBJECT_0) || (GetOverlappedResult(port->handle, &overlappedStruct, &numBytesRead, FALSE) == FALSE))
numBytesRead = 0; numBytesRead = 0;
} }
} }
@ -603,9 +604,7 @@ JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent(JNI
if (listenerRunning) if (listenerRunning)
{ {
COMSTAT commInfo; COMSTAT commInfo;
if (!ClearCommError(serialPortHandle, &errorsMask, &commInfo)) if (ClearCommError(port->handle, &errorsMask, &commInfo))
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD);
else
numBytesRead = commInfo.cbInQue; numBytesRead = commInfo.cbInQue;
} }
@ -615,88 +614,80 @@ JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_waitForEvent(JNI
(((eventMask & EV_TXEMPTY) > 0) ? com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_WRITTEN : 0); (((eventMask & EV_TXEMPTY) > 0) ? com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_WRITTEN : 0);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_closePortNative(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jlong JNICALL Java_com_fazecast_jSerialComm_SerialPort_closePortNative(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
// Purge any outstanding port operations
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_TRUE;
PurgeComm(serialPortHandle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
// Force the port to enter non-blocking mode to ensure that any current reads return // Force the port to enter non-blocking mode to ensure that any current reads return
COMMTIMEOUTS timeouts{}; COMMTIMEOUTS timeouts{};
serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutMultiplier = 0;
timeouts.ReadIntervalTimeout = MAXDWORD; timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutMultiplier = 0;
timeouts.ReadTotalTimeoutConstant = 0; timeouts.ReadTotalTimeoutConstant = 0;
timeouts.WriteTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutConstant = 0;
SetCommTimeouts(serialPortHandle, &timeouts); SetCommTimeouts(port->handle, &timeouts);
// Purge any outstanding port operations
PurgeComm(port->handle, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
CancelIoEx(port->handle, NULL);
FlushFileBuffers(port->handle);
// Close the port // Close the port
int numRetries = 10; if (!CloseHandle(port->handle))
while (!CloseHandle(serialPortHandle) && (GetLastError() != ERROR_INVALID_HANDLE) && (numRetries-- > 0));
if (numRetries > 0)
{ {
env->SetLongField(obj, serialPortHandleField, -1l); port->handle = INVALID_HANDLE_VALUE;
return JNI_TRUE; port->errorLineNumber = __LINE__ - 3;
port->errorNumber = GetLastError();
return 0;
} }
port->handle = INVALID_HANDLE_VALUE;
// Error closing port, reconfigure port settings return -1;
Java_com_fazecast_jSerialComm_SerialPort_configEventFlags(env, obj, serialPortFD);
return JNI_FALSE;
} }
JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_bytesAvailable(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_bytesAvailable(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return -1;
// Retrieve bytes available to read // Retrieve bytes available to read
COMSTAT commInfo; COMSTAT commInfo;
DWORD errorsMask; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (!ClearCommError(serialPortHandle, &errorsMask, &commInfo)) if (ClearCommError(port->handle, NULL, &commInfo))
return commInfo.cbInQue;
else
{ {
// Problem detected, close port port->errorLineNumber = __LINE__ - 4;
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD); port->errorNumber = GetLastError();
serialPortHandle = INVALID_HANDLE_VALUE;
} }
return (serialPortHandle == INVALID_HANDLE_VALUE) ? -1 : commInfo.cbInQue; return -1;
} }
JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_bytesAwaitingWrite(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_bytesAwaitingWrite(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return -1;
// Retrieve bytes awaiting write // Retrieve bytes awaiting write
COMSTAT commInfo; COMSTAT commInfo;
DWORD errorsMask; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (!ClearCommError(serialPortHandle, &errorsMask, &commInfo)) if (ClearCommError(port->handle, NULL, &commInfo))
return commInfo.cbOutQue;
else
{ {
// Problem detected, close port port->errorLineNumber = __LINE__ - 4;
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD); port->errorNumber = GetLastError();
serialPortHandle = INVALID_HANDLE_VALUE;
} }
return (serialPortHandle == INVALID_HANDLE_VALUE) ? -1 : commInfo.cbOutQue; return -1;
} }
JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_readBytes(JNIEnv *env, jobject obj, jlong serialPortFD, jbyteArray buffer, jlong bytesToRead, jlong offset) JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_readBytes(JNIEnv *env, jobject obj, jlong serialPortPointer, jbyteArray buffer, jlong bytesToRead, jlong offset, jint timeoutMode, jint readTimeout)
{ {
// Retrieve the serial port handle // Ensure that the allocated read buffer is large enough
HANDLE serialPortHandle = (HANDLE)serialPortFD; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (serialPortHandle == INVALID_HANDLE_VALUE) if (bytesToRead > port->readBufferLength)
return -1;
// Check for a disconnected serial port
COMSTAT commInfo;
DWORD errorsMask;
if (!ClearCommError(serialPortHandle, &errorsMask, &commInfo))
{ {
// Problem detected, close port port->errorLineNumber = __LINE__ + 1;
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD); char *newMemory = (char*)realloc(port->readBuffer, bytesToRead);
return -1; if (!newMemory)
{
port->errorNumber = errno;
return -1;
}
port->readBuffer = newMemory;
port->readBufferLength = bytesToRead;
} }
// Create an asynchronous result structure // Create an asynchronous result structure
@ -704,97 +695,113 @@ JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_readBytes(JNIEnv
overlappedStruct.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); overlappedStruct.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlappedStruct.hEvent == NULL) if (overlappedStruct.hEvent == NULL)
{ {
port->errorNumber = GetLastError();
port->errorLineNumber = __LINE__ - 4;
CloseHandle(overlappedStruct.hEvent); CloseHandle(overlappedStruct.hEvent);
return -1; return -1;
} }
char *readBuffer = (char*)malloc(bytesToRead);
DWORD numBytesRead = 0;
// Read from serial port and close upon error // Read from the serial port
BOOL result; BOOL result;
if (((result = ReadFile(serialPortHandle, readBuffer, bytesToRead, NULL, &overlappedStruct)) == FALSE) && (GetLastError() != ERROR_IO_PENDING)) DWORD numBytesRead = 0;
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD); if (((result = ReadFile(port->handle, port->readBuffer, bytesToRead, NULL, &overlappedStruct)) == FALSE) && (GetLastError() != ERROR_IO_PENDING))
else if ((result = GetOverlappedResult(serialPortHandle, &overlappedStruct, &numBytesRead, TRUE)) == FALSE) {
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD); port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
}
else if ((result = GetOverlappedResult(port->handle, &overlappedStruct, &numBytesRead, TRUE)) == FALSE)
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
}
// Return number of bytes read if successful // Return number of bytes read
CloseHandle(overlappedStruct.hEvent); CloseHandle(overlappedStruct.hEvent);
env->SetByteArrayRegion(buffer, offset, numBytesRead, (jbyte*)readBuffer); env->SetByteArrayRegion(buffer, offset, numBytesRead, (jbyte*)port->readBuffer);
free(readBuffer);
return (result == TRUE) ? numBytesRead : -1; return (result == TRUE) ? numBytesRead : -1;
} }
JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_writeBytes(JNIEnv *env, jobject obj, jlong serialPortFD, jbyteArray buffer, jlong bytesToWrite, jlong offset) JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_writeBytes(JNIEnv *env, jobject obj, jlong serialPortPointer, jbyteArray buffer, jlong bytesToWrite, jlong offset, jint timeoutMode)
{ {
// Retrieve the serial port handle
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return -1;
// Check for a disconnected serial port
COMSTAT commInfo;
DWORD errorsMask;
if (!ClearCommError(serialPortHandle, &errorsMask, &commInfo))
{
// Problem detected, close port
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD);
return -1;
}
// Create an asynchronous result structure // Create an asynchronous result structure
OVERLAPPED overlappedStruct{}; OVERLAPPED overlappedStruct{};
serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
overlappedStruct.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); overlappedStruct.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (overlappedStruct.hEvent == NULL) if (overlappedStruct.hEvent == NULL)
{ {
port->errorNumber = GetLastError();
port->errorLineNumber = __LINE__ - 4;
CloseHandle(overlappedStruct.hEvent); CloseHandle(overlappedStruct.hEvent);
return -1; return -1;
} }
jbyte *writeBuffer = env->GetByteArrayElements(buffer, 0);
DWORD numBytesWritten = 0; // Write to the serial port
BOOL result; BOOL result;
DWORD numBytesWritten = 0;
jbyte *writeBuffer = env->GetByteArrayElements(buffer, 0);
if (((result = WriteFile(port->handle, writeBuffer+offset, bytesToWrite, NULL, &overlappedStruct)) == FALSE) && (GetLastError() != ERROR_IO_PENDING))
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
}
else if ((result = GetOverlappedResult(port->handle, &overlappedStruct, &numBytesWritten, TRUE)) == FALSE)
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
}
// Write to serial port and close upon error // Return number of bytes written
if (((result = WriteFile(serialPortHandle, writeBuffer+offset, bytesToWrite, NULL, &overlappedStruct)) == FALSE) && (GetLastError() != ERROR_IO_PENDING))
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD);
else if ((result = GetOverlappedResult(serialPortHandle, &overlappedStruct, &numBytesWritten, TRUE)) == FALSE)
Java_com_fazecast_jSerialComm_SerialPort_closePortNative(env, obj, serialPortFD);
// Return number of bytes written if successful
CloseHandle(overlappedStruct.hEvent); CloseHandle(overlappedStruct.hEvent);
env->ReleaseByteArrayElements(buffer, writeBuffer, JNI_ABORT); env->ReleaseByteArrayElements(buffer, writeBuffer, JNI_ABORT);
return (result == TRUE) ? numBytesWritten : -1; return (result == TRUE) ? numBytesWritten : -1;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_setBreak(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_setBreak(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (serialPortHandle == INVALID_HANDLE_VALUE) if (!SetCommBreak(port->handle))
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
return JNI_FALSE; return JNI_FALSE;
return SetCommBreak(serialPortHandle); }
return JNI_TRUE;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_clearBreak(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_clearBreak(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (serialPortHandle == INVALID_HANDLE_VALUE) if (!ClearCommBreak(port->handle))
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
return JNI_FALSE; return JNI_FALSE;
return ClearCommBreak(serialPortHandle); }
return JNI_TRUE;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_setRTS(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_setRTS(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (serialPortHandle == INVALID_HANDLE_VALUE) if (!EscapeCommFunction(port->handle, SETRTS))
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
return JNI_FALSE; return JNI_FALSE;
return EscapeCommFunction(serialPortHandle, SETRTS); }
return JNI_TRUE;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_clearRTS(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_clearRTS(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (serialPortHandle == INVALID_HANDLE_VALUE) if (!EscapeCommFunction(port->handle, CLRRTS))
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
return JNI_FALSE; return JNI_FALSE;
return EscapeCommFunction(serialPortHandle, CLRRTS); }
return JNI_TRUE;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_presetRTS(JNIEnv *env, jobject obj) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_presetRTS(JNIEnv *env, jobject obj)
@ -855,20 +862,28 @@ JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_preclearRTS(
return (result != 0); return (result != 0);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_setDTR(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_setDTR(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (serialPortHandle == INVALID_HANDLE_VALUE) if (!EscapeCommFunction(port->handle, SETDTR))
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
return JNI_FALSE; return JNI_FALSE;
return EscapeCommFunction(serialPortHandle, SETDTR); }
return JNI_TRUE;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_clearDTR(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_clearDTR(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD; serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
if (serialPortHandle == INVALID_HANDLE_VALUE) if (!EscapeCommFunction(port->handle, CLRDTR))
{
port->errorLineNumber = __LINE__ - 2;
port->errorNumber = GetLastError();
return JNI_FALSE; return JNI_FALSE;
return EscapeCommFunction(serialPortHandle, CLRDTR); }
return JNI_TRUE;
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_presetDTR(JNIEnv *env, jobject obj) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_presetDTR(JNIEnv *env, jobject obj)
@ -929,56 +944,48 @@ JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_preclearDTR(
return (result != 0); return (result != 0);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCTS(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCTS(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
DWORD modemStatus = 0; DWORD modemStatus = 0;
return GetCommModemStatus(serialPortHandle, &modemStatus) && (modemStatus & MS_CTS_ON); return GetCommModemStatus(((serialPort*)(intptr_t)serialPortPointer)->handle, &modemStatus) && (modemStatus & MS_CTS_ON);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getDSR(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getDSR(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
DWORD modemStatus = 0; DWORD modemStatus = 0;
return GetCommModemStatus(serialPortHandle, &modemStatus) && (modemStatus & MS_DSR_ON); return GetCommModemStatus(((serialPort*)(intptr_t)serialPortPointer)->handle, &modemStatus) && (modemStatus & MS_DSR_ON);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getDCD(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getDCD(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
DWORD modemStatus = 0; DWORD modemStatus = 0;
return GetCommModemStatus(serialPortHandle, &modemStatus) && (modemStatus & MS_RLSD_ON); return GetCommModemStatus(((serialPort*)(intptr_t)serialPortPointer)->handle, &modemStatus) && (modemStatus & MS_RLSD_ON);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getDTR(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getDTR(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
return env->GetBooleanField(obj, isDtrEnabledField); return env->GetBooleanField(obj, isDtrEnabledField);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getRTS(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getRTS(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
return env->GetBooleanField(obj, isRtsEnabledField); return env->GetBooleanField(obj, isRtsEnabledField);
} }
JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getRI(JNIEnv *env, jobject obj, jlong serialPortFD) JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_getRI(JNIEnv *env, jobject obj, jlong serialPortPointer)
{ {
HANDLE serialPortHandle = (HANDLE)serialPortFD;
if (serialPortHandle == INVALID_HANDLE_VALUE)
return JNI_FALSE;
DWORD modemStatus = 0; DWORD modemStatus = 0;
return GetCommModemStatus(serialPortHandle, &modemStatus) && (modemStatus & MS_RING_ON); return GetCommModemStatus(((serialPort*)(intptr_t)serialPortPointer)->handle, &modemStatus) && (modemStatus & MS_RING_ON);
}
JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_getLastErrorLocation(JNIEnv *env, jobject obj, jlong serialPortPointer)
{
return ((serialPort*)(intptr_t)serialPortPointer)->errorLineNumber;
}
JNIEXPORT jint JNICALL Java_com_fazecast_jSerialComm_SerialPort_getLastErrorCode(JNIEnv *env, jobject obj, jlong serialPortPointer)
{
return ((serialPort*)(intptr_t)serialPortPointer)->errorNumber;
} }
#endif #endif

View File

@ -2,7 +2,7 @@
* WindowsHelperFunctions.c * WindowsHelperFunctions.c
* *
* Created on: May 05, 2015 * Created on: May 05, 2015
* Last Updated on: Nov 03, 2021 * Last Updated on: Nov 14, 2021
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2021 Fazecast, Inc. * Copyright (C) 2012-2021 Fazecast, Inc.
@ -28,37 +28,73 @@
#include <string.h> #include <string.h>
#include "WindowsHelperFunctions.h" #include "WindowsHelperFunctions.h"
void pushBack(struct charTupleVector* vector, const wchar_t* key, const wchar_t* firstString, const wchar_t* secondString) // Common storage functionality
serialPort* pushBack(serialPortVector* vector, const wchar_t* key, const wchar_t* friendlyName, const wchar_t* description)
{ {
// Allocate memory for new string storage // Allocate memory for the new SerialPort storage structure
vector->length++; if (vector->capacity == vector->length)
wchar_t** newMemory = (wchar_t**)realloc(vector->first, vector->length*sizeof(wchar_t*)); {
if (newMemory) serialPort** newArray = (serialPort**)realloc(vector->ports, ++vector->capacity * sizeof(serialPort*));
vector->first = newMemory; if (newArray)
newMemory = (wchar_t**)realloc(vector->second, vector->length*sizeof(wchar_t*)); vector->ports = newArray;
if (newMemory) else
vector->second = newMemory; {
newMemory = (wchar_t**)realloc(vector->third, vector->length*sizeof(wchar_t*)); vector->capacity--;
if (newMemory) return NULL;
vector->third = newMemory; }
}
serialPort* port = (serialPort*)malloc(sizeof(serialPort));
if (port)
vector->ports[vector->length++] = port;
else
return NULL;
// Store new strings // Initialize the storage structure
vector->first[vector->length-1] = (wchar_t*)malloc((wcslen(key)+1)*sizeof(wchar_t)); memset(port, 0, sizeof(serialPort));
vector->second[vector->length-1] = (wchar_t*)malloc((wcslen(firstString)+1)*sizeof(wchar_t)); port->handle = (void*)-1;
vector->third[vector->length-1] = (wchar_t*)malloc((wcslen(secondString)+1)*sizeof(wchar_t)); port->enumerated = 1;
wcscpy(vector->first[vector->length-1], key); port->portPath = (wchar_t*)malloc((wcslen(key)+1)*sizeof(wchar_t));
wcscpy(vector->second[vector->length-1], firstString); port->friendlyName = (wchar_t*)malloc((wcslen(friendlyName)+1)*sizeof(wchar_t));
wcscpy(vector->third[vector->length-1], secondString); port->portDescription = (wchar_t*)malloc((wcslen(description)+1)*sizeof(wchar_t));
// Store port strings
wcscpy(port->portPath, key);
wcscpy(port->friendlyName, friendlyName);
wcscpy(port->portDescription, description);
// Return the newly created serial port structure
return port;
} }
char keyExists(struct charTupleVector* vector, const wchar_t* key) serialPort* fetchPort(serialPortVector* vector, const wchar_t* key)
{ {
// Search for a vector item with a matching key for (int i = 0; i < vector->length; ++i)
size_t i; if (wcscmp(key, vector->ports[i]->portPath) == 0)
for (i = 0; i < vector->length; ++i) return vector->ports[i];
if (wcscmp(key, vector->first[i]) == 0) return NULL;
return 1; }
return 0;
void removePort(serialPortVector* vector, serialPort* port)
{
// Clean up memory associated with the port
free(port->portPath);
free(port->friendlyName);
free(port->portDescription);
if (port->readBuffer)
free(port->readBuffer);
// Move up all remaining ports in the serial port listing
for (int i = 0; i < vector->length; ++i)
if (vector->ports[i] == port)
{
for (int j = i; j < (vector->length - 1); ++j)
vector->ports[j] = vector->ports[j+1];
vector->length--;
break;
}
// Free the serial port structure memory
free(port);
} }
#endif #endif

View File

@ -2,7 +2,7 @@
* WindowsHelperFunctions.h * WindowsHelperFunctions.h
* *
* Created on: May 05, 2015 * Created on: May 05, 2015
* Last Updated on: Nov 03, 2021 * Last Updated on: Nov 14, 2021
* Author: Will Hedgecock * Author: Will Hedgecock
* *
* Copyright (C) 2012-2021 Fazecast, Inc. * Copyright (C) 2012-2021 Fazecast, Inc.
@ -26,13 +26,27 @@
#ifndef __WINDOWS_HELPER_FUNCTIONS_HEADER_H__ #ifndef __WINDOWS_HELPER_FUNCTIONS_HEADER_H__
#define __WINDOWS_HELPER_FUNCTIONS_HEADER_H__ #define __WINDOWS_HELPER_FUNCTIONS_HEADER_H__
typedef struct charTupleVector // Serial port JNI header file
{ #include "../com_fazecast_jSerialComm_SerialPort.h"
wchar_t **first, **second, **third;
size_t length;
} charTupleVector;
void pushBack(struct charTupleVector* vector, const wchar_t* key, const wchar_t* firstString, const wchar_t* secondString); // Serial port data structure
char keyExists(struct charTupleVector* vector, const wchar_t* key); typedef struct serialPort
{
void *handle;
char *readBuffer;
wchar_t *portPath, *friendlyName, *portDescription;
int errorLineNumber, errorNumber, readBufferLength;
volatile char enumerated, eventListenerRunning;
} serialPort;
// Common storage functionality
typedef struct serialPortVector
{
serialPort **ports;
int length, capacity;
} serialPortVector;
serialPort* pushBack(serialPortVector* vector, const wchar_t* key, const wchar_t* friendlyName, const wchar_t* description);
serialPort* fetchPort(serialPortVector* vector, const wchar_t* key);
void removePort(serialPortVector* vector, serialPort* port);
#endif // #ifndef __WINDOWS_HELPER_FUNCTIONS_HEADER_H__ #endif // #ifndef __WINDOWS_HELPER_FUNCTIONS_HEADER_H__