diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 17:07:39 +0000 |
| commit | 37da2899f40661e3e9631e497da8dc59b971cbd0 (patch) | |
| tree | cbc6d4680e347d906f5fa7fca73214418741df72 /appl/demo/lego/legolink.b | |
| parent | 54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff) | |
20060303a
Diffstat (limited to 'appl/demo/lego/legolink.b')
| -rw-r--r-- | appl/demo/lego/legolink.b | 601 |
1 files changed, 601 insertions, 0 deletions
diff --git a/appl/demo/lego/legolink.b b/appl/demo/lego/legolink.b new file mode 100644 index 00000000..ea29efd6 --- /dev/null +++ b/appl/demo/lego/legolink.b @@ -0,0 +1,601 @@ +implement LegoLink; + +include "sys.m"; +include "draw.m"; +include "timers.m"; +include "rcxsend.m"; + +LegoLink : module { + init : fn (ctxt : ref Draw->Context, argv : list of string); +}; + +POLLDONT : con 0; +POLLNOW : con 16r02; +POLLDO : con 16r04; + +sys : Sys; +timers : Timers; +Timer : import timers; +datain : chan of array of byte; +errormsg : string; + +error(msg: string) +{ + sys->fprint(sys->fildes(2), "%s\n", msg); + raise "fail:error"; +} + +init(nil : ref Draw->Context, argv : list of string) +{ + sys = load Sys Sys->PATH; + sys->pctl(Sys->NEWPGRP, nil); + + argv = tl argv; + if (len argv != 1) + error("usage: legolink portnum"); + + timers = load Timers "timers.dis"; + if (timers == nil) + error(sys->sprint("cannot load timers module: %r")); + + portnum := int hd argv; + (rdfd, wrfd, err) := serialport(portnum); + if (err != nil) + error(err); + + # set up our mount file + if (sys->bind("#s", "/net", Sys->MBEFORE) == -1) + error(sys->sprint("failed to bind srv device: %r")); + + f2c := sys->file2chan("/net", "legolink"); + if (f2c == nil) + error(sys->sprint("cannot create legolink channel: %r")); + + datain = chan of array of byte; + send := chan of array of byte; + recv := chan of array of byte; + timers->init(50); + spawn reader(rdfd, datain); + consume(); + spawn protocol(wrfd, send, recv); + spawn srvlink(f2c, send, recv); +} + +srvlink(f2c : ref Sys->FileIO, send, recv : chan of array of byte) +{ + me := sys->pctl(0, nil); + rdfid := -1; + wrfid := -1; + buffer := array [256] of byte; + bix := 0; + + rdblk := chan of (int, int, int, Sys->Rread); + readreq := rdblk; + wrblk := chan of (int, array of byte, int, Sys->Rwrite); + writereq := f2c.write; + wrreply : Sys->Rwrite; + sendblk := chan of array of byte; + sendchan := sendblk; + senddata : array of byte; + + for (;;) alt { + data := <- recv => + # got some data from brick, nil for error + if (data == nil) { + # some sort of error + if (wrreply != nil) { + wrreply <- = (0, errormsg); + } + killgrp(me); + } + if (bix + len data > len buffer) { + newb := array [bix + len data + 256] of byte; + newb[0:] = buffer; + buffer = newb; + } + buffer[bix:] = data; + bix += len data; + readreq = f2c.read; + + (offset, count, fid, rc) := <- readreq => + if (rdfid == -1) + rdfid = fid; + if (fid != rdfid) { + if (rc != nil) + rc <- = (nil, "file in use"); + continue; + } + if (rc == nil) { + rdfid = -1; + continue; + } + if (errormsg != nil) { + rc <- = (nil, errormsg); + killgrp(me); + } + # reply with what we've got + if (count > bix) + count = bix; + rdata := array [count] of byte; + rdata[0:] = buffer[0:count]; + buffer[0:] = buffer[count:bix]; + bix -= count; + if (bix == 0) + readreq = rdblk; + alt { + rc <- = (rdata, nil)=> + ; + * => + ; + } + + (offset, data, fid, wc) := <- writereq => + if (wrfid == -1) + wrfid = fid; + if (fid != wrfid) { + if (wc != nil) + wc <- = (0, "file in use"); + continue; + } + if (wc == nil) { + wrfid = -1; + continue; + } + if (errormsg != nil) { + wc <- = (0, errormsg); + killgrp(me); + } + senddata = data; + sendchan = send; + wrreply = wc; + writereq = wrblk; + + sendchan <- = senddata => + alt { + wrreply <- = (len senddata, nil) => + ; + * => + ; + } + wrreply = nil; + sendchan = sendblk; + writereq = f2c.write; + } +} + +killgrp(pid : int) +{ + pctl := sys->open("/prog/" + string pid + "/ctl", Sys->OWRITE); + if (pctl != nil) { + poison := array of byte "killgrp"; + sys->write(pctl, poison, len poison); + } + exit; +} + +serialport(port : int) : (ref Sys->FD, ref Sys->FD, string) +{ + serport := "/dev/eia" + string port; + serctl := serport + "ctl"; + + rfd := sys->open(serport, Sys->OREAD); + if (rfd == nil) + return (nil, nil, sys->sprint("cannot read %s: %r", serport)); + wfd := sys->open(serport, Sys->OWRITE); + if (wfd == nil) + return (nil, nil, sys->sprint("cannot write %s: %r", serport)); + ctlfd := sys->open(serctl, Sys->OWRITE); + if (ctlfd == nil) + return (nil, nil, sys->sprint("cannot open %s: %r", serctl)); + + config := array [] of { + "b2400", + "l8", + "po", + "m0", + "s1", + "d1", + "r1", + }; + + for (i := 0; i < len config; i++) { + cmd := array of byte config[i]; + if (sys->write(ctlfd, cmd, len cmd) <= 0) + return (nil, nil, sys->sprint("serial config (%s): %r", config[i])); + } + return (rfd, wfd, nil); +} + +# reader and nbread as in rcxsend.b +reader(fd : ref Sys->FD, out : chan of array of byte) +{ + # with buf size of 1 there is no need + # for overrun code in nbread() + + buf := array [1] of byte; + for (;;) { + n := sys->read(fd, buf, len buf); + if (n <= 0) + break; + data := array [n] of byte; + data[0:] = buf[0:n]; + out <- = data; + } + out <- = nil; +} + +overrun : array of byte; + +nbread(ms, n : int) : array of byte +{ + ret := array[n] of byte; + tot := 0; + if (overrun != nil) { + if (n < len overrun) { + ret[0:] = overrun[0:n]; + overrun = overrun[n:]; + return ret; + } + ret[0:] = overrun; + tot += len overrun; + overrun = nil; + } + tmr := timers->new(ms, 0); +loop: + while (tot < n) { + tmr.reset(); + alt { + data := <- datain => + if (data == nil) + break loop; + dlen := len data; + if (dlen > n - tot) { + dlen = n - tot; + overrun = data[dlen:]; + } + ret[tot:] = data[0:dlen]; + tot += dlen; + <- tmr.tick => + # reply timeout; + break loop; + } + } + tmr.destroy(); + if (tot == 0) + return nil; + return ret[0:tot]; +} + +consume() +{ + while (nbread(300, 1024) != nil) + ; +} + +# fd: connection to remote client +# send: from local to remote +# recv: from remote to local +protocol(fd : ref Sys->FD, send, recv : chan of array of byte) +{ + seqnum := 0; + towerdown := timers->new(1500, 0); + starttower := 1; + tmr := timers->new(250, 0); + + for (;;) { + data : array of byte = nil; + # get data to send + alt { + data = <- send => + ; + <- tmr.tick => + data = nil; + <- towerdown.tick => + starttower = 1; + continue; + } + + poll := POLLNOW; + while (poll == POLLNOW) { + reply : array of byte; + (reply, poll, errormsg) = datasend(fd, seqnum++, data, starttower); + starttower = 0; + towerdown.reset(); + if (errormsg != nil) { +sys->print("protocol: send error: %s\n", errormsg); + tmr.destroy(); + recv <- = nil; + return; + } + if (reply != nil) { + recv <- = reply; + } + if (poll == POLLNOW) { + # quick check to see if we have any more data + alt { + data = <- send => + ; + * => + data = nil; + } + } + } + if (poll == POLLDO) + tmr.reset(); + else + tmr.cancel(); + } +} + +TX_HDR : con 3; +DL_HDR : con 5; # 16r45 seqLSB seqMSB lenLSB lenMSB +DL_CKSM : con 1; +LN_HDR : con 1; +LN_JUNK : con 2; +LN_LEN : con 2; +LN_RXLEN : con 2; +LN_POLLMASK : con 16r06; +LN_COMPMASK : con 16r08; + + +# send a message (may be empty) +# wait for the reply +# returns (data, poll request, error) + +datasend(wrfd : ref Sys->FD, seqnum : int, data : array of byte, startup : int) : (array of byte, int, string) +{ +if (startup) { + dummy := array [] of { byte 255, byte 0, byte 255, byte 0}; + sys->write(wrfd, dummy, len dummy); + nbread(100, 100); +} + seqnum = seqnum & 1; + docomp := 0; + if (data != nil) { + comp := rlencode(data); + if (len comp < len data) { + docomp = 1; + data = comp; + } + } + + # construct the link-level data packet + # DL_HDR LN_HDR data cksum + # last byte of data is stored in cksum byte + llen := LN_HDR + len data; + blklen := LN_LEN + llen - 1; # llen includes cksum + ldata := array [DL_HDR + blklen + 1] of byte; + + # DL_HDR + if (seqnum == 0) + ldata[0] = byte 16r45; + else + ldata[0] = byte 16r4d; + ldata[1] = byte 0; # blk number LSB + ldata[2] = byte 0; # blk number MSB + ldata[3] = byte (blklen & 16rff); # blk length LSB + ldata[4] = byte ((blklen >> 8) & 16rff); # blk length MSB + + # LN_LEN + ldata[5] = byte (llen & 16rff); + ldata[6] = byte ((llen>>8) & 16rff); + # LN_HDR + lhval := byte 0; + if (seqnum == 1) + lhval |= byte 16r01; + if (docomp) + lhval |= byte 16r08; + + ldata[7] = lhval; + + # data (+cksum) + ldata[8:] = data; + + # construct the rcx data packet + # TX_HDR (dn ~dn) cksum ~cksum + rcxlen := TX_HDR + 2*(len ldata + 1); + rcxdata := array [rcxlen] of byte; + + rcxdata[0] = byte 16r55; + rcxdata[1] = byte 16rff; + rcxdata[2] = byte 16r00; + rcix := TX_HDR; + cksum := 0; + for (i := 0; i < len ldata; i++) { + b := ldata[i]; + rcxdata[rcix++] = b; + rcxdata[rcix++] = ~b; + cksum += int b; + } + rcxdata[rcix++] = byte (cksum & 16rff); + rcxdata[rcix++] = byte (~cksum & 16rff); + + # send it + err : string; + reply : array of byte; + for (try := 0; try < 8; try++) { + if (err != nil) + sys->print("Try %d (lasterr %s)\n", try, err); + err = ""; + step := 8; + for (i = 0; err == nil && i < rcxlen; i += step) { + if (i + step > rcxlen) + step = rcxlen -i; + if (sys->write(wrfd, rcxdata[i:i+step], step) != step) { + return (nil, 0, "hangup"); + } + + # get the echo + reply = nbread(300, step); + if (reply == nil || len reply != step) + # short echo + err = "tower not responding"; + + # check the echo + for (ei := 0; err == nil && ei < step; ei++) { + if (reply[ei] != rcxdata[i+ei]) + # echo mis-match + err = "serial comms error"; + } + } + if (err != nil) { + consume(); + continue; + } + + # wait for a reply + replen := TX_HDR + LN_JUNK + 2*LN_RXLEN; + reply = nbread(300, replen); + if (reply == nil || len reply != replen) { + err = "brick not responding"; + consume(); + continue; + } + if (reply[0] != byte 16r55 || reply[1] != byte 16rff || reply[2] != byte 0 + || reply[5] != ~reply[6] || reply[7] != ~reply[8]) { + err = "bad reply from brick"; + consume(); + continue; + } + # reply[3] and reply [4] are junk, ~junk + # put on front of msg by rcx rom + replen = int reply[5] + ((int reply[7]) << 8) + 1; + cksum = int reply[3] + int reply[5] + int reply[7]; + reply = nbread(200, replen * 2); + if (reply == nil || len reply != replen * 2) { + err = "short reply from brick"; + consume(); + continue; + } + cksum += int reply[0]; + for (i = 1; i < replen; i++) { + reply[i] = reply[2*i]; + cksum += int reply[i]; + } + cksum -= int reply[replen-1]; + if (reply[replen-1] != byte (cksum & 16rff)) { + err = "bad checksum from brick"; + consume(); + continue; + } + if ((reply[0] & byte 1) != byte (seqnum & 1)) { + # seqnum error + # we have read everything, don't bother with consume() + err = "bad seqnum from brick"; + continue; + } + + # TADA! we have a valid message + mdata : array of byte; + lnhdr := int reply[0]; + poll := lnhdr & LN_POLLMASK; + if (replen > 2) { + # more than just hdr and cksum + if (lnhdr & LN_COMPMASK) { + mdata = rldecode(reply[1:replen-1]); + if (mdata == nil) { + err = "bad brick msg compression"; + continue; + } + } else { + mdata = array [replen - 2] of byte; + mdata[0:] = reply[1:replen-1]; + } + } + return (mdata, poll, nil); + } + return (nil, 0, err); +} + + +rlencode(data : array of byte) : array of byte +{ + srcix := 0; + outix := 0; + out := array [64] of byte; + val := 0; + nextval := -1; + n0 := 0; + + while (srcix < len data || nextval != -1) { + if (nextval != -1) { + val = nextval; + nextval = -1; + } else { + val = int data[srcix]; + if (val == 16r88) + nextval = 0; + if (val == 0) { + n0++; + srcix++; + if (srcix < len data && n0 < 16rff + 2) + continue; + } + case n0 { + 0 => + srcix++; + 1 => + val = 0; + nextval = -1; + n0 = 0; + 2 => + val = 0; + nextval = 0; + n0 = 0; + * => + val = 16r88; + nextval = (n0-2); + n0 = 0; + } + } + if (outix >= len out) { + newout := array [2 * len out] of byte; + newout[0:] = out; + out = newout; + } + out[outix++] = byte val; + } + return out[0:outix]; +} + +rldecode(data : array of byte) : array of byte +{ + srcix := 0; + outix := 0; + out := array [64] of byte; + + n0 := 0; + val := 0; + while (srcix < len data || n0 > 0) { + if (n0 > 0) + n0--; + else { + val = int data[srcix++]; + if (val == 16r88) { + if (srcix >= len data) + # bad encoding + return nil; + n0 = int data[srcix++]; + if (n0 > 0) { + n0 += 2; + val = 0; + continue; + } + } + } + if (outix >= len out) { + newout := array [2 * len out] of byte; + newout[0:] = out; + out = newout; + } + out[outix++] = byte val; + } + return out[0:outix]; +} + +hexdump(data : array of byte) +{ + for (i := 0; i < len data; i++) + sys->print("%.2x ", int data[i]); + sys->print("\n"); +} |
