forsyth20121205-174063a80cc13 years agoHistory
469 lines · 8.7 KB · Brainfuck
RawDownload
implement Modem; ## Copyright © 1998-2001 Vita Nuova Holdings Limited.  All rights reserved.# include "sys.m";	sys: Sys; include "lock.m";	lock: Lock;	Semaphore: import lock; include "draw.m"; include "modem.m"; hangupcmd := "ATH0";		# was ATZH0 but some modem versions on Umec hung on ATZ # modem return codesOk, Success, Failure, Abort, Noise, Found: con iota; maxspeed: con 115200; ##  modem return messages#Msg: adt {	text: 		string;	code: 		int;}; msgs: array of Msg = array [] of {	("OK", 			Ok),	("NO CARRIER", 	Failure),	("ERROR", 		Failure),	("NO DIALTONE", Failure),	("BUSY", 		Failure),	("NO ANSWER", 	Failure),	("CONNECT", 	Success),}; kill(pid: int){	fd := sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE);	if(fd == nil || sys->fprint(fd, "kill") < 0)		sys->print("modem: can't kill %d: %r\n", pid);} ## prepare a modem port#openserial(d: ref Device): string{	d.data = nil;	d.ctl = nil; 	d.data = sys->open(d.local, Sys->ORDWR);	if(d.data == nil)		return sys->sprint("can't open %s: %r", d.local); 	d.ctl = sys->open(d.local+"ctl", Sys->ORDWR);	if(d.ctl == nil)		return sys->sprint("can't open %s: %r", d.local+"ctl"); 	d.speed = maxspeed;	d.avail = nil;	return nil;} ## shut down the monitor (if any) and return the connection# Device.close(m: self ref Device): ref Sys->Connection{	if(m.pid != 0){		kill(m.pid);		m.pid = 0;	}	if(m.data == nil)		return nil;	mc := ref sys->Connection(m.data, m.ctl, nil);	m.ctl = nil;	m.data = nil;	return mc;} ## Send a string to the modem# Device.send(d: self ref Device, x: string): string{	a := array of byte x;	f := sys->write(d.data, a, len a);	if(f != len a) {		# let's attempt to close & reopen the modem		d.close();		err := openserial(d);		if(err != nil)			return err;		f = sys->write(d.data,a, len a);		if(f < 0)			return sys->sprint("%r");		if(f != len a)			return "short write";	}	if(d.trace)		sys->print("->%s\n",x);	return nil;} ##  apply a string of commands to modem & look for a response# apply(d: ref Device, s: string, substr: string, secs: int): int{	m := Ok;	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(d.send(buf) != nil)				return Abort;			(m, nil) = readmsg(d, secs, substr);			buf = "";		}	}	return m;} ##  get modem into command mode if it isn't already#GUARDTIME: con 1100;	# usual default for S12=50 in units of 1/50 sec; allow 100ms fuzz attention(d: ref Device): int{	for(i := 0; i < 3; i++){		if(apply(d, hangupcmd, nil, 2) == Ok)			return Ok;		sys->sleep(GUARDTIME);		if(d.send("+++") != nil)			return Abort;		sys->sleep(GUARDTIME);		(nil, msg) := readmsg(d, 0, nil);		if(msg != nil && d.trace)			sys->print("status: %s\n", msg);	}	return Failure;} ##  apply a command type# applyspecial(d: ref Device, cmd: string): int{	if(cmd == nil)		return Failure;	return apply(d, cmd, nil, 2);} ##  hang up any connections in progress and close the device#Device.onhook(d: self ref Device){	# hang up the modem	monitoring(d);	if(attention(d) != Ok)		sys->print("modem: no attention\n"); 	# hangup the stream (eg, for ppp) and toggle the lines to the modem	if(d.ctl != nil) {		sys->fprint(d.ctl,"d0\n");		sys->fprint(d.ctl,"r0\n");		sys->fprint(d.ctl, "h\n");	# hangup on native serial 		sys->sleep(250);		sys->fprint(d.ctl,"r1\n");		sys->fprint(d.ctl,"d1\n");	} 	d.close();} ## does string s contain t anywhere?# 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;} ##  read till we see a message or we time out#readmsg(d: ref Device, secs: int, substr: string): (int, string){	found := 0;	msecs := secs*1000;	limit := 1000;		# pretty arbitrary	s := ""; 	for(start := sys->millisec(); sys->millisec() <= start+msecs;){		a := d.getinput(1);		if(len a == 0){			if(limit){				sys->sleep(1);				continue;			}			break;		}		if(a[0] == byte '\n' || a[0] == byte '\r' || limit == 0){			if (len s) {				if (s[(len s)-1] == '\r')					s[(len s)-1] = '\n';				sys->print("<-%s\n",s);			}			if(substr != nil && contains(s, substr))				found = 1;			for(k := 0; k < len msgs; k++)				if(len s >= len msgs[k].text &&				   s[0:len msgs[k].text] == msgs[k].text){					if(found)						return (Found, s);					return (msgs[k].code, s);				}			start = sys->millisec();			s = "";			continue;		}		s[len s] = int a[0];		limit--;	}	s = "no response from modem";	if(found)		return (Found, s); 	return (Noise, s);} ##  get baud rate from a connect message# getspeed(msg: string, speed: int): int{	p := msg[7:];	# skip "CONNECT"	while(p[0] == ' ' || p[0] == '\t')		p = p[1:];	s := int p;	if(s <= 0)		return speed;	else		return s;} ##  set speed and RTS/CTS modem flow control# setspeed(d: ref Device, baud: int){	if(d != nil && d.ctl != nil){		sys->fprint(d.ctl, "b%d", baud);		sys->fprint(d.ctl, "m1");	}} monitoring(d: ref Device){	# if no monitor then spawn one	if(d.pid == 0) {		pidc := chan of int;		spawn monitor(d, pidc, nil);		d.pid = <-pidc;	}} ##  a process to read input from a modem.#monitor(d: ref Device, pidc: chan of int, errc: chan of string){	err := openserial(d);	pidc <-= sys->pctl(0, nil);	if(err != nil && errc != nil)		errc <-= err;	a := array[Sys->ATOMICIO] of byte;	for(;;) {		d.lock.obtain();		d.status = "Idle";		d.remote = "";		setspeed(d, d.speed);		d.lock.release();		# shuttle bytes		while((n := sys->read(d.data, a, len a)) > 0){			d.lock.obtain();			if (len d.avail < Sys->ATOMICIO) {				na := array[len d.avail + n] of byte;				na[0:] = d.avail[0:];				na[len d.avail:] = a[0:n];				d.avail = na;			}							d.lock.release();		}		# on an error, try reopening the device		d.data = nil;		d.ctl = nil;		err = openserial(d);		if(err != nil && errc != nil)			errc <-= err;	}} ##  return up to n bytes read from the modem by monitor()#Device.getinput(d: self ref Device, n: int): array of byte{	if(d==nil || n <= 0)		return nil;	a: array of byte;	d.lock.obtain();	if(len d.avail != 0){		if(n > len d.avail)			n = len d.avail;		a = d.avail[0:n];		d.avail = d.avail[n:];	}	d.lock.release();	return a;} Device.getc(d: self ref Device, msec: int): int{	start := sys->millisec();	while((b  := d.getinput(1)) == nil) {		if (msec && sys->millisec() > start+msec)			return 0;		sys->sleep(1);	}	return int b[0];} init(): string{	sys = load Sys Sys->PATH;	lock = load Lock Lock->PATH;	if(lock == nil)		return sys->sprint("can't load %s: %r", Lock->PATH);	lock->init();	return nil;} Device.new(modeminfo: ref ModemInfo, trace: int): ref Device{	d := ref Device;	d.lock = Semaphore.new();	d.local = modeminfo.path;	d.pid = 0;	d.speed = 0;	d.t = *modeminfo;	if(d.t.hangup == nil)		d.t.hangup = hangupcmd;	d.trace = trace | 1;	# always trace for now	return d;} ##  dial a number#Device.dial(d: self ref Device, number: string): string{	monitoring(d); 	# modem type should already be established, but just in case	if(d.trace)		sys->print("modem: attention\n");	x := attention(d);	if (x != Ok && d.trace)		return "bad response from modem";	#	#  extended Hayes commands, meaning depends on modem	#	sys->print("modem: init\n");	if(d.t.country != nil)		applyspecial(d, d.t.country); 	if(d.t.init != nil)		applyspecial(d, d.t.init); 	if(d.t.other != nil)		applyspecial(d, d.t.other); 	applyspecial(d, d.t.errorcorrection); 	compress := Abort;	if(d.t.mnponly != nil)			compress = applyspecial(d, d.t.mnponly);	if(d.t.compression != nil)			compress = applyspecial(d, d.t.compression); 	rateadjust := Abort;	if(compress != Ok)		rateadjust = applyspecial(d, d.t.rateadjust);	applyspecial(d, d.t.flowctl); 	# finally, dialout	if(d.trace)		sys->print("modem: dial\n");	if((dt := d.t.dialtype) == nil)		dt = "ATDT";	err := d.send(sys->sprint("%s%s\r", dt, number));	if(err != nil){		if(d.trace)			sys->print("modem: can't dial %s: %s\n", number, err);		return err;	} 	(i, msg) := readmsg(d, 120, nil);	if(i != Success){		if(d.trace)			sys->print("modem: modem error reply: %s\n", msg);		return msg;	} 	connectspeed := getspeed(msg, d.speed); 	# change line rate if not compressing	if(rateadjust == Ok)		setspeed(d, connectspeed); 	if(d.ctl != nil){		if(d != nil)			sys->fprint(d.ctl, "s%d", connectspeed);	# set DCE speed (if device implements it)		sys->fprint(d.ctl, "c1");	# enable CD monitoring	} 	return nil;} dumpa(a: array of byte): string{	s := "";	for(i:=0; i<len a; i++){		b := int a[i];		if(b >= ' ' && b < 16r7f)			s[len s] = b;		else			s += sys->sprint("\\%.2x", b);	}	return s;}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t