/*
* Copyright (c) Martin Schoeberl, martin@jopdesign.com
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Martin Schoeberl
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS OR 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.
*
*/
//
// Nullmodem cable (both female):
//
// 1 NC
// 2 3
// 3 2
// 4 6
// 5 5
// 6 4
// 7 8
// 8 7
// 9 NC
//
package ejip;
/**
* Slip.java
*
* communicate with JOP via serial line.
*/
import util.Serial;
import util.Timer;
/**
* Slip driver.
*/
public class Slip extends LinkLayer {
private static final int MAX_BUF = 1500; // or should we use 1006
private static final int END = 0xc0;
private static final int ESC = 0xdb;
private static final int ESC_END = 0xdc;
private static final int ESC_ESC = 0xdd;
private static int simSendErr, simRcvErr;
/**
* receive buffer
*/
private int[] rbuf;
/**
* send buffer
*/
private int[] sbuf;
/**
* bytes received.
*/
private int cnt;
/**
* mark escape sequence.
*/
private boolean esc;
/**
* an ip packet is in the receive buffer.
*/
private boolean ready;
/**
* bytes to be sent. 0 means txFree
*/
private int scnt;
/**
* allready sent bytes.
*/
private int sent;
private Serial ser;
/**
* Create a SLIP connection.
*/
public Slip(Ejip ejip, Serial serPort, int ipAddr) {
super(ejip, ipAddr);
rbuf = new int[MAX_BUF];
sbuf = new int[MAX_BUF];
cnt = 0;
esc = false;
ready = false;
scnt = 0;
sent = 0;
ser = serPort;
}
int timer;
/**
* main loop.
*/
public void run() {
Packet p;
//
// read, write loop
//
if (ipLoop()) {
timer = Timer.getTimeoutMs(1000);
} else {
if (Timer.timeout(timer) && cnt > 0) { // flush buffer on timeout
// (1000ms)
if (Logging.LOG) {
Logging.wr('t');
for (int i = 0; i < cnt; ++i) {
int val = rbuf[i];
if (val == '\r') {
Logging.wr('r');
} else {
Logging.wr((char) val);
}
}
}
cnt = 0;
esc = false;
ready = false;
timer = Timer.getTimeoutMs(1000);
// send anything back for Windoz slip version
if (ser.txFreeCnt() > 0) {
ser.wr('C');
ser.wr('L');
ser.wr('I');
ser.wr('E');
ser.wr('N');
ser.wr('T');
ser.wr('S');
ser.wr('E');
ser.wr('R');
ser.wr('V');
ser.wr('E');
ser.wr('R');
ser.wr('\r');
}
}
}
//
// copy packet to packet buffer.
//
if (ready) { // we got a packet
read();
}
if (scnt == 0) { // transmit buffer is free
//
// get a ready to send packet with source from this driver.
//
p = txQueue.deq();
if (p != null) {
send(p); // send one packet
}
}
}
/**
* get a Packet buffer and copy from receive buffer.
*/
private void read() {
int i, j, k;
Packet p = ejip.getFreePacket(this);
if (p==null) {
if (Logging.LOG) Logging.wr('!');
cnt = 0; // drop it
ready = false; // don't block the receiver
return;
}
int[] pb = p.buf;
rbuf[cnt] = 0; // fill remaining bytes to word
rbuf[cnt+1] = 0; // boundry with 0 for UDP
rbuf[cnt+2] = 0; // checksum
// copy buffer
k = 0;
for (i=0; i<cnt; i+=4) {
for (j=0; j<4; ++j) {
k <<= 8;
k += rbuf[i+j];
}
pb[i>>>2] = k;
}
p.len = cnt;
if (Logging.LOG) Logging.wr('r');
if (Logging.LOG) Logging.intVal(cnt);
cnt = 0;
ready = false;
/*
++simRcvErr;
if (simRcvErr%5==1) {
p.setStatus(Packet.FREE);
if (Logging.LOG) Logging.wr(" rcv dropped");
if (Logging.LOG) Logging.lf();
return;
}
*/
rxQueue.enq(p); // inform upper layer
}
/**
* copy packet to send buffer.
*/
private void send(Packet p) {
int i, k;
int[] pb = p.buf;
if (Logging.LOG) Logging.wr('s');
if (Logging.LOG) Logging.intVal(p.len);
/*
++simSendErr;
if (simSendErr%7==3) {
p.setStatus(Packet.FREE);
if (Logging.LOG) Logging.wr(" send dropped");
if (Logging.LOG) Logging.lf();
return;
}
*/
scnt = p.len;
sent = 0;
for (i=0; i<scnt; i+=4) {
k = pb[i>>>2];
sbuf[i] = k>>>24;
sbuf[i+1] = (k>>>16)&0xff;
sbuf[i+2] = (k>>>8)&0xff;
sbuf[i+3] = k&0xff;
}
if (!p.isTcpOnFly) {
ejip.returnPacket(p);
}
}
/**
* read from serial buffer and build an ip packet.
* send a packet if one is in our send buffer.
*/
private boolean ipLoop() {
int i;
boolean ret = false;
i = ser.rxCnt();
if (i!=0) {
ret = true;
rcv(i);
}
if (cnt==MAX_BUF && !ready) cnt = 0; // buffer full, but not ready => drop it
if (scnt!=0) {
i = ser.txFreeCnt();
if (i>2) {
snd(i);
}
}
return ret;
}
/**
* copy from send buffer to serial buffer.
*/
private void snd(int free) {
int i;
if (sent==0) {
ser.wr(END);
--free;
}
for (i=sent; free>1 && i<scnt; ++i) {
int c = sbuf[i];
if (c==END) {
ser.wr(ESC);
ser.wr(ESC_END);
free -= 2;
} else if (c==ESC) {
ser.wr(ESC);
ser.wr(ESC_ESC);
free -= 2;
} else {
ser.wr(c);
--free;
}
}
sent = i;
if (sent==scnt && free!=0) {
ser.wr(END);
scnt = 0;
sent = 0;
}
}
/**
* copy from serial buffer to receive buffer.
*/
private void rcv(int len) {
int i;
// get all bytes from serial buffer
for (i=0; i<len && cnt<MAX_BUF; ++i) {
if (ready) break; // wait till buffer is copied
int val = ser.rd();
if (esc) {
if (val == ESC_END) {
rbuf[cnt++] = END;
} else if (val == ESC_ESC) {
rbuf[cnt++] = ESC;
} else {
rbuf[cnt++] = val;
}
esc = false;
continue;
}
if (val == ESC) {
esc = true;
} else {
esc = false;
if (val==END) {
if (cnt>=20) {
ready = true; // we got one packet
break;
} else { // ignore too short packages
cnt = 0;
continue;
}
} else {
rbuf[cnt++] = val;
}
}
}
}
}