/* jSSC (Java Simple Serial Connector) - serial port communication library. * © Alexey Sokolov (scream3r), 2010-2013. * * This file is part of jSSC. * * jSSC is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * jSSC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with jSSC. If not, see . * * If you use jSSC in public project you can inform me about this by e-mail, * of course if you want it. * * e-mail: scream3r.org@gmail.com * web-site: http://scream3r.org | http://code.google.com/p/java-simple-serial-connector/ */ #include #include #include #include "../jssc_SerialNativeInterface.h" //#include /* * Port opening. * * In 2.2.0 added useTIOCEXCL (not used only for compatibility with _nix version) */ JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_openPort(JNIEnv *env, jobject object, jstring portName, jboolean useTIOCEXCL){ char prefix[] = "\\\\.\\"; const char* port = env->GetStringUTFChars(portName, JNI_FALSE); //since 2.1.0 -> string concat fix char portFullName[strlen(prefix) + strlen(port) + 1]; strcpy(portFullName, prefix); strcat(portFullName, port); //<- since 2.1.0 HANDLE hComm = CreateFile(portFullName, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0); env->ReleaseStringUTFChars(portName, port); //since 2.3.0 -> if(hComm != INVALID_HANDLE_VALUE){ DCB *dcb = new DCB(); if(!GetCommState(hComm, dcb)){ hComm = (HANDLE)jssc_SerialNativeInterface_ERR_INCORRECT_SERIAL_PORT;//(-4)Incorrect serial port } delete dcb; } else { DWORD errorValue = GetLastError(); if(errorValue == ERROR_ACCESS_DENIED){ hComm = (HANDLE)jssc_SerialNativeInterface_ERR_PORT_BUSY;//(-1)Port busy } else if(errorValue == ERROR_FILE_NOT_FOUND){ hComm = (HANDLE)jssc_SerialNativeInterface_ERR_PORT_NOT_FOUND;//(-2)Port not found } } //<- since 2.3.0 #if defined(_X86_) return (jint)hComm; #elif defined(__x86_64) return (intptr_t)hComm; #endif }; /* * Setting serial port params. */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setParams (JNIEnv *env, jobject object, jint portHandle, jint baudRate, jint byteSize, jint stopBits, jint parity, jboolean setRTS, jboolean setDTR){ HANDLE hComm = (HANDLE)portHandle; DCB *dcb = new DCB(); jboolean returnValue = JNI_FALSE; if(GetCommState(hComm, dcb)){ dcb->BaudRate = baudRate; dcb->ByteSize = byteSize; dcb->StopBits = stopBits; dcb->Parity = parity; //since 0.8 -> if(setRTS == JNI_TRUE){ dcb->fRtsControl = RTS_CONTROL_ENABLE; } else { dcb->fRtsControl = RTS_CONTROL_DISABLE; } if(setDTR == JNI_TRUE){ dcb->fDtrControl = DTR_CONTROL_ENABLE; } else { dcb->fDtrControl = DTR_CONTROL_DISABLE; } dcb->fOutxCtsFlow = FALSE; dcb->fOutxDsrFlow = FALSE; dcb->fDsrSensitivity = FALSE; dcb->fTXContinueOnXoff = TRUE; dcb->fOutX = FALSE; dcb->fInX = FALSE; dcb->fErrorChar = FALSE; dcb->fNull = FALSE; dcb->fAbortOnError = FALSE; dcb->XonLim = 2048; dcb->XoffLim = 512; dcb->XonChar = (char)17; //DC1 dcb->XoffChar = (char)19; //DC3 //<- since 0.8 if(SetCommState(hComm, dcb)){ //since 2.1.0 -> previously setted timeouts by another application should be cleared COMMTIMEOUTS *lpCommTimeouts = new COMMTIMEOUTS(); lpCommTimeouts->ReadIntervalTimeout = 0; lpCommTimeouts->ReadTotalTimeoutConstant = 0; lpCommTimeouts->ReadTotalTimeoutMultiplier = 0; lpCommTimeouts->WriteTotalTimeoutConstant = 0; lpCommTimeouts->WriteTotalTimeoutMultiplier = 0; if(SetCommTimeouts(hComm, lpCommTimeouts)){ returnValue = JNI_TRUE; } delete lpCommTimeouts; //<- since 2.1.0 } } delete dcb; return returnValue; } /* * PurgeComm */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_purgePort (JNIEnv *env, jobject object, jint portHandle, jint flags){ HANDLE hComm = (HANDLE)portHandle; DWORD dwFlags = (DWORD)flags; return (PurgeComm(hComm, dwFlags) ? JNI_TRUE : JNI_FALSE); } /* * Port closing */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_closePort (JNIEnv *env, jobject object, jint portHandle){ HANDLE hComm = (HANDLE)portHandle; return (CloseHandle(hComm) ? JNI_TRUE : JNI_FALSE); } /* * Set events mask */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setEventsMask (JNIEnv *env, jobject object, jint portHandle, jint mask){ HANDLE hComm = (HANDLE)portHandle; DWORD dwEvtMask = (DWORD)mask; return (SetCommMask(hComm, dwEvtMask) ? JNI_TRUE : JNI_FALSE); } /* * Get events mask */ JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getEventsMask (JNIEnv *env, jobject object, jint portHandle){ HANDLE hComm = (HANDLE)portHandle; DWORD lpEvtMask; if(GetCommMask(hComm, &lpEvtMask)){ return (jint)lpEvtMask; } else { return -1; } } /* * Change RTS line state (ON || OFF) */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setRTS (JNIEnv *env, jobject object, jint portHandle, jboolean enabled){ HANDLE hComm = (HANDLE)portHandle; if(enabled == JNI_TRUE){ return (EscapeCommFunction(hComm, SETRTS) ? JNI_TRUE : JNI_FALSE); } else { return (EscapeCommFunction(hComm, CLRRTS) ? JNI_TRUE : JNI_FALSE); } } /* * Change DTR line state (ON || OFF) */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setDTR (JNIEnv *env, jobject object, jint portHandle, jboolean enabled){ HANDLE hComm = (HANDLE)portHandle; if(enabled == JNI_TRUE){ return (EscapeCommFunction(hComm, SETDTR) ? JNI_TRUE : JNI_FALSE); } else { return (EscapeCommFunction(hComm, CLRDTR) ? JNI_TRUE : JNI_FALSE); } } /* * Write data to port * portHandle - port handle * buffer - byte array for sending */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_writeBytes (JNIEnv *env, jobject object, jint portHandle, jbyteArray buffer){ HANDLE hComm = (HANDLE)portHandle; DWORD lpNumberOfBytesTransferred; DWORD lpNumberOfBytesWritten; OVERLAPPED *overlapped = new OVERLAPPED(); jboolean returnValue = JNI_FALSE; jbyte* jBuffer = env->GetByteArrayElements(buffer, JNI_FALSE); overlapped->hEvent = CreateEventA(NULL, true, false, NULL); if(WriteFile(hComm, jBuffer, (DWORD)env->GetArrayLength(buffer), &lpNumberOfBytesWritten, overlapped)){ returnValue = JNI_TRUE; } else if(GetLastError() == ERROR_IO_PENDING){ if(WaitForSingleObject(overlapped->hEvent, INFINITE) == WAIT_OBJECT_0){ if(GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false)){ returnValue = JNI_TRUE; } } } env->ReleaseByteArrayElements(buffer, jBuffer, 0); CloseHandle(overlapped->hEvent); delete overlapped; return returnValue; } /* * Read data from port * portHandle - port handle * byteCount - count of bytes for reading */ JNIEXPORT jbyteArray JNICALL Java_jssc_SerialNativeInterface_readBytes (JNIEnv *env, jobject object, jint portHandle, jint byteCount){ HANDLE hComm = (HANDLE)portHandle; DWORD lpNumberOfBytesTransferred; DWORD lpNumberOfBytesRead; OVERLAPPED *overlapped = new OVERLAPPED(); jbyte lpBuffer[byteCount]; jbyteArray returnArray = env->NewByteArray(byteCount); overlapped->hEvent = CreateEventA(NULL, true, false, NULL); if(ReadFile(hComm, lpBuffer, (DWORD)byteCount, &lpNumberOfBytesRead, overlapped)){ env->SetByteArrayRegion(returnArray, 0, byteCount, lpBuffer); } else if(GetLastError() == ERROR_IO_PENDING){ if(WaitForSingleObject(overlapped->hEvent, INFINITE) == WAIT_OBJECT_0){ if(GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false)){ env->SetByteArrayRegion(returnArray, 0, byteCount, lpBuffer); } } } CloseHandle(overlapped->hEvent); delete overlapped; return returnArray; } /* * Get bytes count in serial port buffers (Input and Output) */ JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getBuffersBytesCount (JNIEnv *env, jobject object, jint portHandle){ HANDLE hComm = (HANDLE)portHandle; jint returnValues[2]; returnValues[0] = -1; returnValues[1] = -1; jintArray returnArray = env->NewIntArray(2); DWORD lpErrors; COMSTAT *comstat = new COMSTAT(); if(ClearCommError(hComm, &lpErrors, comstat)){ returnValues[0] = (jint)comstat->cbInQue; returnValues[1] = (jint)comstat->cbOutQue; } else { returnValues[0] = -1; returnValues[1] = -1; } delete comstat; env->SetIntArrayRegion(returnArray, 0, 2, returnValues); return returnArray; } //since 0.8 -> const jint FLOWCONTROL_NONE = 0; const jint FLOWCONTROL_RTSCTS_IN = 1; const jint FLOWCONTROL_RTSCTS_OUT = 2; const jint FLOWCONTROL_XONXOFF_IN = 4; const jint FLOWCONTROL_XONXOFF_OUT = 8; //<- since 0.8 /* * Setting flow control mode * * since 0.8 */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_setFlowControlMode (JNIEnv *env, jobject object, jint portHandle, jint mask){ HANDLE hComm = (HANDLE)portHandle; jboolean returnValue = JNI_FALSE; DCB *dcb = new DCB(); if(GetCommState(hComm, dcb)){ dcb->fRtsControl = RTS_CONTROL_ENABLE; dcb->fOutxCtsFlow = FALSE; dcb->fOutX = FALSE; dcb->fInX = FALSE; if(mask != FLOWCONTROL_NONE){ if((mask & FLOWCONTROL_RTSCTS_IN) == FLOWCONTROL_RTSCTS_IN){ dcb->fRtsControl = RTS_CONTROL_HANDSHAKE; } if((mask & FLOWCONTROL_RTSCTS_OUT) == FLOWCONTROL_RTSCTS_OUT){ dcb->fOutxCtsFlow = TRUE; } if((mask & FLOWCONTROL_XONXOFF_IN) == FLOWCONTROL_XONXOFF_IN){ dcb->fInX = TRUE; } if((mask & FLOWCONTROL_XONXOFF_OUT) == FLOWCONTROL_XONXOFF_OUT){ dcb->fOutX = TRUE; } } if(SetCommState(hComm, dcb)){ returnValue = JNI_TRUE; } } delete dcb; return returnValue; } /* * Getting flow control mode * * since 0.8 */ JNIEXPORT jint JNICALL Java_jssc_SerialNativeInterface_getFlowControlMode (JNIEnv *env, jobject object, jint portHandle){ HANDLE hComm = (HANDLE)portHandle; jint returnValue = 0; DCB *dcb = new DCB(); if(GetCommState(hComm, dcb)){ if(dcb->fRtsControl == RTS_CONTROL_HANDSHAKE){ returnValue |= FLOWCONTROL_RTSCTS_IN; } if(dcb->fOutxCtsFlow == TRUE){ returnValue |= FLOWCONTROL_RTSCTS_OUT; } if(dcb->fInX == TRUE){ returnValue |= FLOWCONTROL_XONXOFF_IN; } if(dcb->fOutX == TRUE){ returnValue |= FLOWCONTROL_XONXOFF_OUT; } } delete dcb; return returnValue; } /* * Send break for setted duration * * since 0.8 */ JNIEXPORT jboolean JNICALL Java_jssc_SerialNativeInterface_sendBreak (JNIEnv *env, jobject object, jint portHandle, jint duration){ HANDLE hComm = (HANDLE)portHandle; jboolean returnValue = JNI_FALSE; if(duration > 0){ if(SetCommBreak(hComm) > 0){ Sleep(duration); if(ClearCommBreak(hComm) > 0){ returnValue = JNI_TRUE; } } } return returnValue; } /* * Wait event * portHandle - port handle */ JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_waitEvents (JNIEnv *env, jobject object, jint portHandle) { HANDLE hComm = (HANDLE)portHandle; DWORD lpEvtMask = 0; DWORD lpNumberOfBytesTransferred = 0; OVERLAPPED *overlapped = new OVERLAPPED(); jclass intClass = env->FindClass("[I"); jobjectArray returnArray; boolean functionSuccessful = false; overlapped->hEvent = CreateEventA(NULL, true, false, NULL); if(WaitCommEvent(hComm, &lpEvtMask, overlapped)){ functionSuccessful = true; } else if(GetLastError() == ERROR_IO_PENDING){ if(WaitForSingleObject(overlapped->hEvent, INFINITE) == WAIT_OBJECT_0){ if(GetOverlappedResult(hComm, overlapped, &lpNumberOfBytesTransferred, false)){ functionSuccessful = true; } } } if(functionSuccessful){ boolean executeGetCommModemStatus = false; boolean executeClearCommError = false; DWORD events[9];//fixed since 0.8 (old value is 8) jint eventsCount = 0; if((EV_BREAK & lpEvtMask) == EV_BREAK){ events[eventsCount] = EV_BREAK; eventsCount++; } if((EV_CTS & lpEvtMask) == EV_CTS){ events[eventsCount] = EV_CTS; eventsCount++; executeGetCommModemStatus = true; } if((EV_DSR & lpEvtMask) == EV_DSR){ events[eventsCount] = EV_DSR; eventsCount++; executeGetCommModemStatus = true; } if((EV_ERR & lpEvtMask) == EV_ERR){ events[eventsCount] = EV_ERR; eventsCount++; executeClearCommError = true; } if((EV_RING & lpEvtMask) == EV_RING){ events[eventsCount] = EV_RING; eventsCount++; executeGetCommModemStatus = true; } if((EV_RLSD & lpEvtMask) == EV_RLSD){ events[eventsCount] = EV_RLSD; eventsCount++; executeGetCommModemStatus = true; } if((EV_RXCHAR & lpEvtMask) == EV_RXCHAR){ events[eventsCount] = EV_RXCHAR; eventsCount++; executeClearCommError = true; } if((EV_RXFLAG & lpEvtMask) == EV_RXFLAG){ events[eventsCount] = EV_RXFLAG; eventsCount++; executeClearCommError = true; } if((EV_TXEMPTY & lpEvtMask) == EV_TXEMPTY){ events[eventsCount] = EV_TXEMPTY; eventsCount++; executeClearCommError = true; } /* * Execute GetCommModemStatus function if it's needed (get lines status) */ jint statusCTS = 0; jint statusDSR = 0; jint statusRING = 0; jint statusRLSD = 0; boolean successGetCommModemStatus = false; if(executeGetCommModemStatus){ DWORD lpModemStat; if(GetCommModemStatus(hComm, &lpModemStat)){ successGetCommModemStatus = true; if((MS_CTS_ON & lpModemStat) == MS_CTS_ON){ statusCTS = 1; } if((MS_DSR_ON & lpModemStat) == MS_DSR_ON){ statusDSR = 1; } if((MS_RING_ON & lpModemStat) == MS_RING_ON){ statusRING = 1; } if((MS_RLSD_ON & lpModemStat) == MS_RLSD_ON){ statusRLSD = 1; } } else { jint lastError = (jint)GetLastError(); statusCTS = lastError; statusDSR = lastError; statusRING = lastError; statusRLSD = lastError; } } /* * Execute ClearCommError function if it's needed (get count of bytes in buffers and errors) */ jint bytesCountIn = 0; jint bytesCountOut = 0; jint communicationsErrors = 0; boolean successClearCommError = false; if(executeClearCommError){ DWORD lpErrors; COMSTAT *comstat = new COMSTAT(); if(ClearCommError(hComm, &lpErrors, comstat)){ successClearCommError = true; bytesCountIn = (jint)comstat->cbInQue; bytesCountOut = (jint)comstat->cbOutQue; communicationsErrors = (jint)lpErrors; } else { jint lastError = (jint)GetLastError(); bytesCountIn = lastError; bytesCountOut = lastError; communicationsErrors = lastError; } delete comstat; } /* * Create int[][] for events values */ returnArray = env->NewObjectArray(eventsCount, intClass, NULL); /* * Set events values */ for(jint i = 0; i < eventsCount; i++){ jint returnValues[2]; switch(events[i]){ case EV_BREAK: returnValues[0] = (jint)events[i]; returnValues[1] = 0; goto forEnd; case EV_CTS: returnValues[1] = statusCTS; goto modemStatus; case EV_DSR: returnValues[1] = statusDSR; goto modemStatus; case EV_ERR: returnValues[1] = communicationsErrors; goto bytesAndErrors; case EV_RING: returnValues[1] = statusRING; goto modemStatus; case EV_RLSD: returnValues[1] = statusRLSD; goto modemStatus; case EV_RXCHAR: returnValues[1] = bytesCountIn; goto bytesAndErrors; case EV_RXFLAG: returnValues[1] = bytesCountIn; goto bytesAndErrors; case EV_TXEMPTY: returnValues[1] = bytesCountOut; goto bytesAndErrors; default: returnValues[0] = (jint)events[i]; returnValues[1] = 0; goto forEnd; }; modemStatus: { if(successGetCommModemStatus){ returnValues[0] = (jint)events[i]; } else { returnValues[0] = -1; } goto forEnd; } bytesAndErrors: { if(successClearCommError){ returnValues[0] = (jint)events[i]; } else { returnValues[0] = -1; } goto forEnd; } forEnd: { jintArray singleResultArray = env->NewIntArray(2); env->SetIntArrayRegion(singleResultArray, 0, 2, returnValues); env->SetObjectArrayElement(returnArray, i, singleResultArray); }; } } else { returnArray = env->NewObjectArray(1, intClass, NULL); jint returnValues[2]; returnValues[0] = -1; returnValues[1] = (jint)GetLastError(); jintArray singleResultArray = env->NewIntArray(2); env->SetIntArrayRegion(singleResultArray, 0, 2, returnValues); env->SetObjectArrayElement(returnArray, 0, singleResultArray); }; CloseHandle(overlapped->hEvent); delete overlapped; return returnArray; } /* * Get serial port names */ JNIEXPORT jobjectArray JNICALL Java_jssc_SerialNativeInterface_getSerialPortNames (JNIEnv *env, jobject object){ HKEY phkResult; LPCSTR lpSubKey = "HARDWARE\\DEVICEMAP\\SERIALCOMM\\"; jobjectArray returnArray = NULL; if(RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &phkResult) == ERROR_SUCCESS){ boolean hasMoreElements = true; DWORD keysCount = 0; char valueName[256]; DWORD valueNameSize; DWORD enumResult; while(hasMoreElements){ valueNameSize = 256; enumResult = RegEnumValueA(phkResult, keysCount, valueName, &valueNameSize, NULL, NULL, NULL, NULL); if(enumResult == ERROR_SUCCESS){ keysCount++; } else if(enumResult == ERROR_NO_MORE_ITEMS){ hasMoreElements = false; } else { hasMoreElements = false; } } if(keysCount > 0){ jclass stringClass = env->FindClass("java/lang/String"); returnArray = env->NewObjectArray((jsize)keysCount, stringClass, NULL); char lpValueName[256]; DWORD lpcchValueName; byte lpData[256]; DWORD lpcbData; DWORD result; for(DWORD i = 0; i < keysCount; i++){ lpcchValueName = 256; lpcbData = 256; result = RegEnumValueA(phkResult, i, lpValueName, &lpcchValueName, NULL, NULL, lpData, &lpcbData); if(result == ERROR_SUCCESS){ env->SetObjectArrayElement(returnArray, i, env->NewStringUTF((char*)lpData)); } } } CloseHandle(phkResult); } return returnArray; } /* * Get lines status * * returnValues[0] - CTS * returnValues[1] - DSR * returnValues[2] - RING * returnValues[3] - RLSD * */ JNIEXPORT jintArray JNICALL Java_jssc_SerialNativeInterface_getLinesStatus (JNIEnv *env, jobject object, jint portHandle){ HANDLE hComm = (HANDLE)portHandle; DWORD lpModemStat; jint returnValues[4]; for(jint i = 0; i < 4; i++){ returnValues[i] = 0; } jintArray returnArray = env->NewIntArray(4); if(GetCommModemStatus(hComm, &lpModemStat)){ if((MS_CTS_ON & lpModemStat) == MS_CTS_ON){ returnValues[0] = 1; } if((MS_DSR_ON & lpModemStat) == MS_DSR_ON){ returnValues[1] = 1; } if((MS_RING_ON & lpModemStat) == MS_RING_ON){ returnValues[2] = 1; } if((MS_RLSD_ON & lpModemStat) == MS_RLSD_ON){ returnValues[3] = 1; } } env->SetIntArrayRegion(returnArray, 0, 4, returnValues); return returnArray; }