/**************************************************************************
* Copyright (c) 2001 by Punch Telematix. All rights reserved. *
* *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* 1. Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* 2. Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* 3. Neither the name of Punch Telematix nor the names of *
* other contributors may be used to endorse or promote products *
* derived from this software without specific prior written permission.*
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. *
* IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE *
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, *
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE *
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN *
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
**************************************************************************/
/*
** $Id: UARTInputStream.java,v 1.3 2006/10/04 14:24:21 cvsroot Exp $
*/
package com.acunia.device.uart;
import wonka.vm.Etc;
import java.io.*;
/**
* UARTInputStream implements InputStream for a UART of the SM platform.
* It supports the setting of thresholds and timeouts for reading data.
*/
public class UARTInputStream extends InputStream implements Runnable {
/**
** The currently applicable Threshold and Timeout values, as supplied
** by the user of the InputStream. Note that an rxThreshold of zero
** really means one (read returns as soon as *some* data is available),
** and an rxTimeout of zero really means infinity. We call the effective
** Threshold (the minimum number of bytes which must be available before
** read will return), "desired".
*/
private int rxThreshold = 0;
private int rxTimeout = 0;
private int desired;
/**
** micronap is the pause built into the spinlock when we
** are waiting for some data to become available. meganap
** is used in the monitor thread where theoretically we
** should execute an open-ended wait(), but we haven't the
** balls.
*/
private static final long micronap = 100; // milliseconds
private static final long meganap = 1000; // milliseconds
/**
** microbuffer is a one-byte buffer used to implement read()
** as a special case of read(array,offset,length).
*/
private byte [] microbuffer = new byte[1];
/**
** uartMonitor is the thread which monitors the number of input
** bytes available and wakes up the read method as required.
** It is started when a non-zero timeout or threashold is specified,
** and terminates when the InputStream is closed ("open" becomes false).
*/
private Thread uartMonitor = null;
/**
** "open" is set true when the InputStream is created, and false when
** close() is called.
*/
private boolean open;
/**
** The name of the underlying stream.
*/
private String name;
/**
** createFromString initialises this InputStream object using a
** lookup based on "portname".
*/
private native void createFromString(String portname)
throws SecurityException;
/**
** Constructor UARTInputStream(String) calls createFromString
** and starts up the monitor thread.
*/
public UARTInputStream(String portname) {
// TODO: security check?
createFromString(portname);
name = portname;
open = true;
}
/**
** Method readIntoBuffer performs the "guts" of a read,
** shifting bytes from the i/o driver into the buffer.
*/
private native int readIntoBuffer(byte[] b, int off, int len)
throws IOException;
/** Method read() (single-character read) is implemented as a
** special case of read(array,offset,length). This could be
** done more efficiently: feel free if you have the time.
*/
public synchronized int read()
throws IOException
{
int readlen = read(microbuffer,0,1);
if (readlen>0) {
return microbuffer[0] & 0xff;
}
else return readlen;
}
/**
** Meethod read(array) is a connvenience method, a shorthand
** for read(array, 0, b.length).
*/
public synchronized int read(byte[] b)
throws IOException, NullPointerException
{
return read(b, 0, b.length);
}
/**
** OK, now for the real business.
** Method read(byte[] b, int off, int len) first (after sanity checks)
** determines the minimum number of characters to be read ("desired")
** and then wakes up the monitor thread. If a Timeout was specified
** we then wait to be notified by the monitor thread, until at least
** "desired" characters are available. If no Timeout was specified
** then we just spinlock (using micronap) until at least "desired"
** characters are available. In both cases the wait loop is aborted
** if an interrupt is received.
** Finally we either copy the lesser of (available(),length) bytes
** into the buffer and return the number of bytes copied, or (if no
** data is available, i.e. we timed out or were interrupted) return -1.
*/
public synchronized int read(byte[] b, int off, int len)
throws IOException, NullPointerException, ArrayIndexOutOfBoundsException
{
long deadline; // absolute time when timeout will expire
long remaining; // elapsed time from now until deadline
if(b==null) throw new NullPointerException();
if(off<0 || len<0 || off > b.length - len) {
throw new ArrayIndexOutOfBoundsException();
}
if (rxThreshold>0) {
desired = rxThreshold;
} else desired = 1;
if (rxTimeout>0) {
deadline = System.currentTimeMillis()+rxTimeout;
remaining = rxTimeout;
}
else {
deadline = Long.MAX_VALUE;
remaining = Long.MAX_VALUE;
}
this.notify();
try {
while (remaining>0 && available()<desired) {
if (rxTimeout>0) {
this.wait(remaining);
remaining = deadline - System.currentTimeMillis();
}
else { // no rxTimeout
this.wait(micronap);
}
}
} catch (InterruptedException e) {}
int bytesToRead = available();
desired = 0;
if (bytesToRead==0) {
return -1;
}
/*
else if (bytesToRead>b.length) {
return readIntoBuffer(b, off, b.length);
}
else return readIntoBuffer(b, off, bytesToRead);
*/
else if (bytesToRead>len) {
return readIntoBuffer(b, off, len);
}
else return readIntoBuffer(b, off, bytesToRead);
}
/**
** Method skip0 removes and discards exctly n characters from the
** input stream (unless an IOException happens first).
*/
public native long skip0(long n)
throws IOException;
/**
** Method skip calls skip0 to skip either n characters or available()
** characters, whichever is the less.
*/
public long skip(long n)
throws IOException
{
int bytesToRead = available();
if (bytesToRead==0) {
return 0;
}
else if (bytesToRead>n) {
return skip0(n);
}
else return skip0(bytesToRead);
}
/**
** Method available() returns the maximum number of bytes which could
** now be read from the input stream without blocking.
*/
public native synchronized int available()
throws IOException;
private native void close0();
/**
** Method close() sets "open" false.
*/
public synchronized void close()
throws IOException
{
if(open){
open = false;
close0();
}
}
/**
** Method setRxThreshold(t) adjusts the value of rxThreshold and
** then gives the monitor a "kick".
*/
public synchronized void setRxThreshold(int t) {
if (t==0) {
clearRxThreshold();
}
else {
if (uartMonitor == null) {
Etc.woempa(9,"setRxThreshold: starting a monitor thread for "+name);
uartMonitor = new Thread(this,name+"-monitor");
uartMonitor.start();
}
rxThreshold = t;
this.notify();
}
}
/**
** Method clearRxThreshold(t) sets rxThreshold to zero (and hence
** desired to 1) and then gives the monitor a "kick".
*/
public void clearRxThreshold() {
rxThreshold = 0;
desired = 1;
this.notify();
}
/**
** Method setRxTimeout(t) adjusts the value of rxTimeout and
** then gives the monitor a "kick".
*/
public synchronized void setRxTimeout(int t) {
if (t==0) {
clearRxTimeout();
}
else {
if (uartMonitor == null) {
Etc.woempa(9,"setRxTimeout: starting a monitor thread for "+name);
uartMonitor = new Thread(this,name+"-monitor");
uartMonitor.start();
}
rxTimeout = t;
this.notify();
}
}
/**
** Method clearRxTimeout(t) sets rxTimeout to zero and
** then gives the monitor a "kick".
*/
public void clearRxTimeout() {
rxTimeout = 0;
this.notify();
}
/**
** Method run() implements the monitor thread. This sits in an endless
** loop until the InputStream is closed:
** - wait until at least "desired" bytes are available;
** - notify the reading thread;
** - wait to be notified in return (e.g. because of a new read).
** The last wait should really be open-ended, but that would be too
** scary.
*/
public synchronized void run() {
while (open) {
try {
while (available()<desired) {
this.wait(micronap);
}
this.notify();
this.wait(meganap);
} catch (InterruptedException e) {
} catch (IOException e) {
}
}
}
/*
** If we are garbage-collected, make sure our run() method also terminates.
** (Do we need to do this?)
*/
protected void finalize() {
try {
close();
}
catch(IOException e) {
}
}
}