/*
* @(#)Protocol.java 1.34 02/10/04 @(#)
*
* Copyright (c) 2000-2002 Sun Microsystems, Inc. All rights reserved.
* PROPRIETARY/CONFIDENTIAL
* Use is subject to license terms.
*/
package com.sun.midp.io.j2me.comm;
import java.io.*;
import javax.microedition.io.*;
import com.sun.cldc.io.GeneralBase;
import com.sun.midp.main.Configuration;
import com.sun.midp.io.*;
import com.sun.midp.security.*;
/**
* This implements the comm port protocol.
*
* @author Nik Shaylor
* @author Stephen Flores
* @author Efren Serra
* @version 3.1 5/11/2001
*/
public class Protocol extends BufferedConnectionAdapter
implements CommConnection {
/** Native handle to the serial port. */
private int handle = -1;
/** Size of the read ahead buffer, default is 256. */
protected static int bufferSize = 256;
/**
* Class initializer
*/
static {
/* See if a read ahead / write behind buffer size has been specified */
String size = Configuration.getProperty(
"com.sun.midp.io.j2me.comm.buffersize");
if (size != null) {
try {
bufferSize = Integer.parseInt(size);
} catch (NumberFormatException ex) {}
}
}
// From SerialMgr.h of the Palm api
/** Bit flag: 1 stop bits. */
private final static int serSettingsFlagStopBits1 = 0x00000000;
/** Bit flag: 2 stop bits. */
private final static int serSettingsFlagStopBits2 = 0x00000001;
/** Bit flag: parity on. */
private final static int serSettingsFlagParityOddM = 0x00000002;
/** Bit flag: parity even. */
private final static int serSettingsFlagParityEvenM = 0x00000004;
/** Bit flag: RTS rcv flow control. */
private final static int serSettingsFlagRTSAutoM = 0x00000010;
/** Bit flag: CTS xmit flow control. */
private final static int serSettingsFlagCTSAutoM = 0x00000020;
/** Bit flag: 7 bits/char. */
private final static int serSettingsFlagBitsPerChar7 = 0x00000080;
/** Bit flag: 8 bits/char. */
private final static int serSettingsFlagBitsPerChar8 = 0x000000C0;
/** Bit per char. */
private int bbc = serSettingsFlagBitsPerChar8;
/** Stop bits. */
private int stop = serSettingsFlagStopBits1;
/** Parity. */
private int parity = 0;
/** RTS. */
private int rts = serSettingsFlagRTSAutoM;
/** CTS. */
private int cts = serSettingsFlagCTSAutoM;
/** Baud rate. */
private int baud = 19200;
/** Blocking. */
private boolean blocking = true;
/** Creates a buffered comm port connection. */
public Protocol() {
// use the default buffer size
super(bufferSize);
// Protocol to use when asking permissions.
protocol = "comm";
requiredPermission = Permissions.COMM;
}
/**
* Parse the next parameter out of a string.
*
* @param parm a string containing one or more parameters
* @param start where in the string to start parsing
* @param end where in the string to stop parsing
*
* @exception IllegalArgumentException if the next parameter is wrong
*/
private void parseParameter(String parm, int start, int end) {
parm = parm.substring(start, end);
if (parm.equals("baudrate=110")) {
baud = 110;
} else if (parm.equals("baudrate=300")) {
baud = 300;
} else if (parm.equals("baudrate=600")) {
baud = 600;
} else if (parm.equals("baudrate=1200")) {
baud = 1200;
} else if (parm.equals("baudrate=2400")) {
baud = 2400;
} else if (parm.equals("baudrate=4800")) {
baud = 4800;
} else if (parm.equals("baudrate=9600")) {
baud = 9600;
} else if (parm.equals("baudrate=14400")) {
baud = 14400;
} else if (parm.equals("baudrate=19200")) {
baud = 19200;
} else if (parm.equals("baudrate=38400")) {
baud = 38400;
} else if (parm.equals("baudrate=56000")) {
baud = 56000;
} else if (parm.equals("baudrate=57600")) {
baud = 57600;
} else if (parm.equals("baudrate=115200")) {
baud = 115200;
} else if (parm.equals("baudrate=128000")) {
baud = 128000;
} else if (parm.equals("baudrate=256000")) {
baud = 256000;
} else if (parm.equals("bitsperchar=7")) {
bbc = serSettingsFlagBitsPerChar7;
} else if (parm.equals("bitsperchar=8")) {
bbc = serSettingsFlagBitsPerChar8;
} else if (parm.equals("stopbits=1")) {
stop = serSettingsFlagStopBits1;
} else if (parm.equals("stopbits=2")) {
stop = serSettingsFlagStopBits2;
} else if (parm.equals("parity=none")) {
parity = 0;
} else if (parm.equals("parity=odd")) {
parity = serSettingsFlagParityOddM;
} else if (parm.equals("parity=even")) {
parity = serSettingsFlagParityEvenM;
} else if (parm.equals("autorts=off")) {
rts = 0;
} else if (parm.equals("autorts=on")) {
rts = serSettingsFlagRTSAutoM;
} else if (parm.equals("autocts=off")) {
cts = 0;
} else if (parm.equals("autocts=on")) {
cts = serSettingsFlagCTSAutoM;
} else if (parm.equals("blocking=off")) {
blocking = false;
} else if (parm.equals("blocking=on")) {
blocking = true;
} else {
throw new IllegalArgumentException("Bad parameter");
}
}
/**
* Open a serial port connection.
*
* Note: DTR line is always on. <br>
* Hint: On Solaris opening by port number or /dev/term/* will block
* until the Data Set Ready line is On. To work around this open
* by device name using /dev/cua/* as root.
*
* @param name A URI with the type and parameters for the connection.
* <pre>
* The scheme must be: comm
*
* The first parameter must be a port ID: A device name or
* a logical port number from 0 to 9.
*
* Any additional parameters must be separated by a ";" and
* spaces are not allowed.
*
* The optional parameters are:
*
* baudrate: The speed of the port, defaults to 19200.
* bitsperchar: The number bits that character is. 7 or 8.
* Defaults to 8.
* stopbits: The number of stop bits per char. 1 or 2.
* Defaults to 1.
* parity: The parity can be "odd", "even", or "none".
* Defaults to "none".
* blocking: If "on" wait for a full buffer when reading.
* Defaults to "on".
* autocts: If "on", wait for the CTS line to be on
* before writing. Defaults to "on".
* autorts: If "on", turn on the RTS line when the
* input buffer is not full. If "off",
* the RTS line is always on.
* Defaults to "on".
* </pre>
* @param mode A flag that is <code>true</code> if the caller expects
* to write to the connection. This is ignored
* in all connections that are read-write.
* @param timeouts A flag to indicate that the called wants
* timeout exceptions. This is ignored.
*
* @exception IOException if an I/O error occurs, or
* IllegalArgumentException
* if the name string is has an error.
*/
public void connect(String name, int mode, boolean timeouts)
throws IOException {
int portNumber = 0;
String deviceName = null;
int start = 0;
int pos = 0;
verifyPermissionCheck();
if (name.length() == 0) {
throw new IllegalArgumentException("Missing port ID");
}
if (Character.isDigit(name.charAt(0))) {
portNumber = Integer.parseInt(name.substring(0, 1));
pos++;
} else {
pos = name.indexOf(";");
if (pos < 0) {
deviceName = name;
pos = name.length();
} else {
deviceName = name.substring(0, pos);
}
}
while (name.length() > pos) {
if (name.charAt(pos) != ';') {
throw new IllegalArgumentException(
"missing parameter delimiter");
}
pos++;
start = pos;
while (true) {
if (pos == name.length()) {
parseParameter(name, start, pos);
break;
}
if (name.charAt(pos) == ';') {
parseParameter(name, start, pos);
break;
}
pos++;
}
}
// blocking is handled at the Java layer so other Java threads can run
if (deviceName != null) {
handle = native_openByName(deviceName, baud,
bbc|stop|parity|rts|cts);
} else {
handle = native_openByNumber(portNumber, baud,
bbc|stop|parity|rts|cts);
}
registerCleanup();
}
/**
* Gets the baudrate for the serial port connection.
* @return the baudrate of the connection
* @see #setBaudRate
*/
public int getBaudRate() {
return baud;
}
/**
* Sets the baudrate for the serial port connection.
* If the requested <code>baudrate</code> is not supported
* on the platform, then the system MAY use an alternate valid setting.
* The alternate value can be accessed using the
* <code>getBaudRate</code> method.
* @param baudrate the baudrate for the connection
* @return the previous baudrate of the connection
* @see #getBaudRate
*/
public int setBaudRate(int baudrate) {
int temp = baud;
/*
* If the baudrate is not supported, select one
* that is allowed.
*/
if (baudrate < 299) {
baudrate = 110;
} else if (baudrate < 599) {
baudrate = 300;
} else if (baudrate < 1199) {
baudrate = 600;
} else if (baudrate < 2399) {
baudrate = 1200;
} else if (baudrate < 4799) {
baudrate = 2400;
} else if (baudrate < 9599) {
baudrate = 4800;
} else if (baudrate < 14399) {
baudrate = 9600;
} else if (baudrate < 19199) {
baudrate = 14400;
} else if (baudrate < 38399) {
baudrate = 19200;
} else if (baudrate < 55999) {
baudrate = 38400;
} else if (baudrate < 57599) {
baudrate = 56000;
} else if (baudrate < 115199) {
baudrate = 57600;
} else if (baudrate < 127999) {
baudrate = 115200;
} else if (baudrate < 255999) {
baudrate = 128000;
} else {
baudrate = 256000;
}
try {
/* Set the new baudrate. */
native_configurePort(handle, baudrate,
bbc|stop|parity|rts|cts);
/* If successful, update the local baud variable. */
baud = baudrate;
} catch (IOException ioe) {
// NYI - could not set baudrate as requested.
}
return temp;
}
/**
* Close the native serial port.
*
* @exception IOException if an I/O error occurs.
*/
protected void disconnect() throws IOException {
try {
native_close(handle);
} finally {
/* Reset handle to prevent resgistered cleanup close. */
handle = -1;
}
}
/**
* Reads up to <code>len</code> bytes of data from the input stream into
* an array of bytes, blocks until at least one byte is available,
* if blocking is turned on.
* Sets the <code>eof</code> field of the connection when the native read
* returns -1.
* <p>
* Polling the native code is done here to avoid the need for
* asynchronous native methods to be written. Not all implementations
* work this way (they block in the native code) but the same
* Java code works for both.
*
* @param b the buffer into which the data is read
* @param off the start offset in array <code>b</code>
* at which the data is written
* @param len the maximum number of bytes to read
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached
* @exception IOException if an I/O error occurs
*/
protected int nonBufferedRead(byte b[], int off, int len)
throws IOException {
int bytesRead;
for (;;) {
try {
bytesRead = native_readBytes(handle, b, off, len);
} finally {
if (iStreams == 0) {
throw new InterruptedIOException("Stream closed");
}
}
if (bytesRead == -1) {
eof = true;
return -1;
}
if (bytesRead != 0 || !blocking) {
return bytesRead;
}
/* Wait a while for I/O to become ready */
GeneralBase.iowait();
}
}
/**
* Reads up to <code>len</code> bytes of data from the input stream into
* an array of bytes, but does not block if no bytes available.
* Sets the <code>eof</code> flag if the end of stream is reached.
* <p>
* This is implemented so the <code>available</code> method of
* <code>BufferedConnectionBaseAdapter</code> will return more than
* zero if the buffer is empty.
*
* @param b the buffer into which the data is read
* @param off the start offset in array <code>b</code>
* at which the data is written
* @param len the maximum number of bytes to read
* @return the total number of bytes read into the buffer, or
* <code>-1</code> if there is no more data because the end of
* the stream has been reached
* @exception IOException if an I/O error occurs
*/
protected int readBytesNonBlocking(byte b[], int off, int len)
throws IOException {
int bytesRead;
try {
// the native read does not block
bytesRead = native_readBytes(handle, b, off, len);
} finally {
if (iStreams == 0) {
throw new InterruptedIOException("Stream closed");
}
}
if (bytesRead == -1) {
eof = true;
}
return bytesRead;
}
/**
* Writes <code>len</code> bytes from the specified byte array
* starting at offset <code>off</code> to this output stream.
* <p>
* Polling the native code is in the stream object handed out by
* our parent helper class. This done to avoid the need for
* asynchronous native methods to be written. Not all implementations
* work this way (they block in the native code) but the same
* Java code works for both.
*
* @param b the data
* @param off the start offset in the data
* @param len the number of bytes to write
*
* @return number of bytes written
* @exception IOException if an I/O error occurs. In particular,
* an <code>IOException</code> is thrown if the output
* stream is closed.
*/
public int writeBytes(byte b[], int off, int len) throws IOException {
return native_writeBytes(handle, b, off, len);
}
/*
* Real primitive methods
*/
/**
* Open a serial port by logical number.
*
* @param port logical number of the port 0 being the first
* @param baud baud rate to set the port at
* @param flags options for the serial port
*
* @return handle to a native serial port
*
* @exception IOException if an I/O error occurs.
*/
private static native int native_openByNumber(int port, int baud,
int flags) throws IOException;
/**
* Open a serial port by system dependent device name.
*
* @param name device name of the port
* @param baud baud rate to set the port at
* @param flags options for the serial port
*
* @return handle to a native serial port
*
* @exception IOException if an I/O error occurs.
*/
private static native int native_openByName(String name, int baud,
int flags) throws IOException;
/**
* Configure a serial port optional parameters.
*
* @param port device port returned from open
* @param baud baud rate to set the port at
* @param flags options for the serial port
*
* @exception IOException if an I/O error occurs
*/
private static native void native_configurePort(int port, int baud,
int flags) throws IOException;
/**
* Close a serial port.
*
* @param hPort handle to a native serial port
*
* @exception IOException if an I/O error occurs
*/
private static native void native_close(int hPort) throws IOException;
/** Register this object's native cleanup function. */
private native void registerCleanup();
/**
* Read from a serial port without blocking.
*
* @param hPort handle to a native serial port
* @param b I/O buffer
* @param off starting offset for data
* @param len length of data
*
* @return number of bytes read
*
* @exception IOException if an I/O error occurs
*/
private static native int native_readBytes(int hPort, byte b[], int off,
int len) throws IOException;
/**
* Write to a serial port without blocking.
*
* @param hPort handle to a native serial port
* @param b I/O buffer
* @param off starting offset for data
* @param len length of data
*
* @return number of bytes that were written
*
* @exception IOException if an I/O error occurs.
*/
private static native int native_writeBytes(int hPort, byte b[], int off,
int len) throws IOException;
/**
* Native finalizer.
*/
private native void finalize();
}
|