Forth Lesson 23

Jump to: navigation, search
Mitch Bradley's Forth and
Open Firmware Lessons:

Random Recipes

This is a collection of "How to Do X" snippets. Feel free to add your favorites.

Changing the prompt

ok : si ." si " ;
ok ' si to (ok)

Undo with

si ' "ok" is (ok)
ok █

Adding status data to the prompt

ok : .voltage  uvolt@ .milli ." V "  ;
ok ' .voltage to status
6.74V ok █

XO-1.75 Heavy Load

This is a trick for running several XO-1.75 subsystems simultaneously, thus forcing a high power draw. The subsystems are audio, camera, and wireless LAN transmit.

 ok dev /camera
 ok patch false shoot-still selftest
 ok patch exit ?dup selftest
 ok dend
 ok test /camera
 ok select /audio
 ok patch 20 -9 tone
 ok patch abort install-playback-alarm start-audio-out
 ok d# 100 tone
 ok dend
 ok nb-tx: u:\os6.zd

It depends on the fact that both the camera and the audio use DMA to do most of the work, and they can run autonomously.

Forcing EC Code Update on XO-1.75 and XO-4

On XO-1.75 and XO-4, the Embedded Controller code is executed from FLASH internal to the EC (on previous XOs, the EC code was executed from the same SPI FLASH chip that contained the Open Firmware code). The OFW SPI FLASH contains a copy of the EC code, and if that copy is newer than the running EC code, OFW auto-updates the EC code by writing the new copy into the internal EC FLASH.

There are circumstances where the auto-update doesn't work the way you want it; for example you might have a test version that doesn't register as "newer" than the running version. Or you might want to force a reversion to an older version.

To explicitly perform the EC FLASH update:

  ok flash-ec rom:ecimage.bin

For safety, that command requires the presence of both battery and AC power. You can bypass that safety check with:

  ok flash-ec! rom:ecimage.bin

rom:ecimage.bin refers to the copy of the EC code that is stored inside the OFW SPI FLASH. More generally, "rom:" refers to a "filesystem" inside that SPI FLASH, containing numerous images that OFW needs from time to time.

The target of the flash-ec command can be any file that OFW can access. For example:

  ok flash-ec u:mynewec.bin                         \ From USB stick
  ok flash-ec http:\\\ecimage3.bin   \ From the web

Wireless Network Access from Open Firmware

OFW contains a network stack that supports numerous protocols including HTTP, Telnet, SMB (Windows File Sharing), TFTP, and NFS. On the XO, the network connection can be either the built-in wireless LAN or a plug-in USB Ethernet adapter. If a plug-in Ethernet adapter is present, it is used as the default network device, otherwise the wireless LAN is used.

Connecting to the Wireless LAN

You can list the wireless access points within range with:

  ok test /wlan
  RSSI: 49 SSID: OLPCOFW  Channel: 1
  RSSI: 49 SSID: 5247 1201  Channel: 1
  RSSI: 85 SSID: 09FX07016302  Channel: 6

By default, when you try to use the wireless LAN, OFW-on-XO tries to associate with an open access point named "OLPCOFW". If you want to perform wireless operations on a lot of XOs, you can set up such an access point to streamline subsequent operations.

You can manually change the association by specifying the ESSID of a different access point with:

  ok essid 09FX07016302

If your access point requires authentication, you must supply the password after establishing the essid. The command for setting the password depends on the type of security.

For the older "WEP" security:

  ok wep 00224488aa                   \ 40-bit/5-byte hex key format
  ok wep 00112233445566778899aabbcc   \ 128-bit/13-byte hex format
  ok " MyPwd" $wep                    \ 5-byte ASCII format
  ok " MyPassword123" $wep            \ 13-byte ASCII format

For the newer "WPA" and "WPA2" security:

  ok wpa MyPassword

After you have entered the ESSID and optionally the authentication information, OFW network commands will automatically associate with the access point as needed. You can also add these to a laptop using the NN and PP tags.

Setting Clock

You can use NTP to set the clock:

  ok essid mine
  ok ntp-set-clock

This searches for an NTP server by trying, in order:

  • the NTP server identified by the DHCP server,
  • the host name "time",
  • the address,
  • several host names in the "" collection.

The search can take a little while if the DHCP server has no NTP server configured. You can speed it up if you have an NTP server by typing in the address in a special command:

  ok : x  ""  ;  ' x to ntp-servers
  ok ntp-set-clock

Connecting to any Wireless LAN

Sometimes it might not be possible to know the ESSID in advance, yet you know it starts with a certain word. This happens in schools. The following code fragment can be used where the only access points that match the prefix school are those that can be used.

: prefix$  " school"  ;

