JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort()

in src/main/c/Posix/SerialPort_Posix.c [588:821]


JNIEXPORT jboolean JNICALL Java_com_fazecast_jSerialComm_SerialPort_configPort(JNIEnv *env, jobject obj, jlong serialPortPointer)
{
	// Retrieve port parameters from the Java class
	serialPort *port = (serialPort*)(intptr_t)serialPortPointer;
	unsigned char disableConfig = (*env)->GetBooleanField(env, obj, disableConfigField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	baud_rate baudRate = (*env)->GetIntField(env, obj, baudRateField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int byteSizeInt = (*env)->GetIntField(env, obj, dataBitsField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int stopBitsInt = (*env)->GetIntField(env, obj, stopBitsField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int parityInt = (*env)->GetIntField(env, obj, parityField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int timeoutMode = (*env)->GetIntField(env, obj, timeoutModeField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int readTimeout = (*env)->GetIntField(env, obj, readTimeoutField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int eventsToMonitor = (*env)->GetIntField(env, obj, eventFlagsField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int flowControl = (*env)->GetIntField(env, obj, flowControlField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	unsigned char rs485ModeEnabled = (*env)->GetBooleanField(env, obj, rs485ModeField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	unsigned char isDtrEnabled = (*env)->GetBooleanField(env, obj, isDtrEnabledField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	unsigned char isRtsEnabled = (*env)->GetBooleanField(env, obj, isRtsEnabledField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	char xonStartChar = (*env)->GetByteField(env, obj, xonStartCharField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	char xoffStopChar = (*env)->GetByteField(env, obj, xoffStopCharField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
#if defined(__linux__)
	unsigned char rs485ModeControlEnabled = (*env)->GetBooleanField(env, obj, rs485ModeControlEnabledField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int sendDeviceQueueSize = (*env)->GetIntField(env, obj, sendDeviceQueueSizeField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int receiveDeviceQueueSize = (*env)->GetIntField(env, obj, receiveDeviceQueueSizeField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int rs485DelayBefore = (*env)->GetIntField(env, obj, rs485DelayBeforeField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	int rs485DelayAfter = (*env)->GetIntField(env, obj, rs485DelayAfterField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	unsigned char rs485ActiveHigh = (*env)->GetBooleanField(env, obj, rs485ActiveHighField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	unsigned char rs485EnableTermination = (*env)->GetBooleanField(env, obj, rs485EnableTerminationField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
	unsigned char rs485RxDuringTx = (*env)->GetBooleanField(env, obj, rs485RxDuringTxField);
	if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
#endif

	// Configure port parameters if not explicitly disabled
	struct termios options = { 0 };
	tcgetattr(port->handle, &options);
	if (!disableConfig)
	{
		// Clear any serial port flags and set up raw non-canonical port parameters
		options.c_cc[VSTART] = (unsigned char)xonStartChar;
		options.c_cc[VSTOP] = (unsigned char)xoffStopChar;
		options.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | INPCK | IGNPAR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
		options.c_oflag &= ~OPOST;
		options.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
		options.c_cflag &= ~(CSIZE | PARENB | CMSPAR | PARODD | CSTOPB | CRTSCTS);

		// Update the user-specified port parameters
		tcflag_t byteSize = (byteSizeInt == 5) ? CS5 : (byteSizeInt == 6) ? CS6 : (byteSizeInt == 7) ? CS7 : CS8;
		tcflag_t parity = (parityInt == com_fazecast_jSerialComm_SerialPort_NO_PARITY) ? 0 : (parityInt == com_fazecast_jSerialComm_SerialPort_ODD_PARITY) ? (PARENB | PARODD) : (parityInt == com_fazecast_jSerialComm_SerialPort_EVEN_PARITY) ? PARENB : (parityInt == com_fazecast_jSerialComm_SerialPort_MARK_PARITY) ? (PARENB | CMSPAR | PARODD) : (PARENB | CMSPAR);
		options.c_cflag |= (byteSize | parity | CLOCAL | CREAD);
		if (!isDtrEnabled || !isRtsEnabled)
			options.c_cflag &= ~HUPCL;
		if (!rs485ModeEnabled)
			options.c_iflag |= BRKINT;
		if (stopBitsInt == com_fazecast_jSerialComm_SerialPort_TWO_STOP_BITS)
			options.c_cflag |= CSTOPB;
		if (byteSizeInt < 8)
			options.c_iflag |= ISTRIP;
		if (parityInt != 0)
			options.c_iflag |= (INPCK | IGNPAR);
		if (((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_CTS_ENABLED) > 0) || ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_RTS_ENABLED) > 0))
			options.c_cflag |= CRTSCTS;
		if ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_IN_ENABLED) > 0)
			options.c_iflag |= IXOFF;
		if ((flowControl & com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED) > 0)
			options.c_iflag |= IXON;

#if defined(__linux__)

		// Attempt to set the transmit buffer size, closing wait time, and latency flags
		struct serial_struct serInfo = { 0 };
		if (!ioctl(port->handle, TIOCGSERIAL, &serInfo))
		{
			serInfo.closing_wait = 250;
			serInfo.xmit_fifo_size = sendDeviceQueueSize;
			serInfo.flags |= ASYNC_LOW_LATENCY;
			ioctl(port->handle, TIOCSSERIAL, &serInfo);
		}

		// Retrieve the driver-reported transmit buffer size
		if (!ioctl(port->handle, TIOCGSERIAL, &serInfo))
			sendDeviceQueueSize = serInfo.xmit_fifo_size;
		receiveDeviceQueueSize = sendDeviceQueueSize;
		(*env)->SetIntField(env, obj, sendDeviceQueueSizeField, sendDeviceQueueSize);
		if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
		(*env)->SetIntField(env, obj, receiveDeviceQueueSizeField, receiveDeviceQueueSize);
		if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;

		// Configure RS-485 mode if requested
		if (rs485ModeControlEnabled) {
			// Attempt to set the requested RS-485 mode
			struct serial_rs485 rs485Conf = { 0 };
			if (!ioctl(port->handle, TIOCGRS485, &rs485Conf))
			{
				if (rs485ModeEnabled)
					rs485Conf.flags |= SER_RS485_ENABLED;
				else
					rs485Conf.flags &= ~SER_RS485_ENABLED;
				if (rs485ActiveHigh)
				{
					rs485Conf.flags |= SER_RS485_RTS_ON_SEND;
					rs485Conf.flags &= ~(SER_RS485_RTS_AFTER_SEND);
				}
				else
				{
					rs485Conf.flags &= ~(SER_RS485_RTS_ON_SEND);
					rs485Conf.flags |= SER_RS485_RTS_AFTER_SEND;
				}
				if (rs485RxDuringTx)
					rs485Conf.flags |= SER_RS485_RX_DURING_TX;
				else
					rs485Conf.flags &= ~(SER_RS485_RX_DURING_TX);
				if (rs485EnableTermination)
					rs485Conf.flags |= SER_RS485_TERMINATE_BUS;
				else
					rs485Conf.flags &= ~(SER_RS485_TERMINATE_BUS);
				rs485Conf.delay_rts_before_send = rs485DelayBefore / 1000;
				rs485Conf.delay_rts_after_send = rs485DelayAfter / 1000;
				ioctl(port->handle, TIOCSRS485, &rs485Conf);
			}
		}

#else

		(*env)->SetIntField(env, obj, sendDeviceQueueSizeField, sysconf(_SC_PAGESIZE));
		if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;
		(*env)->SetIntField(env, obj, receiveDeviceQueueSizeField, sysconf(_SC_PAGESIZE));
		if (checkJniError(env, __LINE__ - 1)) return JNI_FALSE;

#endif
	}
	else
	{
		// Update the Java-side port configuration values
		(*env)->SetIntField(env, obj, dataBitsField, (options.c_cflag & CS5) ? 5 : ((options.c_cflag & CS6) ? 6 : ((options.c_cflag & CS7) ? 7 : 8)));
		(*env)->SetIntField(env, obj, stopBitsField, (options.c_cflag & CSTOPB) ? com_fazecast_jSerialComm_SerialPort_TWO_STOP_BITS : com_fazecast_jSerialComm_SerialPort_ONE_STOP_BIT);
		(*env)->SetIntField(env, obj, parityField, ((options.c_cflag & (PARENB | CMSPAR | PARODD)) == (PARENB | CMSPAR | PARODD)) ? com_fazecast_jSerialComm_SerialPort_MARK_PARITY : (((options.c_cflag & (PARENB | CMSPAR)) == (PARENB | CMSPAR)) ? com_fazecast_jSerialComm_SerialPort_SPACE_PARITY : (((options.c_cflag & (PARENB | PARODD)) == (PARENB | PARODD)) ? com_fazecast_jSerialComm_SerialPort_ODD_PARITY : ((options.c_cflag & PARENB) ? com_fazecast_jSerialComm_SerialPort_EVEN_PARITY : com_fazecast_jSerialComm_SerialPort_NO_PARITY))));
		(*env)->SetByteField(env, obj, xonStartCharField, options.c_cc[VSTART]);
		(*env)->SetByteField(env, obj, xoffStopCharField, options.c_cc[VSTOP]);
		int flowControl = ((options.c_cflag & CRTSCTS) ? (com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_CTS_ENABLED | com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_RTS_ENABLED) : 0) |
				((options.c_iflag & IXOFF) ? com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_IN_ENABLED : 0) |
				((options.c_iflag & IXON) ? com_fazecast_jSerialComm_SerialPort_FLOW_CONTROL_XONXOFF_OUT_ENABLED : 0);
		(*env)->SetIntField(env, obj, flowControlField, flowControl);
		(*env)->SetBooleanField(env, obj, isDtrEnabledField, (options.c_cflag & CRTSCTS) > 0);
		(*env)->SetBooleanField(env, obj, isRtsEnabledField, (options.c_cflag & CRTSCTS) > 0);
	}

	// Configure the serial port read and write timeouts
	int flags = 0;
	port->eventsMask = eventsToMonitor;
	if ((eventsToMonitor & com_fazecast_jSerialComm_SerialPort_LISTENING_EVENT_DATA_RECEIVED) > 0)
	{
		// Force specific read timeouts if we are monitoring data received
		options.c_cc[VMIN] = 0;
		options.c_cc[VTIME] = 10;
	}
	else if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING) > 0) && (readTimeout > 0))	// Read Semi-blocking with timeout
	{
		options.c_cc[VMIN] = 0;
		options.c_cc[VTIME] = readTimeout / 100;
	}
	else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_SEMI_BLOCKING) > 0)						// Read Semi-blocking without timeout
	{
		options.c_cc[VMIN] = 1;
		options.c_cc[VTIME] = 0;
	}
	else if (((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0) && (readTimeout > 0))		// Read Blocking with timeout
	{
		options.c_cc[VMIN] = 0;
		options.c_cc[VTIME] = readTimeout / 100;
	}
	else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_READ_BLOCKING) > 0)								// Read Blocking without timeout
	{
		options.c_cc[VMIN] = 1;
		options.c_cc[VTIME] = 0;
	}
	else if ((timeoutMode & com_fazecast_jSerialComm_SerialPort_TIMEOUT_SCANNER) > 0)									// Scanner Mode
	{
		options.c_cc[VMIN] = 1;
		options.c_cc[VTIME] = 1;
	}
	else																												// Non-blocking
	{
		flags = O_NONBLOCK;
		options.c_cc[VMIN] = 0;
		options.c_cc[VTIME] = 0;
	}

	// Apply changes
	if (fcntl(port->handle, F_SETFL, flags))
	{
		port->errorLineNumber = lastErrorLineNumber = __LINE__ - 2;
		port->errorNumber = lastErrorNumber = errno;
		return JNI_FALSE;
	}
	baud_rate baudRateCode = getBaudRateCode(baudRate);
	if (baudRateCode)
	{
		cfsetispeed(&options, baudRateCode);
		cfsetospeed(&options, baudRateCode);
	}
	if (tcsetattr(port->handle, TCSANOW, &options))
	{
		port->errorLineNumber = lastErrorLineNumber = __LINE__ - 2;
		port->errorNumber = lastErrorNumber = errno;
		if (!disableConfig)
			return JNI_FALSE;
	}
	if (!baudRateCode && setCustomBaudRate(port->handle, baudRate))
	{
		port->errorLineNumber = lastErrorLineNumber = __LINE__ - 2;
		port->errorNumber = lastErrorNumber = errno;
		return disableConfig ? JNI_TRUE : JNI_FALSE;
	}
	return JNI_TRUE;
}