Skip to content

Commit cb59248

Browse files
committed
Allow FTDI ports to be opened for full enumeration using a flag
1 parent de84376 commit cb59248

File tree

3 files changed

+142
-20
lines changed

3 files changed

+142
-20
lines changed

src/main/c/Windows/SerialPort_Windows.c

Lines changed: 124 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SerialPort_Windows.c
33
*
44
* Created on: Feb 25, 2012
5-
* Last Updated on: Apr 10, 2024
5+
* Last Updated on: Apr 11, 2024
66
* Author: Will Hedgecock
77
*
88
* Copyright (C) 2012-2024 Fazecast, Inc.
@@ -55,6 +55,7 @@ jfieldID serialNumberField;
5555
jfieldID manufacturerField;
5656
jfieldID eventListenerRunningField;
5757
jfieldID disableConfigField;
58+
jfieldID allowOpenForEnumerationField;
5859
jfieldID isDtrEnabledField;
5960
jfieldID isRtsEnabledField;
6061
jfieldID autoFlushIOBuffersField;
@@ -78,6 +79,8 @@ jfieldID writeTimeoutField;
7879
jfieldID eventFlagsField;
7980

8081
// Runtime-loadable DLL functions
82+
typedef int (__stdcall *FT_CloseFunction)(FT_HANDLE);
83+
typedef int (__stdcall *FT_OpenFunction)(int, FT_HANDLE*);
8184
typedef int (__stdcall *FT_CreateDeviceInfoListFunction)(LPDWORD);
8285
typedef int (__stdcall *FT_GetDeviceInfoListFunction)(FT_DEVICE_LIST_INFO_NODE*, LPDWORD);
8386
typedef int (__stdcall *FT_EEPROM_ReadFunction)(FT_HANDLE, void*, DWORD, char*, char*, char*, char*);
@@ -111,7 +114,7 @@ static inline jboolean checkJniError(JNIEnv *env, int lineNumber)
111114
}
112115