d# 34 buffer: an-ssid
: an-ssid0  ( -- )        0 an-ssid c!  ;
: an-ssid!  ( ssid$ -- )  d# 32 min  an-ssid pack drop  ;
: an-ssid@  ( -- ssid$ )  an-ssid count  ;

dev /supplicant
: capture-ssid  ( ssid$ -- )
   \ test for hidden access point, first byte of essid is zero
   over c@  if          ( ssid$ )
      prefix$           ( ssid$ ssid$ prefix$ )
      rot drop comp     ( ssid$ comparison )
      0= if
         \ a match by prefix, capture the ssid for re-use
         2dup an-ssid!
   then                 ( ssid$ )
   type                 ( )
patch capture-ssid type .ie-short

scan-wifi an-ssid@ $essid

The code works by intercepting the scan results, testing each name, and when it matches storing the name for later use. Then the latest stored name is used.

Some OFW Network Commands

The following network commands work on both wireless and wired (via a USB Ethernet adapter) networks.

  ok ping
  Scan for: OLPCOFW found
  Associate with: OLPCOFW
  DHCP got
  4 ms

If your DHCP server reports the name server address, OFW can use it to resolve hostnames:

  ok ping
  Associate with: OLPCOFW
  112 ms

If ping hangs, and does not respond to any key, use the keyboard interrupt.

The "Scan for:" and "DHCP got" messages are only shown once; OFW caches the information after the first time, so subsequent operations are faster.

  ok more http:\\\index.htm
  (displays some HTML as ASCII)

The more command is not network-specific; it will display any file. This illustrates the fact that any OFW file-oriented command can apply to a network file by using the "http:\\SERVER\FILE" syntax. Note the use of backslashes (\) instead of forward slashes (/). This is an unfortunate consequence of OFWs use of forward slash to separate pathname components in the device tree.

  ok fload smb:\\USER:PASSWORD@SERVER\path\to\file.fth

The above shows an example of loading a Forth source code file from a Windows share (SMB server). You can also say "cifs" instead of "smb" - CIFS (Common Internet File System) and SMB (Server Message Block) are different names for the same thing.

OFW speaks NFS (Network File System) too:

  ok fload nfs:\\SERVER\path\to\file.fth

You can use TELNET to talk to OFW from another machine, or vice versa, see Firmware/Remote.

You can update OFW via the network:

  ok flash http:\\\pub\firmware\q4b09\q4b09.rom
  Reading http:\\\pub\firmware\q4b09\q4b09.rom
  Scan for: OLPCOFW found
  Associate with: OLPCOFW
  DHCP got
  Got firmware version: CL2   Q4B09  Q4B
  Checking integrity ...

Keeping the WLAN active

select ip


d# 1000 to #pings   ping

which pings an IP address for 1000 cycles.

Ping server


Listens for ICMP echo request packets and sends an echo reply. Press a key to stop.

Uses the destination IP address in the echo request for forming the echo reply. When a Linux peer is using "ping -b", the replies are not printed, because the source IP of the arriving packets is the broadcast address. To fix this, prepend this fragment:

\ respond to broadcast packets with our own ip address
: exchange-ips'
   'ip-src  'ip-dst  4  exchange-bytes
   " my-ip-addr" $call-net 'ip-src 4 move
patch exchange-ips' exchange-ips echo-packet

Checking DHCP

1 to bootnet-debug

Displays data about negotiation.

Without a DHCP server

If a wireless access point does not provide a DHCP server, or if you wish to avoid using one, Open Firmware can be configured manually for an IP address. Exactly which address to use is up to you. Here's an example:

setenv ip-address
setenv ip-dns-server
setenv ip-netmask
setenv ip-router
setenv ip-domain
essid AP
Scan for: AP found
Associate with: AP


With a cooperative mail server, and preconfigured network, mail can be sent via SMTP, for example:

ok setenv smtp-server
ok setenv smtp-from-path user@host
ok setenv smtp-to-path user@host
ok setenv smtp-my-hostname host
ok " test message"r"n" sendmail
ok no-page collect( .dropins )collect sendmail


tcpdump and Wireshark compatible packet tracing support is available, and is very handy for network debugging of all kinds, for example:

ok capture u:\trace.cap   \ start a packet capture to a file on USB drive
ok [... some network action ...]
ok .capture               \ show capture status
ok stop-capture           \ stop the capture

The default packet capture length is 64 bytes. To increase to maximum, do this before capture:

ok dev /obp-tftp d# 1500 to capture-length dend

In Open Firmware prior to svn 3639 stop-capture is available in dev /obp-tftp.

md5sum of a small file

(the file must fit in memory)

: .md5 ( md5$ -- )
  bounds  ?do
     i c@  <# u# u# u#> type

