The module is used by creating a channel and passing it to the module's init function, which returns a success/error indicator and starts an asynchronous process to read the remote control. The user of the module executes a receive on the channel whenever it wishes to accept a button-push.
The (abridged) module declaration is
Ir : module
{
# Code buttons on IR remote control
Zero : con 0;
One : con 1;
. . . Mute : con 23;
Error : con 9999;
init : fn(c : chan of int): int;
PATH : con "/dis/ir.dis";
SIMPATH : con "/dis/irsim.dis";
};
The implementation for the real remote control is:
implement Ir;
include "ir.m";
include "sys.m";
FD, Dir : import Sys;
sys : Sys;
init(keys : chan of int) : int
{
cfd, dfd: ref FD;
sys = load Sys Sys->PATH;
cfd = sys->open("/dev/eia1ctl", sys->OWRITE);
if(cfd == nil)
return -1;
sys->fprint(cfd, "b9600");
dfd = sys->open("/dev/eia1", sys->OREAD);
cfd = nil;
spawn reader(keys, dfd);
return 0;
}
The init routine accepts a chan argument; it will be used by the module to send codes for the buttons pressed by the user. In this routine, the calls to sys->open and sys->fprint open and set up the device data and control files /dev/eia1 and /dev/eia1ctl used to communicate with the device itself. The important step is at the end: the spawn statement creates a new, asynchronous task to read the device, using a routine that is passed the communications channel and the FD for the device:
reader(keys: chan of int, dfd: ref FD)
{
n, ta, tb: int;
dir: Dir;
b1:= array[1] of byte;
b2:= array[1] of byte;
# find the number of bytes already
# queued and flush that many
(n, dir) = sys->fstat(dfd);
if(n >= 0 && dir.length > 0) {
while(dir.length) {
n = sys->read(dfd,
array[dir.length] of byte,
dir.length);
if(n < 0)
break;
dir.length -= n;
}
}
pair:
for(;;) {
n = sys->read(dfd, b1, len b1);
if(n <= 0)
break;
ta = sys->millisec();
# Button pushes are pairs of characters
# that arrive closer together than
# 200 ms. Longer than that is likely
# to be noise.
for(;;) {
n = sys->read(dfd, b2, 1);
if (n <= 0)
break pair;
tb = sys->millisec();
if (tb - ta <= 200)
break;
ta = tb;
b1[0] = b2[0];
}
# map the character pair; the significant
# bits are the lowest 5.
case ((int b1[0]&16r1f)<<5) | (int b2[0]&16r1f) {
975 => n = Ir->Zero;
479 => n = Ir->One;
. . .
791 => n = Ir->Mute;
* => n = Ir->Error;
}
# found a button-push; send the value
keys <-= n;
}
keys <-= Ir->Error;
}
The code in the middle is related to noise-filtering and is uninteresting in detail except as it illustrates some of the methods provided by the Sys module; the crucial actions are found at the bottom, where the routine sends either a true button-push or an error code over the channel to the module's client.