By the time Squirrel started developing the new generation of its well-known POS, the then current version was quite successful. It is still selling very well today due to continuous upgrades and support. But it was a 16-bit program with a DOS-like, pseudo-graphics GUI, so the time came to move to modern 32-bit features and truly graphical environments.
Since the hospitality industry is customer-driven to a high degree, we had to address our customers' criteria for the new system. The criteria included a Windows NT-based SQL backend, fault tolerance, high performance (6,000+ transactions per launch), ability for the server to be located from terminals remotely, scalability, flexibility, open-system architecture (server PC could be used for other tasks), attractive GUI that is easy to learn and use, low-cost and long-lasting maintenance.
Figure 1. Squirrel POS Touchscreen
As you can see from the above list, the server's OS was quite defined. Picking a high-performance Microsoft SQL Server as the back end's database was also an easy choice, unlike the terminal's environment.
Traditionally, Squirrel POS ran on bare terminals; no OS of any sort was used. Although that worked well at the time, it didn't seem flexible enough to satisfy the new requirements. On the other hand, we realized that full-blown OSes like MS Windows, Solaris, etc., with their inflexibility and enormous thirst for resources, are not really suitable for the specialized hardware of the POS terminal. So our logical choice was to look at real-time operating systems (RTOSes), not because of their real-time capabilities but because of their modest resource requirements and high degree of customization. After a number of commercial OSes and available JVMs were evaluated, it became apparent that none of them satisfied all of our requirements. That's when Linux got its chance. Much to our surprise, the free OS proved to be our best choice. It is stable and reliable, with broad hardware support and moderate resource requirements. It also has excellent development tools, a knowledgeable community that is ready to answer questions, and most of all, it has flexibility.
Our choice of Java as the development language for the embedded targets seems pretty odd, considering its widely known shortcomings (specifically, thirst for resources, low performance and lack of supported platforms). But it is also a very attractive language for development. Due to its ``write once, run anywhere'' feature, development can start well before the actual terminal OS is chosen. Its rich, simple and well-thought-out API set allows us to write complicated GUIs fast and easily. Also, as you might know, long-lasting efforts in JVM development finally started to bear fruit; a number of companies recently released virtual machines with performance close to those written in C/C++. All this made us believe that Java's advantages outweigh its disadvantages.
Although Squirrel's terminal isn't exactly an embedded computer as we know it, it is pretty close. Its x86 motherboard is combined with a touchscreen LCD panel in one case. The terminal has no keyboard, hard drive, floppy drive or mouse. Some POS-specific peripherals include a badge reader, magnetic card reader, serial printer, customer display and cash drawer. For networking it uses an onboard Ethernet interface.
Squirrel POS architecture is a typical three-tier system (see Figure 2). The Squirrel client (the front end with which the user interacts--see Figure 2) runs on a terminal. It's written in Java and communicates with the Squirrel server via TCP/IP messages. The terminal is connected to the Server PC via Ethernet cable. Squirrel uses Microsoft's SQL Server as a database and Microsoft Windows NT as the server PC's OS.
Figure 2. Squirrel Software Diagram
Due to the significant costs of nonvolatile memory (Compact Flash, hard drives, Disk-On-Chip) at the time this project was conceived, the terminal had no storage devices, so almost all of the terminal's software, including Linux, X and POS, had to be loaded remotely from the server. The only piece left on the terminal was the boot PROM--network boot loader.
Unlike traditional diskless (or X) terminals that use UNIX servers and NFS to boot, the Squirrel terminal boots off the NT machine. It uses SMB to access its files located on NT's share. None of the off-the-shelf Linux distributions at the time allowed easy implementation of this booting scheme, so we developed our own embedded Linux distribution, Squirrel Linux. Although it was designed to run a Java application, it can run any X client.
I also found this distribution more convenient for Ethernet setup of floppyless POSes, the hard disk of which, for some reason, cannot be physically accessed (sealed case under warranty, etc.).
Squirrel Linux consists of two major components: bootable image and RemoteFS. Both of these are located on MS Windows' share.
Image (~1.5MB for the 2.0.36 kernel and ~3.5MB for 2.2.14) is a single file generated by the mknbi utility that comes with the Etherboot package (see Resources). It can include a minimal root filesystem (MRFS) as in my case (I had to boot Linux from the Windows server and the Linux kernel doesn't support SMBFS-Root, only NFS-Root). To generate an image the mkrootnet script can be used [see Listing 1 at ftp://ftp.linuxjournal.com/pub/elj/listings/issue05/4798.tgz].
Note: if RAM disk size RDSZ is set too small you'll see
"end request:I/O error, dev 01:00 ..."
when booting Linux, so increase RDSZ accordingly. The other fix is to change /usr/src/linux/drivers/ block/rd.c:rd_size, but that will require rebuilding the kernel.
I have used kernels 2.0.30, 2.0.36 and 2.2.14, but nothing prevents you from experimenting with other versions. Just make sure to include at least the following in the configuration (2.2.14):
CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_INITRD=y CONFIG_EXT2_FS=y CONFIG_SMB_FS=m
Don't forget to change root device after you've built the kernel (rdev vmlinuz /dev/rd).
Minimal root filesystem is required to get Linux up and running along with networking until it can mount the remote filesystem to run X/Java. After the image is loaded from the server, MRFS is decompressed into the RAM disk. If you can afford a lot of RAM on your terminal, the entire remote filesystem can be moved to MRFS, making your terminal more responsive.
Some folks find it easier to start from scratch, others use known minimal Linux distributions (Linux Router, tomsrtbt, etc.) and still others prefer to start from big Linuces like I did. Every path has its pluses and minuses.
Pruning standard distributions (Red Hat, Debian) to your needs might be very time consuming. To ease that painful process I used a remotely booted diskless client with NFS-Root (see Resources for Etherboot's README, NFS-Root and NFS-Root-Client mini-HOWTOs, Diskless-HOWTO).
To do this, first set up a minimal Red Hat install (networked workstation, X, no development, mail, etc.), and copy the entire filesystem to the NFS exported directory, /usr/NFS. Next, type
mknod /usr/NFS/dev/nfsroot b 0 255
Then, build the vmlinuz-nfs kernel according to the NFS-HOWTO (built-in BOOTP, RARP, NFS, NFS-root, NIC driver, RAM disk) and enter
rdev vmlinuz-nfs /dev/nfsrootto set the NFS root device. Build a bootable image for the NFS-root filesystem with
mknbi -d rom -i rom -k vmlinuz-nfs -o nfsImageThen, edit /usr/NFS/ to adjust startup scripts, etc., and boot the terminal while monitoring NFS file requests with snoop. Finally, copy files from /usr/NFS to /archive/ROOTFS (MRFS model) according to snoop's file list, and generate a new image using the mkrootnet script from Listing 1.
The above trick allows you to determine the sought file set and debug the boot process, analyzing NFS messages. I found it useful to put ``read tmp'' statements into init scripts for debugging. Tracking files up until you are logged in provides an MRFS that can be used to boot Linux from ROM (Flash, EPROM, Disk-On-Chip, SanDisk, etc.) as well. All the other files requested by the terminal during starting X, Java and Java application are placed into RemoteFS.
If you want to get the MRFS as small as possible, strip the symbols out of binaries and libraries before running the mkrootfs script. Also, it is convenient to keep generated-MRFS rootfs*.gz files as backups. Then to restore (say, for the sake of simplicity, MRFS is in /tmp/rootfs.gz) one could use the following restoremrfs script:
#!/bin/sh # restoremrfs: restores minimal root filesystem cd /tmp gunzip rootfs.gz mount -o loop -t ext2 /tmp/rootfs /mnt cd /mnt find . -print|cpio -pmd /archive/ROOTFS umount /mnt
I had to change the attributes of some directories and files (/etc/mtab, /etc/mtab~, /var/lock/subsys/*, /var/run/*, /dev/tty*, etc.) from the standard ones because the diskless client refused to work. For example, /dev/tty* ownerships had to be changed to 99:99 from the original 0:0 or 0:5 to get rid of
errmsg "INIT: Id "1" respawning too fast: disabled for 5 minutes"
RemoteFS is a collection of files located on Windows' share that is required by the terminal after booting completes and the remote filesystem mounts. In our case it was the X Window System, Java and the POS client binaries, libraries, images, data files, etc. For distribution purposes those files were compressed into RemoteFS.zip.
To use it on NT, RemoteFS.zip has to be unzipped into some directory. Share this directory read-only as ``usr'' (or some other name and pass this name to the terminal through the bootptab configuration file for the BOOTP server). Create an account for the terminal, and then grant access rights for this account to the shared directory.
Note that there are no symbolic links on NTFS, so UNIX links must be replaced by copies on NTFS. To determine potential troublemakers, one could use the following procedure. First, copy the required subset (according to snoop's intercept) from /archive/ROOTFS to /archive/REMOTEFS. Then mount some share from NTFS to /mnt via SMB. Finally, copy REMOTEFS to share:
[/archive/REMOTEFS]$find . -print|cpio -pmd /mnt 2>links
You will find names to work with in the links file.
If you think NFS/snoop is too complicated to set up in order to determine files to be included in RemoteFS, here is yet another trick I've used to find files required by X, Java and the POS client.
Start by disabling all unnecessary dæmons and type the following commands separately:
$find / -anewer tst>log.find.jre118In the file log.find.jre118 you'll find files accessed after tst was created.
Boot occurs when the bootprom sends a BOOTP request, and the BOOTP server responds with subnet mask, client's name, client's IP, TFTP server's IP, bootfile name and some optional parameters. Next, the bootprom downloads the image from the TFTP server. Then, the kernel starts and decompresses MRFS into RAM. The system starts init using RAM disk root, and RemoteFS gets mounted from NT via SMBFS. Finally, the xstart script located on RemoteFS (/usr/sbin) starts X, Java and the POS client.
The placement of the xstart script on RemoteFS proves to be very flexible, allowing us, therefore, to add debugging statements, change what programs start and their parameters, load additional modules, etc., without rebuilding the image.
You might find some of the configuration scripts I have used in MRFS and RemoteFS helpful, and they are available as Listings 2, 3 and 4 [at ftp://ftp.linuxjournal.com/pub/elj/listings/issue05/4798.tgz].
The smbmount client from the smbfs package, which used to mount remote Windows shares to local Linux directories in pre-2.2.x era, isn't maintained anymore, so you should use the one included with the Samba package. But be aware that binary smbmount might not work with 2.2.x, so you'll have to recompile it with 2.2.x headers, following Samba's README.
The Squirrel terminal uses bootprom from Ken Yap's Etherboot package (see Resources). Etherboot has excellent documentation that will tell you everything about bootprom. Normally you would have to put bootprom into the network adaptor's EPROM chip. If your hardware is like ours, however, it has an onboard NIC without an EPROM socket, and the BIOS is programmed in Flash. Therefore, you can reprogram it to add bootprom as a BIOS extension.
Here is what I did to add ne.rom (bootprom generated by Etherboot's makerom for an NE2000 clone) to the AMI BIOS on our Flash. I read Flash content by the programmer into the bios.bin binary file, used one of the available binary editors (see Resources) to add ne.rom to bios.bin and wrote the new bios.bin back to Flash.
Note that makerom generates bootprom for standard EPROM sizes (8K, 16K, 32K, etc.), so if you're tight on space use the -s flag to adjust the size, or cut it manually to multiples of 512-byte blocks. Don't forget to adjust the extension's length that is coded in byte 2 and checksum to 8 bits of zero. Valid absolute addresses for BIOS extensions are from 0xC8000 to 0xF4000 (check with your motherboard's manufacturer to see how Flash is mapped onto system memory space). Byte 0 must be 0x55, byte 1 must be 0xAA and byte 2 must be the extension's length in 512-byte blocks. Extension BIOS has to start at a 2K boundary.
Applications like POS and kiosks don't need cursors. Unfortunately, up until JDK 1.2 it was impossible to disable them in Java applications. To hide the cursor on an X-based system, start by downloading ftp.x.org/pub/R6.3/xc/fonts/bdf/misc/cursor.bdf. Edit cursor.bdf to change the bitmap strings in sections X_cursor, X_cursor_mask, left_ptr and left_ptr_mask to 0000. Then enter the following commands separately:
bdftopcf -o cursor.pcf cursor.bdf
cp cursor.pcf.gz /usr/X11R6/lib/X11/fonts/miscAlternatively, if you want to use default cursors from time to time, you can modify some other characters in cursor.bdf, mapped to one of the cursors available in JVM1.1. Then use
setCursor(new Cursor(Cursor.SOME_MODIFIED_CURSOR)) setCursor(new Cursor(Cursor.DEFAULT_CURSOR))to turn the cursor on or off at your will.
The above is more like a quick-and-dirty solution. If someone can offer a real one, I would appreciate it. Also, apparently in JDK 1.2 you can use createCustomCursor() to hide the cursor. It appears that unlike other JVMs, IBM JVM uses left_ptr as the default cursor instead of X_cursor.
As you might know, Java does not allow direct hardware control. On the other hand, POS systems often use nonstandard attachments of peripheral devices. For example, the Squirrel terminal has a printer connected to the serial port and cash drawers to the parallel port, so to get access to the hardware from Java one should use native libraries. Although there is a lot of good Java literature these days, I found the coverage of native methods is still very modest. The included Makefile (see Listing 5) may help you to understand how to build Java native libraries.
In this article I have attempted to walk the thin line between telling you as much as I know and keeping Squirrel's proprietary know-how undisclosed. I hope that the tips and tricks I've shared here will help you save some time and frustration and also avoid the numerous mistakes I've made.
I wish to thank Pierre Mondie who convinced me to write the Diskless-From-NT-Mini-HOWTO (not properly submitted but available at http://yahoo.com/ptkatch/disklessfromnt.htm), which this article is based upon. I'm also very much in debt to all those whose work and goodwill made this article possible, including Squirrel Systems of Canada; Ken Yap for his excellent Etherboot package, help and advice; Randy Chapman for his first Linux JVM; David Newall for his Win32 BOOTP and tftp servers; Jody Winston for helping me to complete the touchscreen device driver; and IBM for their high-performance JVM 1.1.8 for Linux.
Pavel Tkatchouk holds a PhD in Physics. He is currently employed as a senior analyst/developer at Squirrel Systems of Canada. He likes tennis, skiing, scuba diving and his family. You can send him e-mail at firstname.lastname@example.org.