: $md5sum-file  ( prefix$ filename$ -- )
  2dup $read-file abort" Read error" ( prefix$ filename$ adr len )
  2dup $md5digest1        ( prefix$ filename$ adr len md5$ )
  2swap free-mem          ( prefix$ filename$ md5$ )
  .md5                    ( prefix$ filename$ )
  ."  "                   ( prefix$ filename$ )
  2swap type              ( filename$ )
  type                    ( )

: md5sum  ( "file" -- )
  "  " safe-parse-word $md5sum-file

md5sum of a large file

XO-1.75 only.

Read a large file and calculate an MD5 checksum. The result should match the checksum displayed by the Linux md5sum command. Useful as a test of file content, and filesystem and device read performance. Is slower than the Linux equivalent, but by using the Open Firmware USB driver excludes Linux from a test.

: .md5 ( md5$ -- )
  bounds  ?do
     i c@  <# u# u# u#> type

h# 100000 value /buf
0 value ih
: md5sum-large ( "file" -- )
  $open-canonical-file to ih
  use-md5 hash-init
  /buf alloc-mem >r
  " size" ih $call-method drop         ( input-file-size )
  begin  ?dup  while                   ( #remaining )
     r@  /buf " read" ih $call-method  ( #remaining #read )
     ?dup  0=  if
        r> /buf free-mem true abort" short read"
     tuck -  r@ rot                    ( #remaining' adr #read )
     hash-update                       ( #remaining )
     [char] . emit
  r> /buf free-mem
  ih close-dev
  hash-final                           ( digest$ )
  .md5 cr

Example usage:

ok t-hms( md5sum-large u:\os29.zd4 )t-hms

read a large file

Read a large file and ignore it. Useful as a test of filesystem and device read performance.

h# 100000 value /buf
0 value ih
: read-a-whole-file ( "file" -- )
  $open-canonical-file to ih
  /buf alloc-mem >r
  " size" ih $call-method drop         ( input-file-size )
  begin  ?dup  while                   ( #remaining )
     r@  /buf " read" ih $call-method  ( #remaining #read )
     -                                 ( #remaining' )
     [char] . emit
  r> /buf free-mem
  ih close-dev

Example usage:

ok t-hms( read-a-whole-file u:\fs.zd )t-hms

read a .zd file

Read a .zd file without inflating or writing to internal storage. Gives the time spent reading the file. Useful for estimating the cost of inflation and internal storage writes during an fs-update.

: zblocks:  get-hex# get-hex# 2drop  ;
: zblocks-end:  ;
: data:  safe-parse-word 2drop  ;
: zblock:
  get-hex# drop  get-hex#   ( comprlen )
  safe-parse-word 2drop  safe-parse-word 2drop     ( comprlen )
  1+  >r  load-base r@ source-id fgets r> <> abort" short read"
: fs-scan ( "file" -- )
  safe-parse-word r/o open-file abort" can't open"  ( fd )
  linefeed over force-line-delimiter  ( fd )
  t-hms(  ['] include-file catch  )t-hms  throw

Example usage:

ok fs-scan u:\fs.zd
ok █

(Total fs-update time was 5:05, time with null-fsdisk was 4:36, time with fs-scan was 01:45, therefore 2:51 is due to inflation of the compressed data.)

erase a device

Write all over a USB drive, erasing what is there.

d# 512 constant /block
d# 512 buffer: block
block /block ff fill
0 value ih
" u:0" open-dev to ih
: wa
   " size" ih $call-method /block um/mod nip
   dup .d cr 0 do
      i .d (cr
      block /block " write" ih $call-method drop
ih close-dev

See also How to quickly erase everything for microSD, SD cards, and eMMC storage.

using an apple superdrive

The Apple USB SuperDrive requires a vendor-specific SCSI command before it will accept a disk into the mechanism. This prevents use in automatic booting of Open Firmware, unless we fix it. In the meanwhile, manual use can be achieved like this:

ok p2
USB devices:
ok select /usb@d4208000/hub@0,0/scsi@2,0
ok show-children
  Unit 0   Removable Read Only device    Apple   SuperDrive      2.00
ok create mbasd-cmd  h# ea c, 0 c, 0 c,  0 c, 0 c, 0 c,  1 c,
ok 0 0 -1 mbasd-cmd h# 7 -1 retry-command? . .
0 0 
ok show-children                                             
  Unit 0   Removable Read Only device    HL-DT-STDVDRW  GX40N    RQ00
ok unselect
ok dir u:\

In Linux the command required is:

sudo yum install sg3_utils
sudo sg_raw /dev/sr0 ea 00 00 00 00 00 01


Next Lesson: Device Tree Hacking