113116
// Generalized port enumeration function
114-
static void enumeratePorts(void)
117+
static void enumeratePorts(JNIEnv *env, jclass serialComm)
115118
{
116119
// Reset the enumerated flag on all non-open serial ports
117120
for (int i = 0; i < serialPorts.length; ++i)
@@ -398,9 +401,12 @@ static void enumeratePorts(void)
398401
HINSTANCE ftdiLibInstance = LoadLibrary(TEXT("ftd2xx.dll"));
399402
if (ftdiLibInstance != NULL)
400403
{
404+
FT_OpenFunction FT_Open = (FT_OpenFunction)GetProcAddress(ftdiLibInstance, "FT_Open");
405+
FT_CloseFunction FT_Close = (FT_CloseFunction)GetProcAddress(ftdiLibInstance, "FT_Close");
401406
FT_CreateDeviceInfoListFunction FT_CreateDeviceInfoList = (FT_CreateDeviceInfoListFunction)GetProcAddress(ftdiLibInstance, "FT_CreateDeviceInfoList");
402407
FT_GetDeviceInfoListFunction FT_GetDeviceInfoList = (FT_GetDeviceInfoListFunction)GetProcAddress(ftdiLibInstance, "FT_GetDeviceInfoList");
403408
FT_EEPROM_ReadFunction FT_EEPROM_Read = (FT_EEPROM_ReadFunction)GetProcAddress(ftdiLibInstance, "FT_EEPROM_Read");
409+
unsigned char allowOpenForEnumeration = (*env)->GetStaticBooleanField(env, serialComm, allowOpenForEnumerationField);
404410
if (FT_CreateDeviceInfoList && FT_GetDeviceInfoList && FT_EEPROM_Read)
405411
{
406412
DWORD numDevs;
@@ -433,25 +439,120 @@ static void enumeratePorts(void)
433439
for (int j = 0; j < serialPorts.length; ++j)
434440
if ((wcscmp(serialPorts.ports[j]->portPath + 4, comPort) == 0) && strlen(devInfo[i].Description))
435441
{
436-
// Update the port description
437-
serialPorts.ports[j]->enumerated = 1;
438-
size_t descLength = 8 + strlen(devInfo[i].Description);
439-
wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[j]->portDescription, descLength*sizeof(wchar_t));
440-
if (newMemory)
442+
// Check whether we are allowed to open the port to complete enumeration
443+
unsigned char successfullyEnumerated = 0;
444+
if (allowOpenForEnumeration)
441445
{
442-
serialPorts.ports[j]->portDescription = newMemory;
443-
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].Description, -1, serialPorts.ports[j]->portDescription, descLength);
446+
// Open the port and read its configuration from EEPROM
447+
FT_HANDLE ftHandle;
448+
FT_EEPROM_HEADER ftEepromHeader = { .deviceType = devInfo[i].Type };
449+
char manufacturer[64], manufacturerId[64], description[64], serialNumber[64];
450+
if (FT_Open(0, &ftHandle) == FT_OK)
451+
{
452+
switch (devInfo[i].Type)
453+
{
454+
case FT_DEVICE_2232C:
455+
{
456+
FT_EEPROM_2232 ftDeviceHeader = { .common = ftEepromHeader };
457+
successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
458+
break;
459+
}
460+
case FT_DEVICE_232R:
461+
{
462+
FT_EEPROM_232R ftDeviceHeader = { .common = ftEepromHeader };
463+
successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
464+
break;
465+
}
466+
case FT_DEVICE_2232H:
467+
{
468+
FT_EEPROM_2232H ftDeviceHeader = { .common = ftEepromHeader };
469+
successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
470+
break;
471+
}
472+
case FT_DEVICE_4232H:
473+
{
474+
FT_EEPROM_4232H ftDeviceHeader = { .common = ftEepromHeader };
475+
successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
476+
break;
477+
}
478+
case FT_DEVICE_232H:
479+
{
480+
FT_EEPROM_232H ftDeviceHeader = { .common = ftEepromHeader };
481+
successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
482+
break;
483+
}
484+
case FT_DEVICE_X_SERIES:
485+
{
486+
FT_EEPROM_X_SERIES ftDeviceHeader = { .common = ftEepromHeader };
487+
successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
488+
break;
489+
}
490+
default:
491+
{
492+
FT_EEPROM_232B ftDeviceHeader = { .common = ftEepromHeader };
493+
successfullyEnumerated = (FT_EEPROM_Read(ftHandle, &ftDeviceHeader, sizeof(ftDeviceHeader), manufacturer, manufacturerId, description, serialNumber) == FT_OK);
494+
break;
495+
}
496+
}
497+
FT_Close(ftHandle);
498+
}
499+
500+
// Update port details if enumeration was successful
501+
if (successfullyEnumerated)
502+
{
503+
// Update the port description
504+
serialPorts.ports[j]->enumerated = 1;
505+
size_t descLength = 8 + strlen(description);
506+
wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[j]->portDescription, descLength*sizeof(wchar_t));
507+
if (newMemory)
508+
{
509+
serialPorts.ports[j]->portDescription = newMemory;
510+
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, description, -1, serialPorts.ports[j]->portDescription, descLength);
511+
}
512+
513+
// Update the port serial number
514+
size_t serialNumLength = 1 + strlen(serialNumber);
515+
newMemory = (wchar_t*)realloc(serialPorts.ports[j]->serialNumber, serialNumLength*sizeof(wchar_t));
516+
if (newMemory)
517+
{
518+
serialPorts.ports[j]->serialNumber = newMemory;
519+
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, serialNumber, -1, serialPorts.ports[j]->serialNumber, serialNumLength);
520+
}
521+
522+
// Update the port manufacturer
523+
size_t manufacturerLength = 1 + strlen(manufacturer);
524+
newMemory = (wchar_t*)realloc(serialPorts.ports[j]->manufacturer, manufacturerLength*sizeof(wchar_t));
525+
if (newMemory)
526+
{
527+
serialPorts.ports[j]->manufacturer = newMemory;
528+
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, manufacturer, -1, serialPorts.ports[j]->manufacturer, manufacturerLength);
529+
}
530+
}
444531
}
445532

446-
// Update the port serial number
447-
size_t serialNumLength = 1 + strlen(devInfo[i].SerialNumber);
448-
newMemory = (wchar_t*)realloc(serialPorts.ports[j]->serialNumber, serialNumLength*sizeof(wchar_t));
449-
if (newMemory)
533+
// Take what we can get if unable to enumerate by opening the port
534+
if (!successfullyEnumerated)
450535
{
451-
serialPorts.ports[j]->serialNumber = newMemory;
452-
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].SerialNumber, -1, serialPorts.ports[j]->serialNumber, serialNumLength);
536+
// Update the port description
537+
serialPorts.ports[j]->enumerated = 1;
538+
size_t descLength = 8 + strlen(devInfo[i].Description);
539+
wchar_t *newMemory = (wchar_t*)realloc(serialPorts.ports[j]->portDescription, descLength*sizeof(wchar_t));
540+
if (newMemory)
541+
{
542+
serialPorts.ports[j]->portDescription = newMemory;
543+
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].Description, -1, serialPorts.ports[j]->portDescription, descLength);
544+
}
545+
546+
// Update the port serial number
547+
size_t serialNumLength = 1 + strlen(devInfo[i].SerialNumber);
548+
newMemory = (wchar_t*)realloc(serialPorts.ports[j]->serialNumber, serialNumLength*sizeof(wchar_t));
549+
if (newMemory)
550+
{
551+
serialPorts.ports[j]->serialNumber = newMemory;
552+
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, devInfo[i].SerialNumber, -1, serialPorts.ports[j]->serialNumber, serialNumLength);
553+
}
554+
memcpy(serialPorts.ports[j]->ftdiSerialNumber, devInfo[i].SerialNumber, sizeof(serialPorts.ports[j]->ftdiSerialNumber));
453555
}
454-
memcpy(serialPorts.ports[j]->ftdiSerialNumber, devInfo[i].SerialNumber, sizeof(serialPorts.ports[j]->ftdiSerialNumber));
455556
}
456557
}
457558
if (comPort)
@@ -557,6 +658,8 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
557658
if (checkJniError(env, __LINE__ - 1)) return JNI_ERR;
558659
disableConfigField = (*env)->GetFieldID(env, serialCommClass, "disableConfig", "Z");
559660
if (checkJniError(env, __LINE__ - 1)) return JNI_ERR;
661+
allowOpenForEnumerationField = (*env)->GetStaticFieldID(env, serialCommClass, "allowOpenForEnumeration", "Z");
662+
if (checkJniError(env, __LINE__ - 1)) return JNI_ERR;
560663
isDtrEnabledField = (*env)->GetFieldID(env, serialCommClass, "isDtrEnabled", "Z");
561664
if (checkJniError(env, __LINE__ - 1)) return JNI_ERR;
562665
isRtsEnabledField = (*env)->GetFieldID(env, serialCommClass, "isRtsEnabled", "Z");
@@ -651,7 +754,7 @@ JNIEXPORT jobjectArray JNICALL Java_com_fazecast_jSerialComm_SerialPort_getCommP
651754
EnterCriticalSection(&criticalSection);
652755

