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