34 KiB
Psion SIBO / Workabout MX — I/O Device Model and Drivers
Reference for the EPOC (SIBO) I/O system as documented in the Psion SIBO 'C' SDK. Everything here is drawn from two manuals:
- PLIB Reference — I/O System chapter (Ch. 9), Asynchronous Requests and Semaphores (Ch. 8), Memory Allocation (Ch. 7). Cited as PLIB.
- I/O Devices Reference v2.30 (March 1999). Cited as IODEV.
Where a fact is specific to the Workabout / Workabout MX, or is uncertain from the manuals, it is flagged. The MX integral laser scanner is not covered by these manuals; see the cross-reference in the Barcode section.
Notation used by the manuals: an I/O service is identified by a function code of the form
P_Fxxx(defined inp_file.h). The manuals writep_iow(P_FWRITE),p_ioc(P_FWRITE)or justP_FWRITEto refer to that service. (IODEV Ch.1; PLIB Ch.9.)
1. The device model
1.1 Device drivers: LDDs and PDDs
The purpose of a device driver is to hide the underlying hardware behind a stable software interface. EPOC defines two driver types (PLIB Ch.9, "LDDs and PDDs"):
- PDD — physical device driver: hardware dependent (the lower layer).
- LDD — logical device driver: hardware independent (the upper layer).
Applications normally interface to LDDs only. An LDD may use one or more PDDs.
The serial driver is the canonical example: an upper hardware-independent LDD over
a hardware-dependent PDD; incoming data is buffered at the LDD level. Some drivers
touch no hardware at all — e.g. the C floating-point library is implemented as a
device driver (the 8087 emulator LDD sys$8087.ldd). (PLIB Ch.9; Ch.5.)
Some drivers are built into the ROM (e.g. the RS232 LDD/PDD); others are external and must be loaded (e.g. a bar-code reader). (PLIB Ch.9.)
The interface between the OS and an LDD does not follow a C calling convention; an LDD may be written in C but requires some 8086 assembly for the LDD vector interface. (PLIB Ch.9; System Services reference.)
1.2 Opening a channel: p_open
INT p_open(VOID **ppfcb, TEXT *name, UINT mode);
INT f_open(VOID **ppfcb, TEXT *name, UINT mode); /* p_leave()s on error */
name is a 3-character device name terminated by a ':', optionally followed
by further text depending on the device. Examples (PLIB Ch.9):
| Name | Device |
|---|---|
FIL: |
a file |
PAR: |
a parallel port |
TTY: |
an RS232 serial port |
TIM: |
an asynchronous timer |
Rules (PLIB Ch.9, "Opening a channel to a device"):
- The device name of an external LDD bears no relation to the file it was loaded from.
- Where a driver supports more than one unit, the
':'is followed by a unit letter:TTY:A= serial port A,PAR:B= parallel port B. - For
FIL:, the qualifying text is a file name or full path. - A loaded device driver supersedes any existing device of the same name.
On success the channel control block address is written to *ppfcb. On failure
*ppfcb is left untouched — pre-set it to zero so p_close(0) (a no-op) is safe
in clean-up. For an attached driver, the control block is attached to *ppfcb
and the value is not changed. (PLIB Ch.9.)
Errors: E_FILE_ALLOC (no memory for control block), E_FILE_DEVICE (device does
not exist), E_GEN_ARG (bad mode, possibly from falling through to FIL:).
1.3 The FIL: fallback
If p_open fails to match a device name, the name is passed to the FIL:
driver (the process must be connected to the file server). This makes the leading
FIL: optional when opening files:
p_open(..., "C:\\NOTES\\NEW.TXT", ...) == p_open(..., "FIL:C:\\NOTES\\NEW.TXT", ...)
Side effect: opening a non-existent device does not give the expected
E_FILE_DEVICE, because the name is handed to FIL: and the result depends on
mode (it might even succeed). Passing mode = -1 guarantees the FIL: open
fails — albeit with the misleading E_GEN_ARG. Keeping an explicit FIL: prefix
avoids mistaking a file spec for a device name (e.g. a file literally named
TTY:). (PLIB Ch.9.)
1.4 Modes
The interpretation of mode depends on the device; many devices ignore it.
When a device ignores mode, pass -1. (PLIB Ch.9.) The character/hardware
devices in IODEV (TTY:, PAR:, SND:, FRC:, MCR:, BAR:) are all opened
with mode = -1. File-open mode flags such as P_FOPEN, P_FREPLACE,
P_FUPDATE, P_FUNIQUE, P_FSTREAM, P_FSHARE, P_FTEXT are FIL:-specific and
are described in the Files chapter of PLIB.
1.5 Attached drivers
An attached driver is an LDD layered over another LDD, replacing or augmenting
its services. Opening it attaches to an already-open underlying channel rather
than allocating a new one. Example: the printer driver PRO: is opened over a
print-output device (PAR:, TTY: or FIL:) and thereafter replaces that
device's P_FWRITE, P_FCANCEL and P_FCLOSE. Attached drivers that work
asynchronously install a device wait handler at open, called from within the
opening process's p_iowait. (PLIB Ch.9, "Attached drivers".)
1.6 The file server
FIL: operations are serviced by the high-priority system process SYS$FSRV,
which also loads executables and loads external device drivers. It serialises
access to shared storage (SSDs) and uses PDDs to reach the many FLASH/RAM/ROM
configurations. Applications are "clients" of the file server; the normal start-up
code connects to it. (PLIB Ch.9, "The file server".)
2. Operations on an open channel
All I/O requests are asynchronous in principle — the process I/O semaphore is
always signalled on completion — but many are implemented synchronously
(completing before the call returns). A typical device offers zero to three truly
asynchronous functions (commonly P_FREAD / P_FWRITE); the rest are synchronous.
P_FCLOSE is always synchronous. (PLIB Ch.9; IODEV Ch.1.)
2.1 The I/O primitives
/* asynchronous, CDECL + register-calling variants */
INT p_ioa (VOID *pcb, INT func, WORD *pstat, ...);
INT p_ioa3(VOID *pcb, INT func, WORD *pstat);
INT p_ioa4(VOID *pcb, INT func, WORD *pstat, VOID *a1);
INT p_ioa5(VOID *pcb, INT func, WORD *pstat, VOID *a1, VOID *a2);
/* asynchronous, error folded into *pstat (preferred over p_ioa) */
VOID p_ioc (VOID *pcb, INT func, WORD *pstat, ...);
VOID p_ioc3(VOID *pcb, INT func, WORD *pstat);
VOID p_ioc4(VOID *pcb, INT func, WORD *pstat, VOID *a1);
VOID p_ioc5(VOID *pcb, INT func, WORD *pstat, VOID *a1, VOID *a2);
/* synchronous: start then wait (calls p_waitstat) */
INT p_iow (VOID *pcb, INT func, ...);
INT p_iow2(VOID *pcb, INT func);
INT p_iow3(VOID *pcb, INT func, VOID *a1);
INT p_iow4(VOID *pcb, INT func, VOID *a1, VOID *a2);
p_ioastarts the operation and returns immediately. On success,*pstatholdsE_FILE_PENDINGuntil the I/O semaphore is signalled, then zero or a negative error. A cancelled operation completes withE_FILE_CANCEL. ReturnsE_FILE_INViffuncis invalid for the device. The status word must outlive the operation — never put it on a stack frame you return from. (PLIB Ch.9.)p_iocbehaves likep_ioaexcept a failure to start is reported exactly as if the request had started and then completed with that error (only*pstatto check). Preferred overp_ioa. (PLIB Ch.9.)p_iowstarts the request and waits (p_waitstat) for completion, returning the status. Preferred unless you actually need asynchrony. (PLIB Ch.9.)
The register-calling variants (p_iow2/3/4, p_ioc3/4/5, p_ioa3/4/5) generate
smaller code and are preferred where applicable; e.g. p_ioc3 over p_ioa3.
(IODEV Ch.1; PLIB Ch.9.)
Driver rule: normally one pending request per I/O operation per channel. A
second P_FWRITE while one is pending will p_panic the caller; but one read and
one write may be pending simultaneously. (PLIB Ch.9; ADDSYS driver chapter.)
2.2 Convenience functions
| Function | Wraps | Notes |
|---|---|---|
INT p_close(VOID *pcb) |
p_iow(P_FCLOSE) |
p_close(NULL) returns 0. Always closes even if it returns an error. |
INT p_read(VOID *pcb, VOID *buf, UINT len) |
p_iow(P_FREAD) |
returns bytes read, or negative error. f_read p_leaves on error. |
INT p_write(VOID *pcb, VOID *buf, UINT len) |
p_iow(P_FWRITE) |
returns 0 or negative error. f_write p_leaves on error. |
INT p_seek(...) |
p_iow(P_FSEEK) |
FIL: only (Files chapter). |
(PLIB Ch.9.)
2.3 Waiting and cancelling
VOID p_iowait(VOID); /* wait on the process I/O semaphore */
VOID p_waitstat(WORD *pstat); /* wait until *pstat leaves E_FILE_PENDING */
To wait on one specific request, use p_waitstat; p_iowait waits for any
completion and is used in the central dispatch loop. Every p_iosignal must be
matched by a p_iowait (or a function that calls it). (PLIB Ch.8.)
Cancelling — p_iow(pcb, P_FCANCEL) — cancels outstanding async requests on the
channel and returns zero (harmless if none pending). Principles (PLIB Ch.9):
- the cancel precipitates completion; it does not stop the request completing;
- it may or may not be effective (the request may complete naturally first);
- you must still consume the completion signal — typically an immediate
p_waitstatto "use up" the signal.
p_waitstat is safer than p_iowait for using up a cancelled request's signal.
General-purpose synchronous wrappers must use p_waitstat, not p_iowait.
3. I/O function codes (p_file.h)
Services are named P_Fxxx and defined in p_file.h. The generic codes that apply
across many devices are (PLIB Ch.9; IODEV Ch.1):
| Code | Meaning |
|---|---|
P_FREAD |
read data from a channel |
P_FWRITE |
write data to a channel |
P_FCLOSE |
close a channel |
P_FCANCEL |
cancel outstanding async requests |
P_FSENSE |
sense channel characteristics |
P_FSET |
set channel characteristics |
P_FFLUSH |
flush buffered data |
P_FCTRL |
test/set control lines (serial) |
P_FINQ |
inquire supported characteristics |
Numeric values are assigned in p_file.h. The canonical numbering is:
| Constant | Value |
|---|---|
P_FREAD |
1 |
P_FWRITE |
2 |
P_FCLOSE |
3 |
P_FCANCEL |
4 |
P_FSET |
7 |
P_FSENSE |
8 |
P_FFLUSH |
9 |
P_FCTRL |
11 |
P_FINQ |
12 |
Confirmation from the manuals: the AccessIr chapter tabulates its own function numbers and gives
P_FREAD = 1,P_FWRITE = 2(IODEV Ch.16, "Constants"). The System Services driver model (ADDSYS) documents the OS-defined common setIoFuncRead / IoFuncWrite / IoFuncClose / IoFuncCancel / IoFuncSet / IoFuncSense / IoFuncFlush, which map toP_FREAD … P_FFLUSH;p_read,p_writeandp_closecall the driver withIoFuncRead,IoFuncWriteandIoFuncCloserespectively. Drivers are urged to keep these conventional meanings so that attached drivers work.
Device-specific codes also exist, e.g. P_FSEEK, P_FSETEOF (files); P_FTEST
(test for input); P_FEDIT (console edit); P_FRELATIVE / P_FABSOLUTE (timers);
P_FSTART (FRC); P_FCONNECT / P_FDISCONNECT / P_FRSUPER (NCP, Xmodem);
P_FIR* (IR); E_FALARM / E_FDIAL / E_FSSOUNDCHANNELn (sound). These are
listed with the individual devices below and in IODEV.
Error numbers -32..-63 are reserved for I/O device errors of the form
E_FILE_xxx (also in p_file.h). (PLIB Ch.6.)
4. Loading external device drivers
4.1 Load / delete
INT p_loadldd(TEXT *pName); /* default extension .LDD */
INT p_loadpdd(TEXT *pName); /* default extension .PDD */
INT p_devdel (TEXT *pName, INT devType); /* devType: E_LDD or E_PDD */
p_loadldd/p_loadpddload a driver from a file; ifpNamehas no extension,.LDD/.PDDis assumed, and a relative name uses the current path. After loading, a channel is opened withp_open. Errors:E_FILE_EXIST(same file already loaded),E_FILE_NXIST(file missing),E_GEN_IMAGE(bad/corrupt format),E_GEN_NOMEMORY,E_GEN_NOSEGMENTS. Apps that rely on an external LDD should callp_loadlddand ignoreE_FILE_EXIST. (PLIB Ch.9.)p_devdeldeletes a RAM-loaded driver by device name (no trailing':'). Only loaded (not ROM) drivers can be deleted. Errors:E_FILE_DEVICE(not loaded),E_GEN_NSUP(ROM driver),E_GEN_INUSE(currently open), or a device-dependent error. Warning: a nullpNamedeletes the first unloadable driver of that type. Good practice: attemptp_devdelwhen finished and ignore the result (harmless if in use elsewhere or in ROM). (PLIB Ch.9.)
Related helpers: p_devqu(pName) returns the number of units an LDD supports (e.g.
p_devqu("TTY") → 2 if two serial boards are fitted; E_GEN_FAIL means unlimited,
as FIL: returns); p_devfnd(...) iterates device names matching a wildcard.
(PLIB Ch.9.)
4.2 The .LDD / .PDD model and device memory segments
External drivers are loaded from a driver file into a device memory segment, allocated by the segment allocator. The ability to load and remove drivers without a system reset is a key EPOC feature: you can physically attach a peripheral and load its driver without exiting any process. The I/O system also notifies drivers on power off/on so they can save/restore device state. (PLIB Ch.9.)
Memory segments (PLIB Ch.7, "Memory Allocation"):
- Device segments are created when an external device is installed and deleted
when removed; once created a device segment does not normally change size. The
first two device segments are special — the data segments of
SYS$NULLand the supervisorSYS$SMANG. - Device segments are allocated at lower addresses than the volatile dynamic segments so that dynamic-segment activity does not move them (a driver normally has to stop working while its segment moves — risking data loss on, say, a serial receive).
- Segment name extension conventions:
.LDD= LDD device segment,.PDD= PDD device segment;.$SCprimary shared code,.DYLdynamic library,.$nnprocess data segment. - The segment index table has fixed sub-capacities — typically 96 total (32 device + 64 dynamic). Addresses/sizes are in 16-byte paragraphs.
5. Per-device reference
5.1 Serial port — TTY: (IODEV Ch.4)
Fully interrupt-driven RS-232. Two cooperating layers: a hardware PDD and a hardware-independent LDD (which buffers incoming data).
Device names. First port TTY:A; second TTY:B. Availability varies: a Series 3
with a 3-Link recognises only TTY:A; an MC with two serial/parallel modules has
TTY:A (left) and TTY:B (right); HC has a third, cradle port TTY:C. HC also
exposes TTL-level ports TTY:D/E/F (direct) and TTY:G/H/I (inverted) — not
documented in that chapter.
Parameters — P_SRCHAR (p_serial.h), all fields:
typedef struct {
UBYTE tbaud; /* transmit baud rate */
UBYTE rbaud; /* receive baud rate */
UBYTE frame; /* data bits + stop + parity-present */
UBYTE parity; /* parity type */
UBYTE hand; /* handshake flags */
UBYTE xon; /* XON character (default DC1 0x11) */
UBYTE xoff; /* XOFF character (default DC3 0x13) */
UBYTE flags; /* control flags */
ULONG tmask; /* terminator mask */
} P_SRCHAR;
Baud (tbaud, rbaud) — one of P_BAUD_50, _75, _110, _134, _150, _300, _600, _1200, _1800, _2000, _2400, _3600, _4800, _7200, _9600, _19200, _38400, _56000.
Default P_BAUD_9600. All SIBO machines support _50.._9600; MC 200/400 and
Series 3a also support _19200; HC/Series 3 can be set to _19200 but their clock
is slightly too slow (frequent overruns). No SIBO hardware supports split
transmit/receive rates.
Frame (frame) — one of P_DATA_5 / P_DATA_6 / P_DATA_7 / P_DATA_8, optionally
OR'd with P_TWOSTOP (2 stop bits, else 1) and P_PARITY (parity bit present).
Default P_DATA_8 (8 data, 1 stop, no parity). All settings supported on all SIBO
machines.
Parity (parity) — used only if P_PARITY is set: P_PAR_EVEN, P_PAR_ODD,
P_PAR_MARK, P_PAR_SPACE. Default 0. No SIBO machine supports mark or space
parity.
Handshaking (hand, plus xon/xoff) — combination of:
| Flag | Effect |
|---|---|
P_OBEY_XOFF |
obey received XON/XOFF (input flow control) |
P_SEND_XOFF |
transmit XON/XOFF to remote (output flow control) |
P_IGN_CTS |
if set, RTS held permanently active and incoming CTS ignored; if clear, RTS/CTS flow control |
P_OBEY_DSR |
if set, suspend TX when incoming DSR inactive (DTR/DSR); DTR held active while port is open |
P_FAIL_DSR |
(only if P_OBEY_DSR) complete outstanding P_FREAD/P_FWRITE with E_FILE_LINE if DSR goes inactive |
P_OBEY_DCD |
if set, suspend TX when incoming DCD inactive |
P_FAIL_DCD |
(only if P_OBEY_DCD) complete requests with E_FILE_LINE if DCD goes inactive |
Default hand = 0 → RTS/CTS handshaking. DCD is an input-only carrier indicator,
not a flow-control line. xon/xoff default to DC1 (0x11) / DC3 (0x13).
Control flags (flags) — only P_IGNORE_PARITY is defined (discard parity
errors; the errored character is still delivered). Other bits reserved zero. Default 0.
Terminator mask (tmask) — 32 bit flags, bit n (n = 0..31) selecting control
code 0x00..0x1F as a terminating character. E.g. bit 13 = CR, bit 10 = LF (to
read a line at a time); bit 26 = Ctrl-Z. A read completes when a terminator is
received (included in the returned length). Default 0 (none).
Errors: E_FILE_PARITY, E_FILE_FRAME, E_FILE_OVERRUN, E_FILE_LINE
(inactive required control line), plus E_GEN_OVER (driver receive buffer full)
and E_FILE_RECORD (buffer filled with no terminator seen).
Services:
| Service | Prototype / notes |
|---|---|
p_open("TTY:x", -1) |
Powers up port, drives DTR active. Defaults as above; RTS not driven active until first P_FREAD or a P_FSET (so buffered-modem data is not lost). Errors: E_GEN_NOMEMORY, E_GEN_INUSE, E_FILE_DEVICE, E_FILE_LOCKED. |
P_FREAD |
p_iow(pcb, P_FREAD, buf, &len) — read up to *len bytes. Completes on: full length; receive error (partial data + negative status); a tmask terminator (length includes it); or cancel (E_FILE_CANCEL). After a cancel, use P_FTEST — more buffered chars may remain. |
P_FWRITE |
p_iow(pcb, P_FWRITE, buf, &len) — obeys current handshaking. A zero-length write with P_OBEY_DSR set completes only when DSR goes active (used to detect a connection). Cancel → E_FILE_CANCEL; the driver does not report bytes sent before cancel. |
P_FCANCEL |
cancel outstanding read and write. |
P_FSENSE |
p_iow(pcb, P_FSENSE, P_SRCHAR*) — read current characteristics (cannot fail). |
P_FSET |
p_iow(pcb, P_FSET, P_SRCHAR*) — set characteristics (sense-modify-set idiom). Errors E_GEN_ARG, E_GEN_NSUP, E_FILE_LINE. Panics if a read/write is outstanding. |
P_FFLUSH |
discard the LDD read buffer, clear error status, release paused remote. |
P_FTEST |
p_iow(pcb, P_FTEST, &len) — bytes currently buffered (at least that many can be read synchronously). |
P_FCTRL |
p_iow(pcb, P_FCTRL, UBYTE *pctrl) — read CTS/DSR/DCD input line state into *pctrl as a bit mask, and optionally set DTR (see below). |
P_FINQ |
p_iow(pcb, P_FINQ, UWORD *pmask) — write three words of supported-characteristic bit flags. |
Control-line constants (p_serial.h):
- Input line state returned by
P_FCTRLin*pctrl:P_SRCTRL_CTS,P_SRCTRL_DSR,P_SRCTRL_DCD(bit set ⇒ line active). - DTR output: if
*(pctrl+1)is non-zero it sets DTR toP_SRDTR_ON(active) orP_SRDTR_OFF(inactive).E_GEN_NSUPif the driver can't set DTR (all current SIBO machines can). P_FINQbit flags: word 0 =P_SRINQ_50 … P_SRINQ_19200; word 1 =P_SRINQ_38400,P_SRINQ_56000(not supported on SIBO); word 2 =P_SRINQ_DATA5/6/7/8,P_SRINQ_STOP2,P_SRINQ_PAREVEN/PARODD/PARMARK/PARSPACE,P_SRINQ_SETDTR,P_SRINQ_SPLIT. SIBO supports all exceptPARMARK,PARSPACEandSPLIT.
p_close flushes the receive buffer, waits for any in-flight TX char, cancels
outstanding read/write, drops RTS and DTR, and powers the port down.
5.2 Parallel port — PAR: (IODEV Ch.3)
Standard Centronics, output-only (no read service). Available via serial/parallel
or parallel expansion modules; HC cradle provides a third. Names PAR:A, PAR:B,
PAR:C (e.g. Series 3 = PAR:A only; MC with two modules = PAR:A left, PAR:B
right).
p_open("PAR:x", -1)— powers up the port lines (all control lines cleared low); it draws power until closed. Errors:E_FILE_ALLOC,E_FILE_DEVICE,E_FILE_LOCKED/E_GEN_INUSE.P_FWRITE—p_iow(pcb, P_FWRITE, buf, &len). Never completes if no functioning receiver is connected — always write asynchronously with a timer timeout. ErrorsE_FILE_WRITE,E_FILE_CANCEL.P_FCANCEL— cancel the write (an indeterminate amount will already have been written).P_FSENSE—p_iow(pcb, P_FSENSE, UWORD *port)reads input control lines (p_par.h):S_BUSY(pin 11),S_ACK(10),S_ERROR(15),S_PE(12). Only on HC/MC ranges with EPOC ≥ 2.30; not on Series 3/3a.P_FSET—p_iow(pcb, P_FSET, UWORD *type, UWORD *port)sets/clears output lines:*type = 1sets high,0clears low. Lines:S_INIT(16),S_AUTOFD(14),S_SELECT(17), plusS_SPARE(custom hardware only — ASIC5 pin 42, no effect on the standard module). Same HC/MC ≥ 2.30 restriction.
5.3 Timers — TIM: (async) and FRC: (free-running counter)
TIM: — asynchronous timer. Documented in the PLIB "Time, Timers and Dates"
chapter (not in IODEV). Opened p_open(&tcb, "TIM:", -1). Requests are placed on
a delta queue via p_ioc(P_FRELATIVE, ..., &ticks) (relative) or
p_ioc(P_FABSOLUTE, ...); cancel with P_FCANCEL. Relative units are 1/10 s (the
examples use tval = 10L * secs). TIM: is the standard tool for I/O timeouts (see
the PAR: and TTY: examples in IODEV). (PLIB Ch.8/9; Time chapter.)
FRC: — free-running counter (IODEV Ch.7). Specific to Series 3a and
Workabout. Accuracy ±2 ppm, resolution 1/1024 s. Supports only one process at a
time. For resolutions of 1/32 s or coarser, use TIM: instead.
p_open("FRC:", -1)— errorsE_FILE_OPEN(in use),E_GEN_NOMEMORY.P_FSTART—p_iow(pcb, P_FSTART, UWORD *pmode, UWORD *pint):E_FRC_COUNTING: counter increments every 1/1024 s from 0;pintignored.E_FRC_REPEATING: increments every*pint× 1/1024 s (*pintin 10..65535, ≈0.01–64 s). ErrorsE_GEN_NSUP(bad mode),E_GEN_RANGE(interval 0..9).
P_FREAD(COUNTING mode):p_iow(pcb, P_FREAD, ULONG *arg1)— elapsed 1/1024 s sinceP_FSTART, not reset by the read.P_FREAD(REPEATING mode):p_iow(pcb, P_FREAD)— completion status = number of whole intervals elapsed since the lastP_FSTART/P_FREAD(waits for ≥1 interval; time is not lost between reads).- Read errors:
E_GEN_OVER(>32767 intervals / too large forarg1),E_FILE_CANCEL,E_FILE_READ(counter not running, or machine was switched off mid-count).
5.4 Sound — SND: (IODEV Ch.5)
Speaker driven by the SND: driver; a separate piezo buzzer (all machines
except Series 3a, which emulates it via the speaker) is driven by the PLIB
p_sound routine, not SND:. Output is disabled if the E_SOUND_DEVICE sound-flag
bit is clear or E_SOUND_DISABLE is set (managed via p_getsnd/p_setsnd). The
speaker is a two-voice device (chords / DTMF). Series 3 SND: is limited to
DTMF and simple alarms — richer sound needs an extra LDD (e.g. SVDFRC.LDD on the
SDK disk). Series 3a can also play/record .WVE files.
p_open("SND:", -1)— errorsE_FILE_ALLOC,E_GEN_FAIL(disabled),E_FILE_LOCKED/E_GEN_INUSE.P_FCANCEL— cancel outstanding write.P_FSENSE/P_FSET—E_SOUNDstruct (epoc.h):typedef struct { UBYTE beatsPerMinute; UBYTE volume; } E_SOUND;beatsPerMinute(HC/MC/3a)E_SOUND_MIN_BPM2 ..E_SOUND_MAX_BPM240, default 120 (no effect on Series 3).volume0 (E_SOUND_MAX_VOLUME, loudest) .. 5 (E_SOUND_MIN_VOLUME); Series 3 range 1..4; Series 3a has four distinct volumes; smaller = louder; default 1. Always sense-then-set immediately after open, before playing — even at defaults.E_FALARM—p_iow(pcb, E_FALARM, UWORD *palarm):*palarm0 = "rings", 1 = "chimes".E_FSSOUNDCHANNELn(HC/MC/3a only) —p_ioc(pcb, E_FSSOUNDCHANNELn, pstat, WORD *pnotes, WORD *plen), n = 1 or 2.*plen ≤ 16384notes; each note is two words (frequency Hz — middle A = 440; duration in beats). Output starts only once both channels have been called (for sync); use*plen = 0for an unused voice.E_FDIAL(Series 3 / 3a only) —p_iow(pcb, E_FDIAL, TEXT *pstr, E_DIAL *pdial)emits DTMF for*pstr.Valid chars 0-9, A-F (typedef struct { UBYTE toneLengthTicks; UBYTE delayLengthTicks; UWORD pauseLengthTicks; } E_DIAL; /* ticks = 1/32 s */#→F,*→E); space/comma = pause; others ignored; max 26 tone/pause chars. ErrorsE_FILE_CANCEL,E_GEN_ARG(too many chars).
5.5 Magnetic card reader — MCR: (IODEV Ch.12)
HC MCR driver, built into the HC OS. Interface fits top (MCR:A), bottom (MCR:B)
or cradle (MCR:C).
p_open("MCR:x", -1)— errorsE_GEN_NOMEMORY,E_FILE_DEVICE(no interface in slot),E_FILE_NAME,E_FILE_LOCKED/E_GEN_INUSE.P_FREAD—p_iow(pcb, P_FREAD, UBYTE *buf1, UBYTE *buf2): track 1 →*buf1, track 2 →*buf2(each buffer ≥ 256 bytes). Data is leading-byte-count ASCII; count 0 = unsuccessful. Pass either buffer asNULLto skip that track. Track 1 = alphanumeric (name/account), track 2 = numeric (most readers do track 2 only). ErrorsE_FILE_READ(decode error),E_GEN_OVER,E_FILE_CANCEL.P_FCANCEL— cancel the read.P_FSET(EPOC ≥ 2.32) —p_iow(pcb, P_FSET, UWORD *mask)programs the 100 kΩ pull-up/down resistors on the five reader lines (bit set = pull-up, clear = pull-down; default all pull-down):M_DATA1PU0x01,M_CLK1PU0x02,M_DATA2PU0x04,M_CLK2PU0x08,M_CLSPU0x10 (card-present). Other bits ignored.
5.6 Infrared (IODEV Ch.15–17)
Psion IR is IrDA-compliant only (S3a/Siena ports won't talk to non-IrDA devices). The protocol stack, bottom to top: SIR (Serial Infrared physical layer device driver, drives the hardware) → IrLAP (link access) → IrLMP (link management) → the IrMUX API (server side) and AccessIr API (client side). Two application-facing device channels are exposed:
AIR:— the AccessIr API device.p_open(&pcb, "AIR:", 0)(mode 0) powers up the port; errorsE_GEN_INUSE,E_GEN_NOMEMORY. Services useP_FIR*codes (numeric values from IODEV Ch.16):P_FREAD1,P_FWRITE2,P_FIRDISCONNECT4,P_FIRDISCOVER5,P_FIRSELECT6,P_FIRAWAITCONNECT7,P_FIRMAKECONNECT8. Flow:P_FIRDISCOVER(log in-range machines) →P_FIRSELECT→P_FIRMAKECONNECT(primary) /P_FIRAWAITCONNECT(secondary), exchanging up to 56 bytes of connect data →P_FREAD/P_FWRITE→P_FIRDISCONNECT.IRP:— the IR printer-port device driver (the "IRP" API), used for IR printing. (IODEV Ch.15, protocol-layer diagram.)
The IrMUX API (IODEV Ch.17) is the server-side interface (LM-IAS
registration, connection-oriented and connectionless reads/writes) exposed through
LM_* messages rather than P_Fxxx codes.
MX note: these chapters describe the S3a/Siena/S3c-era IrDA stack; the manuals do not document Workabout-MX-specific IR behaviour.
6. Barcode scanning
6.1 Workabout MX integral laser — see SCANNER-API.md
The Workabout MX integral laser scanner is not covered by these manuals. It is
documented separately in SCANNER-API.md (sibling of this file, at
/tmp/claude/inv/SCANNER-API.md). In brief, per that document: the MX uses the
logical driver WL2 (units WL2:A, WL2:D) with decoder type Symbol2
(the integral Symbol laser engine); it is a decoded scanner, triggered by the
keyboard scan key (Window Server key code 368). Do not duplicate that content
here — consult SCANNER-API.md for the MX.
The remainder of this section summarises only the older external wand/RS232 barcode devices as documented in IODEV.
6.2 External wand decoders — BAR: (IODEV Ch.13)
The HC has no built-in barcode driver/decoder; readers are supported by external
combined decoder + device-driver LDDs that the app must load (p_loadldd or OPL
DevLoadLdd). Wand interface module fits the top (BAR:A) or bottom (BAR:B) of
the HC. Available drivers (device name BAR in all cases):
| File | Symbologies |
|---|---|
BAREAN.LDD |
EAN8, EAN13, UPC, UPCE |
BARC39.LDD |
Code 39 |
BARITF.LDD |
Interleaved 2 of 5 (ITF) |
BAR128.LDD |
Code 128 |
BARMPLES.LDD |
Modified Plessey |
BARRAW.LDD |
raw |
None auto-discriminates. Load e.g. p_loadldd("BARC39.LDD"); remove with
p_devdel("BAR") (or OPL DevDelete).
p_open("BAR:", -1)— opens via the previously loaded LDD. Errors as forMCR:.P_FREAD—p_iow(pcb, P_FREAD, UBYTE *buf)(buf ≥ 256). Leading-byte-count ASCII; count 0 = failed read. The first character encodes the symbology (e.g. EAN8/13, UPC, Code 39, ITF, Code 128, Modified Plessey, UPCE); the rest is the decoded data. ErrorsE_GEN_OVER,E_FILE_CANCEL.P_FCANCEL— cancel the read.
6.3 Intelligent reader / RS232 module — TTY: escape commands (IODEV Ch.14)
The intelligent RS232/barcode expansion module contains a decoding
micro-controller and shares one serial port between an RS232 interface and the
barcode interface (not usable simultaneously). Port mapping by slot: top slot →
RS232 TTY:A, barcode TTY:D; bottom slot → RS232 TTY:B, barcode TTY:E. The
barcode interface talks to the host at the standard HC comms settings (XON/XOFF,
no hardware handshaking). It auto-discriminates EAN/JAN 8/13, UPCA, UPCE, Codabar,
Code 128, Interleaved 2 of 5, and Code 39 (standard/extended), transmitting decoded
data left-to-right as ASCII, terminated by default with a single CR.
Because it is reached through TTY:, it uses the normal serial services
(p_open, P_FSENSE, P_FSET, P_FREAD, P_FWRITE, p_close). It is
programmed by writing escape sequences to it via the serial channel:
<Esc>-y<code><command> general form
<Esc>-y<code><command><text> set-termination-string form
<Esc>E hard reset (special form)
<Esc> = 0x1B, then - (0x2D), then y/Y, a decimal <code> of 1–3 digits
(0–255), and an uppercase command letter; no embedded spaces. Multiple option bits
are combined by summing their <code> values. Multiple commands may be
concatenated in one sequence — intermediate command letters lowercase, the final
one uppercase (e.g. <Esc>-y13f2h1D).
Command letters (each <Esc>-y<code><letter>):
| Cmd | Function |
|---|---|
D |
serial inter-character delay (0 = none, 1 = 10 ms) |
E |
hard reset + self-test (special form <Esc>E; reverts all options to default; self-test failures reported as ... SELF TEST FAILED<CR><LF>) |
F |
select symbology bitmask: 1 Code39, 2 ITF, 4 UPC/EAN, 8 Codabar, 16 Code128 (default 31) |
G |
check-character options (verify / transmit check digits; per-symbology) |
H |
decoding options (extended Code39, Codabar start/stop, UPC-vs-EAN, 2/5-digit supplements, UPC E→A expansion, auto-discriminate supplements) |
J |
single-read mode enable/disable |
K |
single-read control (fire one read when in single-read mode) |
M |
set Interleaved 2 of 5 length |
O |
set termination string (<Esc>-y<code>O<string>, up to 4 chars) |
Q |
Code ID characters |
S |
status request |
W |
scanner enable |
Per-symbology output formats (ID chars, check digits, supplement digits for UPC E/A, EAN 8/13 and their +2/+5 variants; Codabar start/stop; ITF and Code 39 check handling) are tabulated in IODEV Ch.14.
7. Other channel devices (pointers)
IODEV also documents the Console (CON:, Ch.2 — screen/keyboard, P_SCR_*
function codes, P_CON_KBREC), Alarm (Ch.6, A_FTIMED/A_FUNTIMED), World
database (Ch.8), Xmodem/Ymodem (Ch.9, P_FCONNECT/P_FREAD/P_FWRITE),
NCP / Link (Ch.10, SYS$NCP), Cradle/Docking Station (Ch.11), and the
Fast Charger (Ch.18, FCHG_*). The FIL: file driver and the TIM:
timer are documented in the PLIB Files and Time chapters. These are outside the
scope of this reference but follow the same p_open + P_Fxxx model described in
Sections 1–3.
Source map
| Topic | Source |
|---|---|
Device model, p_open, FIL: fallback, modes, attached drivers, file server |
PLIB Ch.9 |
I/O primitives (p_ioa/ioc/iow + variants), p_read/write/close, cancel |
PLIB Ch.9 |
p_iowait / p_waitstat, semaphore rules |
PLIB Ch.8 |
Function codes, IoFunc* mapping |
PLIB Ch.9; IODEV Ch.1/16; ADDSYS driver chapter |
p_loadldd/p_loadpdd/p_devdel/p_devqu/p_devfnd; .LDD/.PDD; device segments |
PLIB Ch.9, Ch.7 |
TTY:, P_SRCHAR, control lines |
IODEV Ch.4 |
PAR: |
IODEV Ch.3 |
FRC: |
IODEV Ch.7 |
TIM: |
PLIB Time chapter (referenced from IODEV Ch.1) |
SND: |
IODEV Ch.5 |
MCR: |
IODEV Ch.12 |
IR (AIR:, IRP:, IrMUX) |
IODEV Ch.15–17 |
External BAR: wand decoders |
IODEV Ch.13 |
Intelligent reader / TTY: escape commands |
IODEV Ch.14 |
Workabout MX integral laser (WL2/Symbol2) |
SCANNER-API.md (not these manuals) |