653756
// Enumerate all ports on the current system
654-
enumeratePorts();
757+
enumeratePorts(env, serialComm);
655758

656759
// Get relevant SerialComm methods and fill in com port array
657760
jobjectArray arrayObject = (*env)->NewObjectArray(env, serialPorts.length, serialComm, 0);
@@ -698,7 +801,10 @@ JNIEXPORT void JNICALL Java_com_fazecast_jSerialComm_SerialPort_retrievePortDeta
698801
char continueRetrieval = 1;
699802
EnterCriticalSection(&criticalSection);
700803
if (!portsEnumerated)
701-
enumeratePorts();
804+
{
805+
jclass serialComm = (*env)->GetObjectClass(env, obj);
806+
enumeratePorts(env, serialComm);
807+
}
702808
serialPort *port = fetchPort(&serialPorts, portName);
703809
if (!port)
704810
continueRetrieval = 0;

src/main/java/com/fazecast/jSerialComm/SerialPort.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* SerialPort.java
33
*
44
* Created on: Feb 25, 2012
5-
* Last Updated on: Apr 10, 2024
5+
* Last Updated on: Apr 11, 2024
66
* Author: Will Hedgecock
77
*
88
* Copyright (C) 2012-2024 Fazecast, Inc.
@@ -103,7 +103,8 @@ public class SerialPort
103103
static private final String versionString = "2.10.5";
104104
static private final String tmpdirAppIdProperty = "fazecast.jSerialComm.appid";
105105
static private final List<Thread> shutdownHooks = new ArrayList<Thread>();
106-
static private boolean isWindows = false, isAndroid = false, cleanUpOnShutdown = false, isAndroidDelete = false;
106+
static private boolean cleanUpOnShutdown = false, allowOpenForEnumeration = false, isAndroidDelete = false;
107+
static private boolean isWindows = false, isAndroid = false;
107108
static private volatile boolean isShuttingDown = false;
108109
static
109110
{
@@ -448,6 +449,20 @@ static public void autoCleanupAtShutdown()
448449
cleanUpOnShutdown = true;
449450
}
450451

452+
/**
453+
* Allows the library to open a port during enumeration to retrieve additional details about its
454+
* serial number, manufacturer, and description. Currently, this only affects enumeration of
455+
* FTDI-specific devices on Windows.
456+
* <p>
457+
* It is not recommended to use this function, as its use will increase the overhead of enumerating
458+
* devices on Windows; however, if you have a specific need to ensure that FTDI-specific device info
459+
* is returned by the library, then this functionality might make sense.
460+
*/
461+
static public void allowPortOpenForEnumeration()
462+
{
463+
allowOpenForEnumeration = true;
464+
}
465+
451466
/**
452467
* Returns a list of all available serial ports on this machine.
453468
* <p>

src/test/java/com/fazecast/jSerialComm/SerialPortTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public void serialEvent(SerialPortEvent event)
8282
static public void main(String[] args)
8383
{
8484
System.out.println("\nUsing Library Version v" + SerialPort.getVersion());
85+
SerialPort.allowPortOpenForEnumeration();
8586
SerialPort.autoCleanupAtShutdown();
8687
SerialPort.addShutdownHook(new Thread() { public void run() { System.out.println("\nRunning shutdown hook"); } });
8788
SerialPort[] ports = SerialPort.getCommPorts();

0 commit comments

Comments
 (0)