/* * 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. * */ package ejip123.legacy; import com.jopdesign.sys.Const; import com.jopdesign.sys.Native; import ejip123.util.Dbg; import ejip123.Packet; import ejip123.TcpConnection; import ejip123.Tcp; import ejip123.Ip; /** a very SIMPLE HTML server class. */ public class Html{ private static String prot; private static String end; private static int[] val; private static int[] tmp; private static int hits; private static int[] outVal; private static int[] valArr; private static int[] msg; private static final int MAX_MSG = 80; private static String text; private static final int WINDOW = 2680; private Html(){ } /** allocate buffers. */ public static void init(){ val = new int[5]; tmp = new int[1500]; msg = new int[MAX_MSG]; outVal = new int[1]; for(int i = 0; i < MAX_MSG; ++i) msg[i] = ' '; // hits = 17800; // 10.3.2003 auch jetzt bei ACEX // hits = 18320; // 14.3.2003 // hits = 18750; // 20.3.2003 // hits = 19720; // 31.3.2003 // hits = 19990; // 10.4.2003 // hits = 22030; // 15.5.2003 // hits = 27472; // 22.7.2003 // hits = 30104; // 23.9.2003 // hits = 30390; // 1.10.2003 // hits = 30477; // 6.10.2003 // hits = 32914; // 13.2.2004 // hits = 37038; // 27.4.2004 // hits = 38175; // 13.7.2004 // hits = 38800; // 17.8.2004 hits = 39291; // 9.9.2004 hits = 0; outVal[0] = 0; Native.wr(outVal[0], Const.IO_OUT); text = /* "<html><head></head><body>"+ "<h2>Hej Rasmus and DDM class</h2>"+ "<h3>Greetings from Vienna.</h3>"+ "<h3>Have fun,</h3>"+ "<h3>Martin</h3>"+ "</body></html>"; */ /* "<html><head></head><body><h2>BG 263</h2>"+ "Some Communication Statistics<p>"+ "sent packets #0<br>"+ "sent bytes #1<br>"+ "rcvd packets #2<br>"+ "rcvd bytes #3<br>"+ "wrong packets #4<br>"+ "</body></html>"; */ "<html><head></head><body>" + // "<h2>TAL TeleAlarm</h2>"+ "<h2><a href=\"http://www.jopdesign.com/\">JOP</a> Web Server on Altera Cyclone EP1C6</h2>" + "Analog 1: !a1 mA<br>" + "Analog 2: !a2 mA<br>" + "Vbat: !a3 V<p>" + "input 1: !i1<br>" + "input 2: !i2<br>" + "input 3: !i3<br>" + "input 4: !i4<br>" + "input 5: !i5<br>" + "input 6: !i6<br>" + "input 7: !i7<br>" + "input 8: !i8<br>" + "input 9: !i9<br>" + "input 10: !i:<br>" + "<form method=\"get\">" + "output 1: !o1 <input type=\"checkbox\" name=\"o1\">set 1<br>" + "output 2: !o2 <input type=\"checkbox\" name=\"o2\">set 2<br>" + "output 3: !o3 <input type=\"checkbox\" name=\"o3\">set 3<br>" + "output 4: !o4 <input type=\"checkbox\" name=\"o4\">set 4" + "<p><input type=\"submit\" value=\"Set outputs\">" + "</form>" + "<p><sub>!ht</sub>" + "</body></html>"; prot = "HTTP/1.0 200 OK\r\n\r\n"; end = "\r\n\r\n"; } /** set value array */ public static void setValArray(int[] vals){ valArr = vals; } public static void setOutValArray(int[] out){ outVal = out; } private static int append(int[] buf, int pos, String str){ int ret, len; len = str.length(); for(ret = 0; ret < len; ++ret){ buf[pos + ret] = str.charAt(ret); } return ret; } private static int append(int[] buf, int pos, int[] str){ int ret; for(ret = 0; ret < str.length; ++ret){ buf[pos + ret] = str[ret]; } return ret; } private static int[] getTemp(){ int i = Native.rd(Const.IO_ADC1); // Dbg.intVal(i>>>16); i &= 0xffff; // Dbg.wr('T'); // Dbg.intVal(i); i = (i - 600) / 17 + 27; // Dbg.intVal(i); if(i > 99) i = 99; if(i < 0){ i = -i; val[0] = '-'; } else{ val[0] = ' '; } val[1] = '0' + i / 10; val[2] = '0' + i % 10; val[3] = ' '; val[4] = ' '; return val; } private static void setInt(int val, int[] buf){ int i; for(i = buf.length - 1; i >= 0; --i){ buf[i] = val % 10 + '0'; val /= 10; if(val == 0) break; } --i; for(; i >= 0; --i){ buf[i] = ' '; } } private static int[] getAnalog(int channel){ int i = 0; if(channel == 1){ i = Native.rd(Const.IO_ADC1); // I = ADCout * 3.3 / (100 * (2^16-1)) i *= 100; i /= 19859; } else if(channel == 2){ i = Native.rd(Const.IO_ADC2); i *= 100; i /= 19859; } else if(channel == 3){ i = Native.rd(Const.IO_ADC3); // U = 11 * ADCout * 3.3 / (2^16-1) i *= 100; i /= 18054; } // value is now in 1/10 mA or 1/10 V // setInt(i, val); /* i += 100; i /= 201; */ val[4] = ' '; val[3] = '0' + i % 10; val[2] = '.'; i /= 10; val[1] = '0' + i % 10; val[0] = '0' + i / 10; return val; } private static int[] getHit(){ int j; for(j = 0; j < 5; ++j){ val[j] = ' '; } val[0] = '0' + hits / 10000 % 10; val[1] = '0' + hits / 1000 % 10; val[2] = '0' + hits / 100 % 10; val[3] = '0' + hits / 10 % 10; val[4] = '0' + hits % 10; return val; } private static int[] getDigital(){ setInt(Native.rd(Const.IO_IN), val); return val; } private static void setMsg(int[] buf, int pos){ int i, j; for(i = 0; i < MAX_MSG; ++i){ msg[i] = ' '; } // Dbg.wr('m'); // Dbg.wr(':'); for(i = 0; i < MAX_MSG; ++i){ j = buf[pos + i]; if(j == ' ') break; if(j == '+') j = ' '; msg[i] = j; //Dbg.wr(j); } } private static final int HTML_START = 0x90000; // start at first address (should be changed!) private static int getChar(int pos){ // data from Flash // return Native.rdMem(HTML_START+pos); // data from String if(pos >= text.length()) return 0; return text.charAt(pos); } private static int setText(int[] buf, int req_pos, int req_len, int ret_pos){ int i, j, k; //Dbg.wr('\n'); //Dbg.wr('h'); //Dbg.wr('t'); //Dbg.wr('m'); //Dbg.wr('l'); //Dbg.wr(':'); //Dbg.intVal(hits); // copy request to 'byte' buffer for(i = 0; i < req_len; i += 4){ j = buf[req_pos + (i>>2)]; tmp[i] = j>>>24; tmp[i + 1] = (j>>>16)&0xff; tmp[i + 2] = (j>>>8)&0xff; tmp[i + 3] = j&0xff; } Dbg.wr('\n'); for(i = 0; i < req_len; ++i) Dbg.wr(tmp[i]); int ret = 0; if(tmp[0] != 'G' || tmp[1] != 'E') return 0; k = tmp[5]; if(k == 'T'){ // request for 'Tal.class' ret = setClassFile(tmp); } else{ processRequest(tmp, 5); ret += append(tmp, ret, prot); if(k == 'd'){ // request for 'data.txt' ret += setData(tmp, ret); } else{ ++hits; for(i = 0; i < 1000; ++i){ j = getChar(i); if(j == 0) break; // EOF reached if(j == '!'){ ++i; j = getChar(i); ++i; k = getChar(i); ret += setSpecial(tmp, ret, j, k); } else if(j == '#'){ ++i; j = getChar(i) - '0'; if(valArr != null && j >= 0 && j < valArr.length){ ret += setVal(tmp, ret, valArr[j]); } } else{ tmp[ret] = j; ++ret; } } } ret += append(tmp, ret, end); } // copy replay to word buffer /* Dbg.wr('\n'); for (i=0; i<ret; ++i) Dbg.wr(tmp[i]); Dbg.wr('\n'); */ tmp[ret] = 0; // make shure last bytes are 0 for checksum tmp[ret + 1] = 0; tmp[ret + 2] = 0; k = 0; for(i = 0; i < (ret + 3)>>2; ++i){ j = (tmp[k]<<24) + (tmp[k + 1]<<16) + (tmp[k + 2]<<8) + tmp[k + 3]; buf[ret_pos + i] = j; k += 4; } return ret; } /** Handle one HTTP request. */ private static void processRequest(int[] buf, int pos){ if(buf[pos] != '?') return; // nothing to do! int i = pos + 1; if(buf[i] == 'm'){ setMsg(buf, i + 2); } else{ // set/reset outValuts outVal[0] = 0; for(; i < 100; ++i){ if(buf[i] == 'o'){ ++i; int j = buf[i] - '1'; if(j >= 0 && j <= 3){ outVal[0] |= 1<<j; } } else if(buf[i] == '\r'){ break; } } Native.wr(outVal[0], Const.IO_OUT); } } private static int setVal(int[] buf, int pos, int val){ int j; for(j = 0; j < 5; ++j){ buf[pos + j] = ' '; } buf[pos] = '0' + val / 1000000 % 10; buf[pos + 1] = '0' + val / 100000 % 10; buf[pos + 2] = '0' + val / 10000 % 10; buf[pos + 3] = '0' + val / 1000 % 10; buf[pos + 4] = '0' + val / 100 % 10; buf[pos + 5] = '0' + val / 10 % 10; buf[pos + 6] = '0' + val % 10; return 7; } private static int setSpecial(int[] buf, int pos, int ch1, int ch2){ int i, j; if(ch1 == 'm'){ return append(buf, pos, msg); } else if(ch1 == 'a'){ return append(buf, pos, getAnalog(ch2 - '0')); } else if(ch1 == 't'){ return append(buf, pos, getTemp()); } else if(ch1 == 'h'){ return append(buf, pos, getHit()); } else if(ch1 == 'i'){ i = Native.rd(Const.IO_IN); j = ch2 - '1'; if((i&(1<<j)) != 0){ return append(buf, pos, "on"); } else{ return append(buf, pos, "off"); } } else if(ch1 == 'o'){ i = outVal[0]; j = ch2 - '1'; if((i&(1<<j)) != 0){ return append(buf, pos, "on"); } else{ return append(buf, pos, "off"); } } return 0; } private static int setData(int[] buf, int pos){ int ret = 0; ret += append(buf, pos + ret, getAnalog(1)); ret += append(buf, pos + ret, getAnalog(2)); ret += append(buf, pos + ret, getDigital()); buf[pos + ret] = ' '; ++ret; ret += append(buf, pos + ret, getHit()); return ret; } private static final int APPL_START = 0x80000 + 0x30000; // start of class file // load Applet Tal.class private static int setClassFile(int[] buf){ int ret = Native.rdMem(APPL_START)<<8; ret += Native.rdMem(APPL_START + 1); for(int i = 0; i < ret; ++i){ buf[i] = Native.rdMem(APPL_START + 2 + i); } return ret; } // TODO:!!!!!! do a real state machine, end is wrong (sending ack in fw1 !!!) makes remote site crazy public static void doTCP(Packet p){ int i; int datlen; int[] buf = p.buf; int rcvcnt, sndcnt; int fl; Dbg.wr('T'); // Find the payload i = buf[8]>>>16; int flags = i&0xff; int hlen = i>>>12; datlen = p.len() - 20 - (hlen<<2); // "TCB" // In a full tcp implementation we would keep track of this per // connection. // This implementation only handles one connection at a time. // As a result, very little of this state is actually used after // the reply packet has been sent. // if (datlen < 0) return 0; // If it's not http, just drop it i = buf[5]; if((i&0xffff) != 80){ Dbg.lf(); Dbg.wr('T'); Dbg.intVal(i&0xffff); p.setLen(0); return; } // Get source port int tcb_port = i>>>16; rcvcnt = buf[6]; // sequence number sndcnt = buf[7]; // acknowledge number // sndcnt has to be incremented for SYN!!! fl = TcpConnection.FL_ACK; p.setLen(40); // Figure out what kind of packet this is, and respond if((flags&TcpConnection.FL_SYN) != 0){ // SYN sndcnt = -1; // start with -1 for SYN rcvcnt++; fl |= TcpConnection.FL_SYN; // tcb_st = ST_ESTAB; } else if(datlen > 0){ // incoming data rcvcnt += datlen; // TODO get url if(sndcnt == 0){ p.setLen(p.len() + setText(buf, 5 + hlen, datlen, 10)); // Send reply packet // if (len > MTU) len = MTU; // TODO MTU should be taken from // tcp options // Read next segment of data into buffer } else{ fl |= TcpConnection.FL_FIN; // tcb_st = ST_FW1; } fl |= TcpConnection.FL_PSH; } else if((flags&TcpConnection.FL_FIN) != 0){ // FIN rcvcnt++; // Don't bother with FIN-WAIT-2, TIME-WAIT, or CLOSED; they just // cause trouble // tcb_st = ST_LISTEN; } else if((flags&TcpConnection.FL_ACK) != 0){ // ack with no data if(sndcnt > 0){ // calculate no of bytes left to send // i = len2send - sndnxt i = 0; // TODO: wtf. if(i == 0){ // EOF; send FIN fl |= TcpConnection.FL_FIN; // tcb_st = ST_FW1; } else if(i > 0){ // not EOF; send next segment // len += i; fl |= TcpConnection.FL_PSH; } else{ // ***** this is never used! thats bad // ack of FIN; no reply p.setLen(0); return; } } else{ p.setLen(0); return; // No reply packet } } else{ p.setLen(0); return; // drop it } // Fill in TCP header buf[5] = (80<<16) + tcb_port; buf[6] = sndcnt; buf[7] = rcvcnt; buf[8] = 0x50000000 + (fl<<16) + WINDOW; // hlen = 20, no options buf[9] = 0; // clear checksum field buf[2] = (Tcp.PROTOCOL<<16) + p.len() - 20; // set protocol and tcp length // in iph checksum for tcp // checksum buf[9] = Ip.chkSum(buf, 2, p.len() - 8)<<16; } }