/* * @(#)Protocol.java 1.16 06/10/13 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program 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 * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.cdc.io.j2me.comm; import java.io.*; import java.util.Vector; import javax.microedition.io.*; import com.sun.cdc.io.GeneralBase; import com.sun.cdc.io.BufferedConnectionAdapter; import sun.security.action.GetPropertyAction; /** * This implements the comm port protocol. * * @version 3.1 5/11/2001 */ public class Protocol extends BufferedConnectionAdapter implements CommConnection { static File file; static FileInputStream fis; static FileOutputStream fos; /** Native handle to the serial port. */ private int handle = -1; /** Size of the read ahead buffer, default is 256. */ protected static int bufferSize = 256; /* List of successfully opened serial ports */ private static Vector openedPorts = new Vector(); /* This object's device name */ private String thisDeviceName = null; /* This object's device mode */ private int deviceMode = Connector.READ; /** * 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"); String size = (String) java.security.AccessController.doPrivileged( new GetPropertyAction("com.sun.cdc.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; /** True if the permissions have been checked. */ private boolean permissionChecked; /** Protocol to use when asking permissions. */ private static final String protocol = "comm"; /** Creates a buffered comm port connection. */ public Protocol() { // use the default buffer size super(bufferSize); } /** * Check for the required permission. * * @param token security token of the calling class or null * @param name the URL string without the protocol for prompting * * @exception SecurityException if the permission is not available * @exception InterruptedIOException if I/O associated with permissions is interrupted */ /** * 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"); } } /* * Throws SecurityException if permission check fails. * Will be overriden by MIDP version of the protocol * handler. */ protected void checkPermission(String name) { java.lang.SecurityManager sm = System.getSecurityManager(); if (sm != null) { if (deviceMode == Connector.READ) { sm.checkRead(name); } else { sm.checkWrite(name); } } return; } /* * Check permission when opening an OutputStream. MIDP * versions of the protocol handler should override this * with an empty method. Throw a SecurityException if * the connection is not allowed. Currently the comm * protocol handler does not make a permission check at * this point so this method is empty. */ protected void outputStreamPermissionCheck() { return; } /* * Check permission when opening an InputStream. MIDP * versions of the protocol handler should override this * with an empty method. A SecurityException will be * raised if the connection is not allowed. Currently the * comm protocol handler does not make a permission * check at this point so this method is empty. */ protected void inputStreamPermissionCheck() { return; } /** * 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; deviceMode = mode; 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) { checkPermission(deviceName); /* 6231661: before checking if port is already open, check if no open Streams (ensureNoStreamsOpen). This is done to throw the correct exception: IOException when open Streams exist */ ensureNoStreamsOpen(); /* 6227981: before opening, check to see if the port is already opened" */ if (openedPorts.contains(deviceName)) { throw new IOException("Connection already open"); } handle = native_openByName(deviceName, baud, bbc|stop|parity|rts|cts); } else { checkPermission("comm:" + portNumber); handle = native_openByNumber(portNumber, baud, bbc|stop|parity|rts|cts); } /* 6227970: if open fails throw an IOException */ if (handle < 0) { throw new IOException("Could not open connection"); } openedPorts.addElement(deviceName); thisDeviceName = new String(deviceName); 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); 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; } /** * Override close the GCF connection * * * @exception IOException if an I/O error occurs when closing the * connection. */ public void close() throws IOException { if ((thisDeviceName != null) && (openedPorts.contains(thisDeviceName))) { openedPorts.remove(thisDeviceName); } super.close(); } /** * Close the native serial port. * * @exception IOException if an I/O error occurs. */ protected void disconnect() throws IOException { try { ///* native_close(handle); 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 = 0; try { if (b == null) { int chunk = 256; b = new byte[chunk]; int end = off + len; int tmp = chunk; for (; off < end && tmp == chunk; off += chunk) { if (off + chunk > end) { chunk = end - off; } tmp = native_readBytes(handle, b, 0, chunk); if (tmp > 0) { bytesRead += tmp; } } if (tmp < 0) { eof = true; } } else { bytesRead = native_readBytes(handle, b, off, len); } } finally { if (iStreams == 0) { throw new InterruptedIOException("Stream closed"); } } if (bytesRead == -1) { eof = true; } /// GeneralBase.iowait(); return(bytesRead); } /** * 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); 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); 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; }