Forth Lesson 23
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) si
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
On XO-1.75, 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:\\192.168.200.200\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.
For XO-1.75, authenticated WiFi access is broken on OFW releases Q4C08 and earlier. It works on Q4C09 and later.
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$ ) 2dup 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 then ( ssid$ ) type ( ) ; patch capture-ssid type .ie-short dend 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 192.168.200.1 Scan for: OLPCOFW found Associate with: OLPCOFW DHCP got 192.168.200.22 4 ms
If your DHCP server reports the name server address, OFW can use it to resolve hostnames:
ok ping dev.laptop.org 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:\\firmworks.com\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:
ok telnetd Associate with: OLPCOFW my-args = last Initial configuration: Using the previous configuration telnet://192.168.200.22
Now run a telnet program on another machine to connect to OFW on the XO.
$ telnet 192.168.200.22 Trying 192.168.200.22... Connected to 192.168.200.22. Escape character is '^]' ok here . fdae68dc ^] telnet> quit Connection closed
(If telnetd hangs before accepting a connection from a client, use the keyboard interrupt. Scripts that use telnetd can exit from it using exit-telnet. Scripts that output much data should have a keyboard check to ensure data is regularly flushed. Use key? drop or something similar.)
You can update OFW via the network:
ok flash http:\\dev.laptop.org\pub\firmware\q4b09\q4b09.rom Reading http:\\dev.laptop.org\pub\firmware\q4b09\q4b09.rom Scan for: OLPCOFW found Associate with: OLPCOFW DHCP got 192.168.200.22 Got firmware version: CL2 Q4B09 Q4B Checking integrity ... (etc)
Keeping the WLAN active
d# 1000 to #pings ping 10.0.0.1
which pings an IP address 10.0.0.1 for 1000 cycles.
1 to bootnet-debug ping 10.0.0.1
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 10.0.0.250 setenv ip-dns-server 220.127.116.11 setenv ip-netmask 255.255.255.0 setenv ip-router 10.0.0.84 setenv ip-domain mit.edu essid AP telnetd Scan for: AP found Associate with: AP telnet://10.0.0.250
md5sum of a small file
(the file must fit in memory)
: .md5 ( md5$ -- ) push-hex bounds ?do i c@ <# u# u# u#> type loop pop-base ; : $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 ( ) cr ; : md5sum ( "file" -- ) " " safe-parse-word $md5sum-file ;
md5sum of a large file
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$ -- ) push-hex bounds ?do i c@ <# u# u# u#> type loop pop-base ; h# 100000 value /buf 0 value ih : md5sum-large ( "file" -- ) safe-parse-word $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" then tuck - r@ rot ( #remaining' adr #read ) hash-update ( #remaining ) [char] . emit repeat cr r> /buf free-mem ih close-dev hash-final ( digest$ ) .md5 cr ;
ok t-hms( md5sum-large u:\os29.zd4 )t-hms .... 00:03:04
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" -- ) safe-parse-word $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 repeat cr r> /buf free-mem ih close-dev ;
ok t-hms( read-a-whole-file u:\fs.zd )t-hms .... 00:01:26
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 ;
ok fs-scan u:\fs.zd 00:01:45 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.)