Files
sibo-playground/code/inventory/SCANNER-API.md
T

7.0 KiB

Workabout MX barcode scanner — API notes (reverse-engineered)

There is no official SDK documentation for the Workabout MX integral laser scanner. The Psion SIBO C SDK and I/O Devices Reference predate the MX and describe only the older external barcode modules (wands / wand-emulation), not the integral laser. These notes are reverse-engineered from device behaviour and from the Workabout MX ROM w2mx_v7.20f_eng.bin (strings), so treat unconfirmed items as such.

Hardware

  • Integral laser scanner, Symbol engine. The demo (DEMMAN.APP) reports Type: Laser 1223 (some units 1222).
  • It is a decoded scanner: it decodes in hardware and lights a green good-read LED. It does not emit an undecoded (HHLC) signal.
  • Trigger: the keyboard scan key, which the Window Server reports as key code 368. The key alone does not fire the laser from an arbitrary app; the scanner software arms/reads the engine.

Device driver and ports

LLDEV on the device lists a logical driver wl2 (units=1). The scanner is reached through this driver. The demo's "Select Scanner" screen lets you pick both a decoder type and a port (device driver + unit letter):

  • Port device drivers offered: TTY, WLS, WL2. The MX integral laser uses WL2.
  • Units seen in the ROM: WL2:A and WL2:D.

Decoder types (from the demo, "chosen from those used in standard Workabout products"):

Decoder Meaning
Generic External non-configurable reader
Symbol1 Workabout standard scanner
Datalogic Workabout CCD
HP Workabout Wand
Symbol2 Workabout MX scanner (the integral laser)

Opening the device

VOID *h;
INT  err = p_open(&h, "WL2:D", (UINT)-1);

Observed results:

Call Result Meaning
p_open("WL2:D", -1) 0 opens — this is the integral-laser unit
p_open("WL2:A", -1) -9 (E_GEN_INUSE) in use — held by the resident scanner software
p_open("WL2:B" / ":C") (untested)

WL2 has a single unit, so only one process may hold it. WL2:A is permanently -9 even with the demo closed, indicating a resident holder; WL2:D is the unit an application opens.

The 11-byte parameter block

The scanner is configured by an eleven-byte parameter block. The application interprets these bytes and converts them into commands for the selected decoder (so the same block means different wire commands for Symbol2 vs HP, etc.):

Byte Parameter
Param0 Decode security
Param1 Code type (symbology select)
Param2 Decode options A
Param3 Decode options B
Param4 General parameters
Param5 ITF length 1
Param6 ITF length 2
Param7 Preamble
Param8 Postamble byte 1
Param9 Postamble byte 2
Param10 General decode options

The preamble/postamble bytes mean decoded output may carry configurable leading / trailing characters — parsing code must account for them.

Access model

Applications do not drive WL2 byte-by-byte themselves; they use the ROM library SCANNER.DYL (used by SCANAPP.APP and DEMMAN.APP). That library opens the port, converts the 11-byte block to decoder commands, configures and enables the engine, and returns decoded scans.

Paths that do NOT work for the integral laser

Recorded so they are not retried:

  • bar*.ldd decoders (BAREAN, BARC39, …) + BAR: device. These are software decoders for the external wand modules. Loading BAREAN.LDD and opening BAR:A/BAR:B returns -41 (E_FILE_DEVICE, "no interface found in slot") — the integral laser is not a BAR: expansion interface. BAR:D/BAR:E return -38 (E_FILE_NAME, invalid unit).
  • TTY:D serial reads. Opening TTY:D powers the laser (it fires briefly), but no decoded bytes ever arrive on the serial channel — not with default config, not after P_FSET to 9600/8/1, not with P_OBEY_DSR cleared, not with the intelligent-reader escape commands (<Esc>-y1J / <Esc>-y1K), not reading one byte at a time. The integral laser's decoded data is not on TTY:D.

Initialisation sequence (reverse-engineered from the ROM)

Opening WL2:D is not enough — the scanner must be configured and enabled first. The sequence was recovered by disassembling SCANNER.DYL in the v7.20f ROM (around file offset 0xD7AA4). It uses the SIBO I/O executive int 0xCF, whose convention here is: CL = I/O function code, BX = channel handle, DX = argument (by value), result in AX. From C this is the p_iow(chan, func, ...) layer.

Function codes observed on the WL2 channel (from p_file.h, plus WL2-specific ones above the standard range):

CL Meaning
6 (P_FDETACH slot) used as a scanner enable/control op
7 (P_FSET) used as a scanner enable/control op
8 P_FSENSE
9 P_FFLUSH
0x0C (12) WL2-specific: write a config/command byte (byte passed in DX)

The driver keeps a per-channel structure; the param block sits at channel+4, with the decoder type at offset +6 (dispatch values 1..4) and the 11-byte parameter block at +8..+0x12. Configuration is pushed to the engine as a series of cl=0x0C writes (one command byte per call in DX), then enable ops (cl=6, cl=7).

Default Symbol2 parameter block (from the ROM)

The routine at 0xD7AB3 fills the 11-byte block with these defaults for the Workabout MX (Symbol2) scanner:

Byte Param Value
+8 Param0 Decode security 0x04
+9 Param1 Code type 0x3F (all symbologies)
+0xA Param2 Decode options A 0x01
+0xB Param3 Decode options B 0x15
+0xC Param4 General params 0x06
+0xD Param5 ITF length 1 0x04
+0xE Param6 ITF length 2 0x1E
+0xF Param7 Preamble 0x80
+0x10 Param8 Postamble 1 0x0D (CR)
+0x11 Param9 Postamble 2 0x0A (LF)
+0x12 Param10 General decode 0x06

So decoded output is terminated by CR LF (0x0D 0x0A), and Code type 0x3F enables all symbologies (UPC included). A reader should assemble bytes until CR/LF and strip the preamble/postamble.

Still to confirm on-device

The int 0xCF calls pass their argument by value in DX; replicating this from C requires that p_iow(chan, func, arg) land arg in DX (or inline asm). The exact ordering of the config-byte writes and the two enable ops, and two helper subroutines (0xD6DC4, 0xD8DCB), still need tracing and — crucially — validation on the device, since none of this can be tested off-hardware. The clean alternative remains the Workabout MX C SDK SCANNER.DYL header, which SCANAPP was built against.

Error codes seen (from epocdefs.h)

Value Name Meaning
-9 E_GEN_INUSE device already open / in use
-32 E_FILE_EXIST (LDD) already loaded
-38 E_FILE_NAME invalid device name / unit
-41 E_FILE_DEVICE device / interface not present