diff options
Diffstat (limited to 'appl/cmd/palm/palmsrv.b')
| -rw-r--r-- | appl/cmd/palm/palmsrv.b | 901 |
1 files changed, 901 insertions, 0 deletions
diff --git a/appl/cmd/palm/palmsrv.b b/appl/cmd/palm/palmsrv.b new file mode 100644 index 00000000..f878be03 --- /dev/null +++ b/appl/cmd/palm/palmsrv.b @@ -0,0 +1,901 @@ +implement Palmsrv; + +# +# serve up a Palm using SLP and PADP +# +# Copyright © 2003 Vita Nuova Holdings Limited. All rights reserved. +# +# forsyth@vitanuova.com +# +# TO DO +# USB and possibly other transports +# tickle + +include "sys.m"; + sys: Sys; + +include "draw.m"; + +include "timers.m"; + timers: Timers; + Timer, Sec: import timers; + +include "palm.m"; + +include "arg.m"; + +Palmsrv: module +{ + init: fn(nil: ref Draw->Context, nil: list of string); +}; + +debug := 0; + +usage() +{ + sys->fprint(sys->fildes(2), "usage: palm/palmsrv [-d /dev/eia0] [-s 57600]\n"); + raise "fail:usage"; +} + +init(nil: ref Draw->Context, args: list of string) +{ + sys = load Sys Sys->PATH; + sys->pctl(Sys->NEWPGRP|Sys->FORKFD, nil); + + device, speed: string; + + arg := load Arg Arg->PATH; + if(arg == nil) + error(sys->sprint("can't load %s: %r", Arg->PATH)); + arg->init(args); + while((c := arg->opt()) != 0) + case c { + 'D' => + debug++; + 'd' => + device = arg->arg(); + 's' => + speed = arg->arg(); + * => + usage(); + } + args = arg->argv(); + arg = nil; + + if(device == nil) + device = "/dev/eia0"; + if(speed == nil) + speed = "57600"; + + dfd := sys->open(device, Sys->ORDWR); + if(dfd == nil) + error(sys->sprint("can't open %s: %r", device)); + cfd := sys->open(device+"ctl", Sys->OWRITE); + + timers = load Timers Timers->PATH; + if(timers == nil) + error(sys->sprint("can't load %s: %r", Timers->PATH)); + srvio := sys->file2chan("/chan", "palmsrv"); + if(srvio == nil) + error(sys->sprint("can't create channel /chan/palmsrv: %r")); + timers->init(Sec/100); + p := Pchan.init(dfd, cfd); + spawn server(srvio, p); +} + +error(s: string) +{ + sys->fprint(sys->fildes(2), "palmsrv: %s\n", s); + raise "fail:error"; +} + +Xact: adt +{ + fid: int; + reply: array of byte; + error: string; +}; + +server(srv: ref Sys->FileIO, p: ref Pchan) +{ + actions: list of ref Xact; + nuser := 0; + for(;;)alt{ + (nil, nbytes, fid, rc) := <-srv.read => + if(rc == nil){ + actions = delact(actions, fid); + break; + } + act := findact(actions, fid); + if(act == nil){ + rc <-= (nil, "no transaction in progress"); + break; + } + actions = delact(actions, fid); + if(p.shutdown) + rc <-= (nil, "link shut down"); + else if(act.error != nil) + rc <-= (nil, act.error); + else if(act.reply != nil) + rc <-= (act.reply, nil); + else + rc <-= (nil, "no reply"); # probably shouldn't happen + + (nil, data, fid, wc) := <-srv.write => + actions = delact(actions, fid); # discard result of any previous transaction + if(wc == nil){ + if(--nuser <= 0){ + nuser = 0; + p.stop(); + } + break; + } + if(len data == 4 && string data == "exit"){ + p.close(); + wc <-= (len data, nil); + exit; + } + if(p.shutdown){ + wc <-= (0, "link shut down"); # must close then reopen + break; + } + if(!p.started){ + err := p.start(); + if(err != nil){ + wc <-= (0, sys->sprint("can't start protocol: %s", err)); + break; + } + nuser++; + } + (result, err) := p.padp_xchg(data, 20*1000); + if(err != nil){ + wc <-= (0, err); + break; + } + actions = ref Xact(fid, result, err) :: actions; + wc <-= (len data, nil); + } +} + +findact(l: list of ref Xact, fid: int): ref Xact +{ + for(; l != nil; l = tl l) + if((a := hd l).fid == fid) + return a; + return nil; +} + +delact(l: list of ref Xact, fid: int): list of ref Xact +{ + ol := l; + l = nil; + for(; ol != nil; ol = tl ol) + if((a := hd ol).fid != fid) + l = a :: l; + return l; +} + +killpid(pid: int) +{ + if(pid != 0){ + fd := sys->open("/prog/"+string pid+"/ctl", sys->OWRITE); + if(fd != nil) + sys->fprint(fd, "kill"); + } +} + +# +# protocol implementation +# Serial Link Protocol (framing) +# Connection Management Protocol (wakeup, negotiation) +# Packet Assembly/Disassembly Protocol (reliable delivery fragmented datagram) +# + +DATALIM: con 1024; + +# SLP packet types +SLP_System, SLP_Unused, SLP_PAD, SLP_Loop: con iota; + +# SLP block content, without framing +Sblock: adt { + src: int; # socket ID + dst: int; # socket ID + proto: int; # packet type + xid: int; # transaction ID + data: array of byte; + + new: fn(): ref Sblock; + print: fn(sb: self ref Sblock, dir: string); +}; + +# +# Palm channel +# +Pchan: adt { + started: int; + shutdown: int; + + protocol: int; + lport: byte; + rport: byte; + + fd: ref Sys->FD; + cfd: ref Sys->FD; + baud: int; + + rpid: int; + lastid: int; + rd: chan of ref Sblock; + reply: ref Sblock; # data replacing lost ack + + init: fn(dfd: ref Sys->FD, cfd: ref Sys->FD): ref Pchan; + start: fn(p: self ref Pchan): string; + stop: fn(p: self ref Pchan); + close: fn(p: self ref Pchan): int; + slp_read: fn(p: self ref Pchan, nil: int): (ref Sblock, string); + slp_write: fn(p: self ref Pchan, xid: int, nil: array of byte): string; + + setbaud: fn(p: self ref Pchan, nil: int); + + padp_read: fn(p: self ref Pchan, xid: int, timeout: int): (array of byte, string); + padp_write: fn(p: self ref Pchan, msg: array of byte, xid: int): string; + padp_xchg: fn(p: self ref Pchan, msg: array of byte, timeout: int): (array of byte, string); + tickle: fn(p: self ref Pchan); + + connect: fn(p: self ref Pchan): string; + accept: fn(p: self ref Pchan, baud: int): string; + + nextseq: fn(p: self ref Pchan): int; +}; + +Pchan.init(dfd: ref Sys->FD, cfd: ref Sys->FD): ref Pchan +{ + p := ref Pchan; + p.fd = dfd; + p.cfd = cfd; + p.baud = InitBaud; + p.protocol = SLP_PAD; + p.rport = byte 3; + p.lport = byte 3; + p.rd = chan of ref Sblock; + p.lastid = 0; + p.rpid = 0; + p.started = 0; + p.shutdown = 0; + return p; +} + +Pchan.start(p: self ref Pchan): string +{ + if(p.started) + return nil; + p.shutdown = 0; + p.baud = InitBaud; + p.reply = nil; + ctl(p, "f"); + ctl(p, "d1"); + ctl(p, "r1"); + ctl(p, "i8"); + ctl(p, "q8192"); + ctl(p, sys->sprint("b%d", InitBaud)); + pidc := chan of int; + spawn slp_recv(p, pidc); + p.started = 1; + p.rpid = <-pidc; + err := p.accept(57600); + if(err != nil) + p.stop(); + return err; +} + +ctl(p: ref Pchan, s: string) +{ + if(p.cfd != nil) + sys->fprint(p.cfd, "%s", s); +} + +Pchan.setbaud(p: self ref Pchan, baud: int) +{ + if(p.baud != baud){ + p.baud = baud; + ctl(p, sys->sprint("b%d", baud)); + sys->sleep(200); + } +} + +Pchan.stop(p: self ref Pchan) +{ + p.shutdown = 0; + if(!p.started) + return; + killpid(p.rpid); + p.rpid = 0; + p.reply = nil; +# ctl(p, "f"); +# ctl(p, "d0"); +# ctl(p, "r0"); +# ctl(p, sys->sprint("b%d", InitBaud)); + p.started = 0; +} + +Pchan.close(p: self ref Pchan): int +{ + if(p.started) + p.stop(); + p.reply = nil; + p.cfd = nil; + p.fd = nil; + timers->shutdown(); + return 0; +} + +# CMP protocol for connection management +# See include/Core/System/CMCommon.h, Palm SDK +# There are two major versions: the original V1, still always used in wakeup messsages; +# and V2, which is completely different (similar structure to Desklink) and used by newer devices, but the headers +# are the same length. Start off in V1 announcing version 2.x, then switch to that. +# My device supports only V1, so I use that. + +CMPHDRLEN: con 10; # V1: type[1] flags[1] vermajor[1] verminor[1] mbz[2] baud[4] + # V2: type[1] cmd[1] error[2] argc[1] mbz[1] mbz[4] + +# CMP V1 +Cmajor: con 1; +Cminor: con 2; + +InitBaud: con 9600; + +# type +Cwake, Cinit, Cabort, Cextended: con 1+iota; + +# Cinit flags +ChangeBaud: con 16r80; +RcvTimeout1: con 16r40; # tell Palm to set receive timeout to 1 minute (CMP v1.1) +RcvTimeout2: con 16r20; # tell Palm to set receive timeout to 2 minutes (v1.1) + +# Cinit and Cwake flag +LongPacketEnable: con 16r10; # enable long packet support (v1.2) + +# Cabort flags +WrongVersion: con 16r80; # incompatible com versions + +# CMP V2 +Carg1: con Palm->ArgIDbase; +Cresponse: con 16r80; +Cxchgprefs, Chandshake: con 16r10+iota; + +Pchan.connect(p: self ref Pchan): string +{ + (nil, e1) := cmp_write(p, Cwake, 0, Cmajor, Cminor, 57600); + if(e1 != nil) + return e1; + (op, flag, nil, nil, baud, e2) := cmp_read(p, 0); + if(e2 != nil) + return e2; + case op { + Cinit=> + if(flag & ChangeBaud) + p.setbaud(baud); + return nil; + + Cabort=> + return "Palm rejected connect"; + + * => + return sys->sprint("Palm connect: reply %d", op); + } + return nil; +} + +Pchan.accept(p: self ref Pchan, maxbaud: int): string +{ + (op, nil, major, minor, baud, err) := cmp_read(p, 0); + if(err != nil) + return err; + if(major != 1){ + sys->fprint(sys->fildes(2), "palmsrv: comm version mismatch: %d.%d\n", major, minor); + cmp_write(p, Cabort, WrongVersion, Cmajor, 0, 0); + return sys->sprint("comm version mismatch: %d.%d", major, minor); + } + if(baud > maxbaud) + baud = maxbaud; + flag := 0; + if(baud != InitBaud) + flag = ChangeBaud; + (nil, err) = cmp_write(p, Cinit, flag, Cmajor, Cminor, baud); + if(err != nil) + return err; + p.setbaud(baud); + return nil; +} + +cmp_write(p: ref Pchan, op: int, flag: int, major: int, minor: int, baud: int): (int, string) +{ + cmpbuf := array[CMPHDRLEN] of byte; + cmpbuf[0] = byte op; + cmpbuf[1] = byte flag; + cmpbuf[2] = byte major; + cmpbuf[3] = byte minor; + cmpbuf[4] = byte 0; + cmpbuf[5] = byte 0; + put4(cmpbuf[6:], baud); + + if(op == Cwake) + return (16rFF, p.padp_write(cmpbuf, 16rFF)); + xid := p.nextseq(); + return (xid, p.padp_write(cmpbuf, xid)); +} + +cmp_read(p: ref Pchan, xid: int): (int, int, int, int, int, string) +{ + (c, err) := p.padp_read(xid, 20*Sec); + if(err != nil) + return (0, 0, 0, 0, 0, err); + if(len c != CMPHDRLEN) + return (0, 0, 0, 0, 0, "CMP: bad response"); + return (int c[0], int c[1], int c[2], int c[3], get4(c[6:]), nil); +} + +# +# Palm PADP protocol +# ``The Packet Assembly/Disassembly Protocol'' in +# Developing Palm OS Communications, US Robotics, 1996, pp. 53-68. +# +# forsyth@caldo.demon.co.uk, 1997 +# + +FIRST: con 16r80; +LAST: con 16r40; +MEMERROR: con 16r20; + +# packet types +Pdata: con 1; +Pack: con 2; +Ptickle: con 4; +Pabort: con 8; + +PADPHDRLEN: con 4; # type[1] flags[1] size[2] + +RetryInterval: con 4*Sec; +MaxRetries: con 14; # they say 14 `seconds', but later state they might need 20 for heap mgmt, so i'll assume 14 attempts (at 4sec ea) + +Pchan.padp_xchg(p: self ref Pchan, msg: array of byte, timeout: int): (array of byte, string) +{ + xid := p.nextseq(); + err := p.padp_write(msg, xid); + if(err != nil) + return (nil, err); + return p.padp_read(xid, timeout); +} + +# +# PADP header +# type[1] flags[2] size[2], high byte first for size +# +# max block size is 2^16-1 +# must ack within 2 seconds +# wait at most 10 seconds for next chunk +# 10 retries +# + +Pchan.padp_write(p: self ref Pchan, buf: array of byte, xid: int): string +{ + count := len buf; + if(count >= 1<<16) + return "padp: write too big"; + p.reply = nil; + flags := FIRST; + mem := buf[0:]; + offset := 0; + while(count > 0){ + n := count; + if(n > DATALIM) + n = DATALIM; + else + flags |= LAST; + ob := array[PADPHDRLEN+n] of byte; + ob[0] = byte Pdata; + ob[1] = byte flags; + l: int; + if(flags & FIRST) + l = count; # total size in first segment + else + l = offset; # offset in rest + put2(ob[2:], l); + ob[PADPHDRLEN:] = mem[0:n]; + if(debug) + padp_dump(ob, "Tx"); + p.slp_write(xid, ob); + retries := 0; + for(;;){ + (ib, nil) := p.slp_read(RetryInterval); + if(ib == nil){ + sys->print("padp write: ack timeout\n"); + retries++; + if(retries > MaxRetries){ + # USR says not to give up if (flags&LAST)!=0; giving up seems safer + sys->print("padp write: give up\n"); + return "PADP: no response"; + } + p.slp_write(xid, ob); + continue; + } + if(ib.proto != SLP_PAD || len ib.data < PADPHDRLEN || ib.xid != xid && ib.xid != 16rFF){ + sys->print("padp write: ack wrong type(%d) or xid(%d,%d), or len %d\n", ib.proto, ib.xid, xid, len ib.data); + continue; + } + if(ib.xid == 16rFF){ # connection management + if(int ib.data[0] == Ptickle) + continue; + if(int ib.data[0] == Pabort){ + sys->print("padp write: device abort\n"); + p.shutdown = 1; + return "device cancelled operation"; + } + } + if(int ib.data[0] != Pack){ + if(int ib.data[0] == Ptickle) + continue; + # right transaction ... if it's acceptable data, USR says to save it & treat as ack + sys->print("padp write: type %d, not ack\n", int ib.data[0]); + if(int ib.data[0] == Pdata && flags & LAST && int ib.data[1] & FIRST){ + p.reply = ib; + break; + } + continue; + } + if(int ib.data[1] & MEMERROR) + return "padp: pilot out of memory"; + if((flags&(FIRST|LAST)) != (int ib.data[1]&(FIRST|LAST)) || + get2(ib.data[2:]) != get2(ob[2:])){ + sys->print("padp write: ack, wrong flags (#%x,#%x) or offset (%d,%d)\n", int ib.data[1], flags, get2(ib.data[2:]), get2(ob[2:])); + continue; + } + if(debug) + sys->print("padp write: ack %d %d\n", xid, get2(ob[2:])); + break; + } + mem = mem[n:]; + count -= n; + offset += n; + flags &= ~FIRST; + } + return nil; +} + +Pchan.padp_read(p: self ref Pchan, xid, timeout: int): (array of byte, string) +{ + buf, mem: array of byte; + + offset := 0; + ready := 0; + retries := 0; + ack := array[PADPHDRLEN] of byte; + for(;;){ + b := p.reply; + if(b == nil){ + err: string; + (b, err) = p.slp_read(timeout); + if(b == nil){ + sys->print("padp read: timeout %d\n", retries); + if(++retries <= 5) + continue; + sys->print("padp read: gave up\n"); + return (nil, err); + } + retries = 0; + } else + p.reply = nil; + if(debug) + padp_dump(b.data, "Rx"); + if(len b.data < PADPHDRLEN){ + sys->print("padp read: length\n"); + continue; + } + if(b.proto != SLP_PAD){ + sys->print("padp read: bad proto (%d)\n", b.proto); + continue; + } + if(int b.data[0] == Pabort && b.xid == 16rFF){ + p.shutdown = 1; + return (nil, "device cancelled transaction"); + } + if(int b.data[0] != Pdata || xid != 0 && b.xid != xid){ + sys->print("padp read mismatch: type (%d) or xid(%d::%d)\n", int b.data[0], b.xid, xid); + continue; + } + f := int b.data[1]; + o := get2(b.data[2:]); + if(f & FIRST){ + buf = array[o] of byte; + ready = 1; + offset = 0; + o = 0; + mem = buf; + timeout = 4*Sec; + } + if(!ready || o != offset){ + sys->print("padp read: offset %d, expected %d\n", o, offset); + continue; + } + n := len b.data - PADPHDRLEN; + if(n > len mem){ + sys->print("padp read: record too long (%d/%d)\n", n, len mem); + # it's probably fatal, but retrying does no harm + continue; + } + mem[0:] = b.data[PADPHDRLEN:PADPHDRLEN+n]; + mem = mem[n:]; + offset += n; + ack[0:] = b.data[0:PADPHDRLEN]; + ack[0] = byte Pack; + p.slp_write(xid, ack); + if(f & LAST) + break; + } + if(offset != len buf) + return (buf[0:offset], nil); + return (buf, nil); +} + +Pchan.nextseq(p: self ref Pchan): int +{ + n := p.lastid + 1; + if(n >= 16rFF) + n = 1; + p.lastid = n; + return n; +} + +Pchan.tickle(p: self ref Pchan) +{ + xid := p.nextseq(); + data := array[PADPHDRLEN] of byte; + data[0] = byte Ptickle; + data[1] = byte (FIRST|LAST); + put2(data[2:], 0); + if(debug) + sys->print("PADP: tickle\n"); + p.slp_write(xid, data); +} + +padp_dump(data: array of byte, dir: string) +{ + stype: string; + + case int data[0] { + Pdata => stype = "Data"; + Pack => stype = "Ack"; + Ptickle => stype = "Tickle"; + Pabort => stype = "Abort"; + * => stype = sys->sprint("#%x", int data[0]); + } + + sys->print("PADP %s %s flags=#%x len=%d\n", stype, dir, int data[1], get2(data[2:])); + + if(debug > 1 && (data[0] != byte Pack || len data > 4)){ + data = data[4:]; + for(i := 0; i < len data;){ + sys->print(" %.2x", int data[i]); + if(++i%16 == 0) + sys->print("\n"); + } + sys->print("\n"); + } +} + +# +# Palm's Serial Link Protocol +# See include/Core/System/SerialLinkMgr.h in Palm SDK +# and the description in the USR document mentioned above. +# + +SLPHDRLEN: con 10; # BE[1] EF[1] ED[1] dest[1] src[1] type[1] size[2] xid[1] check[1] body[size] crc[2] +SLP_MTU: con SLPHDRLEN+PADPHDRLEN+DATALIM; + +Sblock.new(): ref Sblock +{ + return ref Sblock(0, 0, 0, 16rFF, nil); +} + +# +# format and write an SLP frame +# +Pchan.slp_write(p: self ref Pchan, xid: int, b: array of byte): string +{ + d := array[SLPHDRLEN] of byte; + cb := array[2] of byte; + + nb := len b; + d[0] = byte 16rBE; + d[1] = byte 16rEF; + d[2] = byte 16rED; + d[3] = byte p.rport; + d[4] = byte p.lport; + d[5] = byte p.protocol; + d[6] = byte (nb >> 8); + d[7] = byte (nb & 16rFF); + d[8] = byte xid; + d[9] = byte 0; + n := 0; + for(i:=0; i<len d; i++) + n += int d[i]; + d[9] = byte (n & 16rFF); + if(debug) + printbytes(d, "SLP Tx hdr"); + crc := crc16(d, 0); + put2(cb, crc16(b, crc)); + + if(sys->write(p.fd, d, SLPHDRLEN) != SLPHDRLEN || + sys->write(p.fd, b, nb) != len b || + sys->write(p.fd, cb, 2) != 2) + return sys->sprint("%r"); + return nil; +} + +Pchan.slp_read(p: self ref Pchan, timeout: int): (ref Sblock, string) +{ + clock := Timer.start(timeout); + alt { + <-clock.timeout => + if(debug) + sys->print("SLP: timeout\n"); + return (nil, "SLP: timeout"); + b := <-p.rd => + clock.stop(); + return (b, nil); + } +} + +slp_recv(p: ref Pchan, pidc: chan of int) +{ + n: int; + + pidc <-= sys->pctl(0, nil); + buf := array[2*SLP_MTU] of byte; + sb := Sblock.new(); + rd := wr := 0; +Work: + for(;;){ + + if(wr != rd){ + # data already in buffer might start a new frame + if(rd != 0){ + buf[0:] = buf[rd:wr]; + wr -= rd; + rd = 0; + } + }else + rd = wr = 0; + + # header + while(wr < SLPHDRLEN){ + n = sys->read(p.fd, buf[wr:], SLPHDRLEN-wr); + if(n <= 0) + break Work; + wr += n; + } +# {for(i:=0; i<wr;i++)sys->print("%.2x", int buf[i]);sys->print("\n");} + if(buf[0] != byte 16rBE || buf[1] != byte 16rEF || buf[2] != byte 16rED){ + rd++; + continue; + } + if(debug) + printbytes(buf[0:wr], "SLP Rx hdr"); + n = 0; + for(i:=0; i<SLPHDRLEN-1; i++) + n += int buf[i]; + if((n & 16rFF) != int buf[9]){ + rd += 3; + continue; + } + hdr := buf[0:SLPHDRLEN]; + sb.dst = int hdr[3]; + sb.src = int hdr[4]; + sb.proto = int hdr[5]; + size := (int hdr[6]<<8) | int hdr[7]; + sb.xid = int hdr[8]; + sb.data = array[size] of byte; + crc := crc16(hdr, 0); + rd += SLPHDRLEN; + if(rd == wr) + rd = wr = 0; + + # data and CRC + while(wr-rd < size+2){ + n = sys->read(p.fd, buf[wr:], size+2-(wr-rd)); + if(n <= 0) + break Work; + wr += n; + } + crc = crc16(buf[rd:rd+size], crc); + if(crc != get2(buf[rd+size:])){ + if(debug) + sys->print("CRC error: local=#%.4ux pilot=#%.4ux\n", crc, get2(buf[rd+size:])); + for(; rd < wr && buf[rd] != byte 16rBE; rd++) + ; # hunt for next header + continue; + } + if(sb.proto != SLP_Loop){ + sb.data[0:] = buf[rd:rd+size]; + if(debug) + sb.print("Rx"); + rd += size+2; + p.rd <-= sb; + sb = Sblock.new(); + } else { + # should we reflect these? + if(debug) + sb.print("Loop"); + rd += size+2; + } + } + p.rd <-= nil; +} + +Sblock.print(b: self ref Sblock, dir: string) +{ + sys->print("SLP %s %d->%d len=%d proto=%d xid=#%.2x\n", + dir, int b.src, int b.dst, len b.data, int b.proto, int b.xid); +} + +printbytes(d: array of byte, what: string) +{ + buf := sys->sprint("%s[", what); + for(i:=0; i<len d; i++) + buf += sys->sprint(" #%.2x", int d[i]); + buf += "]"; + sys->print("%s\n", buf); +} + +get4(p: array of byte): int +{ + return (int p[0]<<24) | (int p[1]<<16) | (int p[2]<<8) | int p[3]; +} + +get3(p: array of byte): int +{ + return (int p[1]<<16) | (int p[2]<<8) | int p[3]; +} + +get2(p: array of byte): int +{ + return (int p[0]<<8) | int p[1]; +} + +put4(p: array of byte, v: int) +{ + p[0] = byte (v>>24); + p[1] = byte (v>>16); + p[2] = byte (v>>8); + p[3] = byte (v & 16rFF); +} + +put3(p: array of byte, v: int) +{ + p[0] = byte (v>>16); + p[1] = byte (v>>8); + p[2] = byte (v & 16rFF); +} + +put2(p: array of byte, v: int) +{ + p[0] = byte (v>>8); + p[1] = byte (v & 16rFF); +} + +# this will be done by table look up; +# polynomial is xⁱ⁶+xⁱ+x⁵+1 + +crc16(buf: array of byte, crc: int): int +{ + for(j := 0; j < len buf; j++){ + crc = crc ^ (int buf[j]) << 8; + for(i := 0; i < 8; i++) + if(crc & 16r8000) + crc = (crc << 1) ^ 16r1021; + else + crc = crc << 1; + } + return crc & 16rffff; +} |
