/*------------------------------------------------------------------------- | rxtx is a native interface to serial ports in java. | Copyright 2002 Michal Hobot MichalHobot@netscape.net | Copyright 1997-2002 by Trent Jarvi taj@www.linux.org.uk | | This library 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 2.1 of the License, or (at your option) any later version. | | This library 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. | | The following has been added to allow RXTX to be distributed with Sun | Microsystem's CommAPI library as suggested by the FSF. | | http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface | | A program that contains no derivative of any portion of RXTX, but | is designed to work with RXTX by being compiled or linked with it, | is considered a "work that uses the Library" subject to the terms and | conditions of the GNU Lesser General Public License. | | As a special exception, the copyright holders of RXTX give you | permission to link RXTX with independent modules that communicate with | RXTX solely through the Sun Microsytems CommAPI interface, regardless of | the license terms of these independent modules, and to copy and distribute | the resulting combined work under terms of your choice, provided that | every copy of the combined work is accompanied by a complete copy of | the source code of RXTX (the version of RXTX used to produce the | combined work), being distributed under the terms of the GNU Lesser General | Public License plus this exception. An independent module is a | module which is not derived from or based on RXTX. | | Note that people who make modified versions of RXTX are not obligated | to grant this special exception for their modified versions; it is | their choice whether to do so. The GNU Lesser General Public License | gives permission to release a modified version without this exception; this | exception also makes it possible to release a modified version which | carries forward this exception. | | You should have received a copy of the GNU Lesser General Public | License along with this library; if not, write to the Free | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --------------------------------------------------------------------------*/ #include "StdAfx.h" #include #include #include "rxtxHelpers.h" /*---------------------------------------------------------- get_java_int_var accept: env (keyhole to java) jobj (java RXTXPort object) return: the jint field from the java object, casted to long exceptions: none comments: ----------------------------------------------------------*/ long get_java_int_var( JNIEnv *env, jobject jobj, char *id) { long result = 0; jclass jclazz = env->GetObjectClass(jobj); jfieldID jfd = env->GetFieldID(jclazz, id, "I"); if( !jfd ) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); env->DeleteLocalRef(jclazz); return result; } result = (long)( env->GetIntField(jobj, jfd) ); env->DeleteLocalRef(jclazz); return result; } /*---------------------------------------------------------- get_java_boolean_var accept: env (keyhole to java) jobj (java RXTXPort object) return: the jboolean field from the java object, converted to bool exceptions: none comments: ----------------------------------------------------------*/ bool get_java_boolean_var( JNIEnv *env, jobject jobj, char *id) { bool result = FALSE; jclass jclazz = env->GetObjectClass(jobj); jfieldID jfd = env->GetFieldID(jclazz, id, "Z"); if( !jfd ) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); env->DeleteLocalRef(jclazz); return result; } result = (env->GetBooleanField(jobj, jfd)) != JNI_FALSE; env->DeleteLocalRef(jclazz); return result; } /*---------------------------------------------------------- get_java_boolean_var2 accept: env (keyhole to java) jobj (java RXTXPort object) jclazz (class of jobj) return: the jboolean field from the java object, converted to bool exceptions: none comments: can be faster for fetching many variables one by one ----------------------------------------------------------*/ bool get_java_boolean_var2(JNIEnv *env, jobject jobj, jclass jclazz, char *id) { bool result = FALSE; jfieldID jfd = env->GetFieldID(jclazz, id, "Z"); if( !jfd ) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); env->DeleteLocalRef(jclazz); return result; } result = (env->GetBooleanField(jobj, jfd)) != JNI_FALSE; return result; } /*---------------------------------------------------------- throw_java_exception accept: env (keyhole to java) *exc (exception class name) *foo (function name) *msg (error message) perform: Throw a new java exception return: none exceptions: haha! comments: ----------------------------------------------------------*/ void throw_java_exception(JNIEnv *env, const char *exc, const char *foo, const char *msg) { char buf[250]; jclass clazz = env->FindClass(exc); if( !clazz ) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); return; } _snprintf(buf, 60, "%s: %s in %s", exc, msg, foo); env->ThrowNew(clazz, buf); env->DeleteLocalRef(clazz); } // Unicode version: void throw_java_exceptionW(JNIEnv *env, const char *exc, const wchar_t *foo, const wchar_t *msg) { wchar_t buf[500]; char *lpcBuf; int msgLen; jclass clazz = env->FindClass(exc); if( !clazz ) { env->ExceptionDescribe(); env->ExceptionClear(); return; } msgLen = swprintf(buf, L"%S: %s in %s", exc, msg, foo); IF_DEBUG ( printj(env, L"!!!!! Throwing %s\n", buf); //MessageBox(NULL, buf, L"throw_java_exceptionW"), MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); ) lpcBuf = (char *)malloc((msgLen+1)*sizeof(wchar_t)); // Will be ok - mbs not longer than wcs lpcBuf[msgLen] = '\0'; wcstombs(lpcBuf, buf, msgLen); env->ThrowNew(clazz, lpcBuf); free(lpcBuf); env->DeleteLocalRef(clazz); } /*---------------------------------------------------------- get_fd accept: env (keyhole to java) jobj (java RXTXPort object) return: "fd" field from the java object, as HANDLE exceptions: none comments: ----------------------------------------------------------*/ HANDLE get_fd(JNIEnv *env, jobject jobj) { HANDLE fd = (HANDLE)get_java_int_var(env, jobj, "fd"); return fd!=0?fd:INVALID_HANDLE_VALUE; } /*---------------------------------------------------------- get_eis accept: env (keyhole to java) jobj (java RXTXPort object) return: "eis" field from the java object, as (EventInfoStruct *) exceptions: none comments: ----------------------------------------------------------*/ EventInfoStruct *get_eis(JNIEnv *env, jobject jobj) { return (EventInfoStruct *)get_java_int_var(env, jobj, "eis"); } /*---------------------------------------------------------- printj accept: like vwprintf() return: number of jchars written or -1 exceptions: none comments: prints data using System.out.print() ----------------------------------------------------------*/ int printj(JNIEnv *env, wchar_t *fmt, ...) { wchar_t buf[1024]; int retval; jstring jsBuf; jclass clsSystem, clsOut; jfieldID jfid; jobject objOut; jmethodID midPrint; va_list ap; va_start(ap, fmt); retval = _vsnwprintf(buf, 1024, fmt, ap); va_end(ap); buf[1023] = '\0'; if((clsSystem = env->FindClass("java/lang/System")) == NULL) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); return -1; } if((jfid = env->GetStaticFieldID(clsSystem, "out", "Ljava/io/PrintStream;")) == NULL) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); env->DeleteLocalRef(clsSystem); return -1; } objOut = env->GetStaticObjectField(clsSystem, jfid); clsOut = env->GetObjectClass(objOut); if((midPrint = env->GetMethodID(clsOut, "print", "(Ljava/lang/String;)V")) == NULL) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); env->DeleteLocalRef(clsOut); env->DeleteLocalRef(clsSystem); return -1; } jsBuf = env->NewString(buf, wcslen(buf)); env->CallVoidMethod(objOut, midPrint, jsBuf); env->DeleteLocalRef(jsBuf); env->DeleteLocalRef(clsOut); env->DeleteLocalRef(clsSystem); return retval; } /*---------------------------------------------------------- CommEventThread accept: communication structure ptr return: 0 - normal finish, other - error exceptions: none comments: runs as separate thread ----------------------------------------------------------*/ DWORD __stdcall CommEventThread(LPVOID lpEventInfo) { DWORD dwErr; EventInfoStruct *EventInfo = (EventInfoStruct *)lpEventInfo; HANDLE hPort = EventInfo->fd; // Specify a set of events to be monitored for the port. if(!SetCommMask(hPort, EventInfo->ef)) { dwErr = GetLastError(); IF_DEBUG ( LPCWSTR lpMsgBuf; CreateErrorMsg(dwErr, lpMsgBuf); MessageBoxW(NULL, lpMsgBuf, L"!!! CommEventThread - SetCommMask() error", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); ReleaseErrorMsg(lpMsgBuf); ) return dwErr; } // Thread is ready to work EventInfo->eventThreadReady = true; do { // Wait for an event to occur for the port. if(!WaitCommEvent(hPort, &(EventInfo->event), NULL)) { dwErr = GetLastError(); if(dwErr == ERROR_INVALID_PARAMETER) { // Mask is empty - let's wait for a moment and continue MessageBeep(MB_ICONQUESTION); Sleep(200); continue; } if(dwErr == ERROR_INVALID_HANDLE) { // Port was closed //MessageBox(NULL, L"--- CommEventThread - Port closed", L"ERROR_INVALID_HANDLE", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); free(EventInfo); return 0; } IF_DEBUG ( LPCWSTR lpMsgBuf; WCHAR MsgBuf2[1000]; CreateErrorMsg(dwErr, lpMsgBuf); wsprintfW(MsgBuf2, L"%ld %s, CommMask==%ld", dwErr, lpMsgBuf, EventInfo->ef); MessageBoxW(NULL, MsgBuf2, L"!!! CommEventThread - WaitCommEvent() error", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); ReleaseErrorMsg(lpMsgBuf); ) return dwErr; } IF_DEBUG ( MessageBeep(MB_ICONEXCLAMATION); //MessageBoxW(NULL, L"--- RXTXPort.CommEventThread() - CommEvent", L"CommEventThread", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); ) // Re-specify the set of events to be monitored for the port. if(!SetCommMask(hPort, EventInfo->ef)) { dwErr = GetLastError(); IF_DEBUG ( LPCWSTR lpMsgBuf; CreateErrorMsg(dwErr, lpMsgBuf); MessageBoxW(NULL, lpMsgBuf, L"!!! CommEventThread - SetCommMask() error", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); ReleaseErrorMsg(lpMsgBuf); ) return dwErr; } if(!SetEvent(EventInfo->eventHandle)) { dwErr = GetLastError(); if(dwErr == ERROR_INVALID_HANDLE) // Event was closed { //MessageBoxW(NULL, L"--- CommEventThread - Event closed", L"ERROR_INVALID_HANDLE", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); free(EventInfo); return 0; } IF_DEBUG ( LPCWSTR lpMsgBuf; CreateErrorMsg(dwErr, lpMsgBuf); MessageBoxW(NULL, lpMsgBuf, L"!!! CommEventThread - SetEvent() error", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); ReleaseErrorMsg(lpMsgBuf); ) return dwErr; } // Wait for receiving and clearing event while(EventInfo->event != 0) Sleep(0); } while(TRUE); } /*---------------------------------------------------------- setEventFlags accept: EventFlag table return: none exceptions: none comments: sets EventFlag table according to Java MonitorThread fields ----------------------------------------------------------*/ /*void setEventFlags(JNIEnv *env, jobject jobjPort, bool ef[]) { jfieldID jfid; jclass cls; jobject jobj; jclass clsPort = env->GetObjectClass(jobjPort); if((jfid = env->GetFieldID(clsPort, "monThread", "Lgnu/io/RXTXPort$MonitorThread;")) == NULL) { IF_DEBUG ( env->ExceptionDescribe(); ) env->ExceptionClear(); env->DeleteLocalRef(clsPort); return; } jobj = env->GetObjectField(jobjPort, jfid); cls = env->GetObjectClass(jobj); ef[SPE_DATA_AVAILABLE] = get_java_boolean_var2(env, jobj, cls, "Data"); ef[SPE_OUTPUT_BUFFER_EMPTY] = get_java_boolean_var2(env, jobj, cls, "Output"); ef[SPE_CTS] = get_java_boolean_var2(env, jobj, cls, "CTS"); ef[SPE_DSR] = get_java_boolean_var2(env, jobj, cls, "DSR"); ef[SPE_RI] = get_java_boolean_var2(env, jobj, cls, "RI"); ef[SPE_CD] = get_java_boolean_var2(env, jobj, cls, "CD"); ef[SPE_OE] = get_java_boolean_var2(env, jobj, cls, "OE"); ef[SPE_PE] = get_java_boolean_var2(env, jobj, cls, "PE"); ef[SPE_FE] = get_java_boolean_var2(env, jobj, cls, "FE"); ef[SPE_BI] = get_java_boolean_var2(env, jobj, cls, "BI"); env->DeleteLocalRef(clsPort); env->DeleteLocalRef(cls); env->DeleteLocalRef(jobj); } */ /*---------------------------------------------------------- InitialiseEventInfoStruct accept: Port handle return: 0 - OK, other - error number exceptions: none comments: Structure for communication with thread ----------------------------------------------------------*/ int InitialiseEventInfoStruct(HANDLE hPort, EventInfoStruct **EventInfoPtr) { DWORD dwErr; WCHAR wsEventName[MAX_PATH]; (*EventInfoPtr) = (EventInfoStruct *)malloc(sizeof(EventInfoStruct)); if(*EventInfoPtr == NULL) return ERROR_NOT_ENOUGH_MEMORY; memset(*EventInfoPtr, 0, sizeof(EventInfoStruct)); (*EventInfoPtr)->fd = hPort; wsprintfW(wsEventName, L"rxtxPort.SerEvt%lx", hPort); (*EventInfoPtr)->eventHandle = CreateEventW(NULL, FALSE, FALSE, wsEventName); dwErr = GetLastError(); if((*EventInfoPtr)->eventHandle == NULL || dwErr == ERROR_ALREADY_EXISTS) { IF_DEBUG ( LPCWSTR lpMsgBuf; CreateErrorMsg(dwErr, lpMsgBuf); MessageBoxW(NULL, lpMsgBuf, L"!!! InitialiseEventInfoStruct - CreateEvent() error", MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND); ReleaseErrorMsg(lpMsgBuf); ) free(*EventInfoPtr); return dwErr; } // Set initial event flags: It should be not empty, but unused here and relatively rare event (*EventInfoPtr)->ef = EV_POWER; //In WinNT you can use EV_EVENT2 instead return 0; } /*---------------------------------------------------------- SendEvents accept: return: exceptions: none comments: Sends events needed ----------------------------------------------------------*/ int SendEvents(JNIEnv *env, jobject jobj, DWORD dwEvent, EventInfoStruct *EventInfo, jmethodID jmSendEvent) { DWORD dwErrors; COMSTAT Stat; IF_DEBUG ( printj(env, L"--- SendEvents(): event %#lx\n", dwEvent); ) if(!ClearCommError(EventInfo->fd, &dwErrors, &Stat)) { IF_DEBUG ( LPCWSTR lpMsgBuf; CreateErrorMsg(GetLastError(), lpMsgBuf); printj(env, L"!!! SendEvents - ClearCommError() error: %s\n", lpMsgBuf); ReleaseErrorMsg(lpMsgBuf); ) return -1; } // Ignore not subscribed events dwEvent &= EventInfo->ef; if(dwEvent & EV_RXCHAR) { IF_DEBUG ( printj(env, L"--- SendEvents(): EV_RXCHAR\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_DATA_AVAILABLE, JNI_TRUE) == JNI_TRUE) return SPE_DATA_AVAILABLE; } if(dwEvent & EV_TXEMPTY) { IF_DEBUG ( printj(env, L"--- SendEvents(): EV_TXEMPTY\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_OUTPUT_BUFFER_EMPTY, JNI_TRUE) == JNI_TRUE) return SPE_OUTPUT_BUFFER_EMPTY; } if(dwEvent & EV_CTS) { IF_DEBUG ( printj(env, L"--- SendEvents(): EV_CTS\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_CTS, Stat.fCtsHold?JNI_TRUE:JNI_TRUE) == JNI_TRUE) return SPE_CTS; } if(dwEvent & EV_DSR) { IF_DEBUG ( printj(env, L"--- SendEvents(): EV_DSR\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_DSR, Stat.fDsrHold?JNI_TRUE:JNI_TRUE) == JNI_TRUE) return SPE_DSR; } if(dwEvent & EV_RING) { IF_DEBUG ( printj(env, L"--- SendEvents(): EV_RING\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_RI, JNI_TRUE) == JNI_TRUE) return SPE_RI; } if(dwEvent & EV_RLSD) { IF_DEBUG ( printj(env, L"--- SendEvents(): EV_RLSD\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_CD, Stat.fRlsdHold?JNI_TRUE:JNI_TRUE) == JNI_TRUE) return SPE_CD; } if(dwEvent & EV_ERR) { if(dwErrors & CE_OVERRUN) { IF_DEBUG ( printj(env, L"--- SendEvents(): CE_OVERRUN\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_OE, JNI_TRUE) == JNI_TRUE) return SPE_OE; } if(dwErrors & CE_RXPARITY) { IF_DEBUG ( printj(env, L"--- SendEvents(): CE_RXPARITY\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_PE, JNI_TRUE) == JNI_TRUE) return SPE_PE; } if(dwErrors & CE_FRAME) { IF_DEBUG ( printj(env, L"--- SendEvents(): CE_FRAME\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_FE, JNI_TRUE) == JNI_TRUE) return SPE_FE; } } if(dwEvent & EV_BREAK) { IF_DEBUG ( printj(env, L"--- SendEvents(): EV_BREAK\n"); ) if(env->CallBooleanMethod(jobj, jmSendEvent, SPE_BI, JNI_TRUE) == JNI_TRUE) return SPE_BI; } return 0; }