summaryrefslogtreecommitdiff
path: root/appl/wm/minitel/modem.b
diff options
context:
space:
mode:
authorCharles Forsyth <charles.forsyth@gmail.com>2015-04-29 15:19:07 +0100
committerCharles Forsyth <charles.forsyth@gmail.com>2015-04-29 15:19:07 +0100
commitc714c442442ef137f20ca4ff9707d5480cb9ba7a (patch)
treed0d285f05cb4292fa8f1f3c0bc70bec1251e956e /appl/wm/minitel/modem.b
parent1ac9729e9325d84db36c04b5cda3b5b1bc0d041f (diff)
remove obsolete minitel, but leave source as example
Diffstat (limited to 'appl/wm/minitel/modem.b')
-rw-r--r--appl/wm/minitel/modem.b620
1 files changed, 0 insertions, 620 deletions
diff --git a/appl/wm/minitel/modem.b b/appl/wm/minitel/modem.b
deleted file mode 100644
index b7a21c1d..00000000
--- a/appl/wm/minitel/modem.b
+++ /dev/null
@@ -1,620 +0,0 @@
-#
-# Copyright © 1998 Vita Nuova Limited. All rights reserved.
-#
-
-#modem states for direct connection
-MSstart, MSdialing, MSconnected, MSdisconnecting,
-
-# special features
-Ecp # error correction
- : con (1 << iota);
-
-Ecplen: con 17; # error correction block length: data[15], crc, validation (=0)
-
-Modem: adt {
- m: ref Module; # common attributes
- in: chan of ref Event;
-
- connect: int; # None, Direct, Network
- state: int; # modem dialing state
- saved: string; # response, so far (direct dial)
- initstr: string; # softmodem init string (direct dial)
- dialstr: string; # softmodem dial string (direct dial)
- lastdialstr: string;
-
- spec: int; # special features
- fd: ref Sys->FD; # modem data file, if != nil
- cfd: ref Sys->FD; # modem ctl file, if != nil (direct dial only)
- devpath: string; # path to the modem;
- avail: array of byte; # already read
- rd: chan of array of byte; # reader -> rd
- pid: int; # reader pid if != 0
-
- seq: int; # ECP block sequence number
- waitsyn: int; # awaiting restart SYN SYN ... sequence
- errforce: int;
- addparity: int; # must add parity to outgoing data
-
- init: fn(m: self ref Modem, connect: int, initstr, dialstr: string);
- reset: fn(m: self ref Modem);
- run: fn(m: self ref Modem);
- quit: fn(m: self ref Modem);
- runstate: fn(m: self ref Modem, data: array of byte);
- write: fn(m: self ref Modem, data: array of byte):int; # to network
- reader: fn(m: self ref Modem, pidc: chan of int);
-};
-
-partab: array of byte;
-
-dump(a: array of byte, n: int): string
-{
- s := sys->sprint("[%d]", n);
- for(i := 0; i < n; i++)
- s += sys->sprint(" %.2x", int a[i]);
- return s;
-}
-
-Modem.init(m: self ref Modem, connect: int, initstr, dialstr: string)
-{
- partab = array[128] of byte;
- for(c := 0; c < 128; c++)
- if(parity(c))
- partab[c] = byte (c | 16r80);
- else
- partab[c] = byte c;
- m.in = chan of ref Event;
- m.connect = connect;
- m.state = MSstart;
- m.initstr = initstr;
- m.dialstr = dialstr;
- m.pid = 0;
- m.spec = 0;
- m.seq = 0;
- m.waitsyn = 0;
- m.errforce = 0;
- m.addparity = 0;
- m.avail = array[0] of byte;
- m.rd = chan of array of byte;
- m.reset();
-}
-
-Modem.reset(m: self ref Modem)
-{
- m.m = ref Module(Pscreen, 0);
-}
-
-Modem.run(m: self ref Modem)
-{
- if(m.dialstr != nil)
- send(ref Event.Eproto(Pmodem, Mmodem, Cconnect, "", 0,0,0));
-Runloop:
- for(;;){
- alt {
- ev := <- m.in =>
- pick e := ev {
- Equit =>
- break Runloop;
- Edata =>
- if(debug['m'] > 0)
- fprint(stderr, "Modem <- %s\n", e.str());
- m.write(e.data);
- if(T.state == Local || T.spec & Echo) { # loopback
- if(e.from == Mkeyb) {
- send(ref Event.Eproto(Pscreen, Mkeyb, Ccursor, "", 0,0,0));
- send(ref Event.Edata(Pscreen, Mkeyb, e.data));
- }
- }
- Eproto =>
- case e.cmd {
- Creset =>
- m.reset();
- Cconnect =>
- if(m.pid != 0)
- break;
- m.addparity = 1;
- T.state = Connecting;
- send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0));
-
- case m.connect {
- Direct =>
- S.msg("Appel "+m.dialstr+" ...");
- dev := "/dev/modem";
- if(openmodem(m, dev) < 0) {
- S.msg("Modem non prêt");
- T.state = Local;
- send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0));
- break;
- }
- m.state = MSdialing;
- m.saved = "";
- dialout(m);
- T.terminalid = TERMINALID2;
- Network =>
- S.msg("Connexion au serveur ...");
- if(debug['m'] > 0 || debug['M'] > 0)
- sys->print("dial(%s)\n", m.dialstr);
- (ok, cx) := sys->dial(m.dialstr, "");
- if (ok == -1){
- S.msg("Echec de la connexion");
- T.state = Local;
- send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0));
- if(debug['m'] > 0)
- sys->print("can't dial %s: %r\n", m.dialstr);
- break;
- }
- m.fd = sys->open(cx.dir + "/data", Sys->ORDWR);
- m.cfd = cx.cfd;
- if(len m.dialstr >= 3 && m.dialstr[0:3] == "tcp")
- m.addparity = 0; # Internet gateway apparently doesn't require parity
- if(m.fd != nil) {
- S.msg(nil);
- m.state = MSconnected;
- T.state = Online;
- send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0));
- }
- T.terminalid = TERMINALID1;
- }
- if(m.fd != nil) {
- pidc := chan of int;
- spawn m.reader(pidc);
- m.pid = <-pidc;
- }
- Cdisconnect =>
- if(m.pid != 0) {
- S.msg("Déconnexion ...");
- m.state = MSdisconnecting;
- }
- if(m.connect == Direct)
- hangup(m);
- else
- nethangup(m);
- Cplay => # for testing
- case e.s {
- "play" =>
- replay(m);
- }
- Crequestecp =>
- if(m.spec & Ecp){ # for testing: if already active, force an error
- m.errforce = 1;
- break;
- }
- m.write(array[] of {byte SEP, byte 16r4A});
-sys->print("sending request for ecp\n");
- Cstartecp =>
- m.spec |= Ecp;
- m.seq = 0; # not in spec
- m.waitsyn = 0; # not in spec
- Cstopecp =>
- m.spec &= ~Ecp;
- * => break;
- }
- }
- b := <- m.rd =>
- if(debug['m'] > 0){
- fprint(stderr, "Modem -> %s\n", dump(b,len b));
- }
- if(b == nil) {
- m.pid = 0;
- case m.state {
- MSdialing =>
- S.msg("Echec appel");
- MSdisconnecting =>
- S.msg(nil);
- }
- m.state = MSstart;
- T.state = Local;
- send(ref Event.Eproto(Pscreen, Mmodem, Cscreenon, "",0,0,0));
- send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0));
- break;
- }
- m.runstate(b);
- }
- }
- if(m.pid != 0)
- kill(m.pid);
- send(nil);
-}
-
-Modem.quit(nil: self ref Modem)
-{
-}
-
-Modem.runstate(m: self ref Modem, data: array of byte)
-{
- if(debug['m']>0)
- sys->print("runstate %d %s\n", m.state, dump(data, len data));
- case m.state {
- MSstart => ;
- MSdialing =>
- for(i:=0; i<len data; i++) {
- ch := int data[i];
- if(ch != '\n' && ch != '\r') {
- m.saved[len m.saved] = ch;
- continue;
- }
- (code, str) := seenreply(m.saved);
- case code {
- Noise or Ok => ;
- Success =>
- S.msg(nil);
- m.state = MSconnected;
- T.state = Online;
- send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0));
- Failure =>
- hangup(m);
- S.msg(str);
- m.state = MSstart;
- T.state = Local;
- send(ref Event.Eproto(Pscreen, Mmodem, Cindicators, "",0,0,0));
- }
- m.saved = "";
- }
- MSconnected =>
- send(ref Event.Edata(m.m.path, Mmodem, data));
- MSdisconnecting => ;
- }
-}
-
-Modem.write(m: self ref Modem, data: array of byte): int
-{
- if(m.fd == nil)
- return -1;
- if(len data == 0)
- return 0;
- if(m.addparity){
- # unfortunately must copy data to add parity for direct modem connection
- pa := array[len data] of byte;
- for(i := 0; i<len data; i++)
- pa[i] = partab[int data[i] & 16r7F];
- data = pa;
- }
- if(debug['m']>0)
- sys->print("WRITE %s\n", dump(data, len data));
- return sys->write(m.fd, data, len data);
-}
-
-#
-# minitel error correction protocol
-#
-# SYN, SYN, block number start of retransmission
-# NUL ignored
-# DLE escapes {DLE, SYN, NACK, NUL}
-# NACK, block restart request
-#
-
-crctab: array of int;
-Crcpoly: con 16r9; # crc7 = x^7+x^3+1
-
-# precalculate the CRC7 remainder for all bytes
-
-mktabs()
-{
- crctab = array[256] of int;
- for(c := 0; c < 256; c++){
- v := c;
- crc := 0;
- for(i := 0; i < 8; i++){
- crc <<= 1; # align remainder's MSB with value's
- if((v^crc) & 16r80)
- crc ^= Crcpoly;
- v <<= 1;
- }
- crctab[c] = (crc<<1) & 16rFE; # pre-align the result to save <<1 later
- }
-}
-
-# return the index of the first non-NUL character (the start of a block)
-
-nextblock(a: array of byte, i: int, n: int): int
-{
- for(; i < n; i++)
- if(a[i] != byte NUL)
- break;
- return i;
-}
-
-# return the data in the ecp block in a[0:Ecplen] (return nil for bad format)
-
-decode(a: array of byte): array of byte
-{
- if(debug['M']>0)
- sys->print("DECODE: %s\n", dump(a, Ecplen));
- badpar := 0;
- oldcrc := int a[Ecplen-2];
- crc := 0;
- op := 0;
- dle := 0;
- for(i:=0; i<Ecplen-2; i++){ # first byte is high-order byte of polynomial (MSB first)
- c := int a[i];
- nc := c & 16r7F; # strip parity
- if((c^int partab[nc]) & 16r80)
- badpar++;
- crc = crctab[crc ^ c];
- # collapse DLE sequences
- if(!dle){
- if(nc == DLE && i+1 < Ecplen-2){
- dle = 1;
- continue;
- }
- if(nc == NUL)
- continue; # strip non-escaped NULs
- }
- dle = 0;
- a[op++] = byte nc;
- }
- if(badpar){
- if(debug['E'] > 0)
- sys->print("bad parity\n");
- return nil;
- }
- crc = (crc>>1)&16r7F;
- if(int partab[crc] != oldcrc){
- if(debug['E'] > 0)
- sys->print("bad crc: in %ux got %ux\n", oldcrc, int partab[crc]);
- return nil;
- }
- b := array[op] of byte;
- b[0:] = a[0:op];
- if(debug['M'] > 0)
- sys->print("OUT: %s [%x :: %x]\n", dump(b,op), crc, oldcrc);
- return b;
-}
-
-Modem.reader(m: self ref Modem, pidc: chan of int)
-{
- pidc <-= sys->pctl(0, nil);
- if(crctab == nil)
- mktabs();
- a := array[Sys->ATOMICIO] of byte;
- inbuf := 0;
- while(m.fd != nil) {
- while((n := read(m.fd, a[inbuf:], len a-inbuf)) > 0){
- n += inbuf;
- inbuf = 0;
- if((m.spec & Ecp) == 0){
- b := array[n] of byte;
- for(i := 0; i<n; i++)
- b[i] = byte (int a[i] & 16r7F); # strip parity
- m.rd <-= b;
- }else{
- #sys->print("IN: %s\n", dump(a,n));
- i := 0;
- if(m.waitsyn){
- sys->print("seeking SYN #%x\n", m.seq);
- syn := byte (SYN | 16r80);
- lim := n-3;
- for(; i <= lim; i++)
- if(a[i] == syn && a[i+1] == syn && (int a[i+2]&16r0F) == m.seq){
- i += 3;
- m.waitsyn = 0;
- sys->print("found SYN #%x@%d\n", m.seq, i-3);
- break;
- }
- }
- lim := n-Ecplen;
- for(; (i = nextblock(a, i, n)) <= lim; i += Ecplen){
- b := decode(a[i:]);
- if(m.errforce || b == nil){
- m.errforce = 0;
- b = array[2] of byte;
- b[0] = byte NACK;
- b[1] = byte (m.seq | 16r40);
- sys->print("NACK #%x\n", m.seq);
- m.write(b);
- m.waitsyn = 1;
- i = n; # discard rest of block
- break;
- }
- m.seq = (m.seq+1) & 16rF; # mod 16 counter
- m.rd <-= b;
- }
- if(i < n){
- a[0:] = a[i:n];
- inbuf = n-i;
- }
- }
- }
- if(n <= 0)
- break;
- }
-# m.fd = nil;
- m.rd <-= nil;
-}
-
-playfd: ref Sys->FD;
-in_code, in_char: con iota;
-
-replay(m: ref Modem)
-{
- buf := array[8192] of byte;
- DMAX: con 10;
- d := 0;
- da := array[DMAX] of byte;
- playfd = nil;
- if(playfd == nil)
- playfd = sys->open("minitel.txt", Sys->OREAD);
- if(playfd == nil)
- return;
- nl := 1;
- discard := 1;
- state := in_code;
- hs := "";
- start := 0;
-mainloop:
- for(;;) {
- n := sys->read(playfd, buf, len buf);
- if(n <= 0)
- break;
- for(i:=0; i<n; i++) {
- ch := int buf[i];
- if(nl)
- case ch {
- '>' => discard = 0;
- '<' => discard = 1;
- if(start)
- sys->sleep(1000);
- '{' => start = 1;
- '}' => break mainloop;
- }
- if(ch == '\n')
- nl = 1;
- else
- nl = 0;
- if(discard)
- continue;
- if(!start)
- continue;
- if(state == in_code && ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'z')))
- hs[len hs] = ch;
- else if(ch == '(') {
- state = in_char;
- (v, nil) := toint(hs, 16);
- da[d++] = byte v;
- if(d == DMAX) {
- send(ref Event.Edata(m.m.path, Mmodem, da));
- d = 0;
- da = array[DMAX] of byte;
- sys->sleep(50);
- }
- hs = "";
- }else if(ch == ')')
- state = in_code;
- }
- }
- playfd = nil;
-
-}
-
-kill(pid : int)
-{
- prog := "#p/" + string pid + "/ctl";
- fd := sys->open(prog, Sys->OWRITE);
- if (fd != nil) {
- cmd := array of byte "kill";
- sys->write(fd, cmd, len cmd);
- }
-}
-
-
-# Modem stuff
-
-
-# modem return codes
-Ok, Success, Failure, Noise, Found: con iota;
-
-#
-# modem return messages
-#
-Msg: adt {
- text: string;
- trans: string;
- code: int;
-};
-
-msgs: array of Msg = array [] of {
- ("OK", "Ok", Ok),
- ("NO CARRIER", "No carrier", Failure),
- ("ERROR", "Bad modem command", Failure),
- ("NO DIALTONE", "No dial tone", Failure),
- ("BUSY", "Busy tone", Failure),
- ("NO ANSWER", "No answer", Failure),
- ("CONNECT", "", Success),
-};
-
-msend(m: ref Modem, x: string): int
-{
- a := array of byte x;
- return sys->write(m.fd, a, len a);
-}
-
-#
-# apply a string of commands to modem
-#
-apply(m: ref Modem, s: string): int
-{
- buf := "";
- for(i := 0; i < len s; i++){
- c := s[i];
- buf[len buf] = c; # assume no Unicode
- if(c == '\r' || i == (len s -1)){
- if(c != '\r')
- buf[len buf] = '\r';
- if(msend(m, buf) < 0)
- return Failure;
- buf = "";
- }
- }
- return Ok;
-}
-
-openmodem(m: ref Modem, dev: string): int
-{
- m.fd = sys->open(dev, Sys->ORDWR);
- m.cfd = sys->open(dev+"ctl", Sys->ORDWR);
- if(m.fd == nil || m.cfd == nil)
- return -1;
-# hangup(m);
-# m.fd = sys->open(dev, Sys->ORDWR);
-# m.cfd = sys->open(dev+"ctl", Sys->ORDWR);
-# if(m.fd == nil || m.cfd == nil)
-# return -1;
- return 0;
-}
-
-hangup(m: ref Modem)
-{
- sys->sleep(1020);
- msend(m, "+++");
- sys->sleep(1020);
- apply(m, "ATH0");
- m.fd = nil;
-# sys->write(m.cfd, array of byte "f", 1);
- sys->write(m.cfd, array of byte "h", 1);
- m.cfd = nil;
- # HACK: shannon softmodem "off-hook" bug fix
- sys->open("/dev/modem", Sys->OWRITE);
-}
-
-nethangup(m: ref Modem)
-{
- m.fd = nil;
- sys->write(m.cfd, array of byte "hangup", 6);
- m.cfd = nil;
-}
-
-
-#
-# check `s' for a known reply or `substr'
-#
-seenreply(s: string): (int, string)
-{
- for(k := 0; k < len msgs; k++)
- if(len s >= len msgs[k].text && s[0:len msgs[k].text] == msgs[k].text) {
- return (msgs[k].code, msgs[k].trans);
- }
- return (Noise, s);
-}
-
-contains(s, t: string): int
-{
- if(t == nil)
- return 1;
- if(s == nil)
- return 0;
- n := len t;
- for(i := 0; i+n <= len s; i++)
- if(s[i:i+n] == t)
- return 1;
- return 0;
-}
-
-dialout(m: ref Modem)
-{
- if(m.initstr != nil)
- apply(m, "AT"+m.initstr);
- if(m.dialstr != nil) {
- apply(m, "ATD"+m.dialstr);
- m.lastdialstr = m.dialstr;
- m.dialstr = nil;
- }
-}