changeset 0:e7502631a0f9

initial import from freecalypso-sw rev 1033:5ab737ac3ad7
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 11 Jun 2016 00:13:35 +0000 (2016-06-11)
parents
children 22c6f5bd7ffa
files Makefile README doc/Compal-unlock doc/Compiling doc/High-speed-serial doc/Host-tools-overview doc/RVTMUX doc/TFC139-breakin doc/TIFFS-Overview doc/TIFFS-old-description doc/linux-2.6.37.6-ftdi_sio.c.patch ffstools/Makefile ffstools/README ffstools/Usage ffstools/tiffs-rd/Makefile ffstools/tiffs-rd/basics.c ffstools/tiffs-rd/cat.c ffstools/tiffs-rd/globals.c ffstools/tiffs-rd/globals.h ffstools/tiffs-rd/inode.c ffstools/tiffs-rd/ls.c ffstools/tiffs-rd/main.c ffstools/tiffs-rd/object.c ffstools/tiffs-rd/pathname.h ffstools/tiffs-rd/struct.h ffstools/tiffs-rd/tree.c ffstools/tiffs-rd/types.h ffstools/tiffs-rd/xtr.c ffstools/tiffs-wrappers/Makefile ffstools/tiffs-wrappers/installpath.c ffstools/tiffs-wrappers/mokoffs.c ffstools/tiffs-wrappers/pirffs.c lcdemu/Makefile lcdemu/globals.c lcdemu/globals.h lcdemu/main.c lcdemu/process.c lcdemu/window.c lcdemu/ximage.c lcdemu/xrm.c loadtools/CHANGES loadtools/Makefile loadtools/README loadtools/README.old loadtools/baudrate.h loadtools/chainload.c loadtools/clmain.c loadtools/compaldummy.c loadtools/compalload.c loadtools/compalram.c loadtools/crc32tab.c loadtools/defpath.c loadtools/flash.h loadtools/flashops.c loadtools/flcmplboot.c loadtools/flmain.c loadtools/flmisc.c loadtools/flprogbin.c loadtools/flprogsrec.c loadtools/flutil.c loadtools/gta-ap-build.sed loadtools/gtapower.c loadtools/hexdecode.c loadtools/hwparam.c loadtools/hwparamstubs.c loadtools/initscript.c loadtools/install-helpers.sh loadtools/labaud.c loadtools/loadtool.help loadtools/ltdispatch.c loadtools/ltdump.c loadtools/ltexit.c loadtools/lthelp.c loadtools/ltmain.c loadtools/ltmisc.c loadtools/ltpassthru.c loadtools/ltscript.c loadtools/romload.c loadtools/scripts/c155.config loadtools/scripts/c155.init loadtools/scripts/compal.config loadtools/scripts/compal.init loadtools/scripts/cs2-4ws-8mb.init loadtools/scripts/dsample.config loadtools/scripts/fcfam.config loadtools/scripts/gta02.config loadtools/scripts/k5a3281.init loadtools/scripts/pirelli.config loadtools/scripts/pirelli.init loadtools/sercomm.c loadtools/sertool.c loadtools/srecreader.c loadtools/srecreader.h loadtools/tpinterf.c loadtools/tpinterf2.c loadtools/tpinterf3.c loadtools/ttypassthru.c miscutil/Makefile miscutil/fc-rgbconv.c miscutil/fc-serterm.c miscutil/imei-luhn.c miscutil/openport.c rvinterf/Makefile rvinterf/asyncshell/Makefile rvinterf/asyncshell/at.c rvinterf/asyncshell/gsm0610.c rvinterf/asyncshell/init.c rvinterf/asyncshell/main.c rvinterf/asyncshell/oneshot.c rvinterf/asyncshell/parse.c rvinterf/asyncshell/pktsort.c rvinterf/asyncshell/poweroff.c rvinterf/asyncshell/rxctl.c rvinterf/asyncshell/sendarb.c rvinterf/asyncshell/sendsp.c rvinterf/asyncshell/tchcmd.c rvinterf/asyncshell/tchplay.c rvinterf/asyncshell/tchrec.c rvinterf/asyncshell/usercmd.c rvinterf/ctracedec/Makefile rvinterf/ctracedec/decode.c rvinterf/ctracedec/doprnt.c rvinterf/ctracedec/main.c rvinterf/ctracedec/processlog.c rvinterf/ctracedec/readtab.c rvinterf/doc/README.old rvinterf/doc/README.older rvinterf/doc/rvinterf.usage rvinterf/doc/rvtdump.usage rvinterf/doc/tfc139.usage rvinterf/etmsync/Makefile rvinterf/etmsync/cmdtab.h rvinterf/etmsync/connect.c rvinterf/etmsync/dispatch.c rvinterf/etmsync/dspapidump.c rvinterf/etmsync/exitcodes.h rvinterf/etmsync/fdcmd.c rvinterf/etmsync/fileio.c rvinterf/etmsync/fsbasics.c rvinterf/etmsync/fscmdtab.c rvinterf/etmsync/fserr.c rvinterf/etmsync/fsiomain.c rvinterf/etmsync/fsmisc.c rvinterf/etmsync/fspath.c rvinterf/etmsync/fsread.c rvinterf/etmsync/fsupload.c rvinterf/etmsync/fswrite.c rvinterf/etmsync/interf.c rvinterf/etmsync/launchrvif.c rvinterf/etmsync/localstruct.h rvinterf/etmsync/memcmd.c rvinterf/etmsync/memops.c rvinterf/etmsync/olddump.c rvinterf/etmsync/pirhackinit.c rvinterf/etmsync/pirimei.c rvinterf/etmsync/pirimeimain.c rvinterf/etmsync/rfcap.c rvinterf/etmsync/simplemain.c rvinterf/etmsync/stddirs.c rvinterf/etmsync/symlink.c rvinterf/include/etm.h rvinterf/include/ffs.h rvinterf/include/ffserr.h rvinterf/include/ffslimits.h rvinterf/include/limits.h rvinterf/include/localsock.h rvinterf/include/localtypes.h rvinterf/include/pktmux.h rvinterf/include/tch_feature.h rvinterf/include/tm3.h rvinterf/include/tmffs1.h rvinterf/include/tmffs2.h rvinterf/libasync/Makefile rvinterf/libasync/README rvinterf/libasync/init.c rvinterf/libasync/interf.c rvinterf/libasync/launchrvif.c rvinterf/libasync/rvtrace.c rvinterf/libasync/ttymagic.c rvinterf/libg23/Makefile rvinterf/libg23/README rvinterf/libg23/fmtdispatch.c rvinterf/libg23/fmtfunc.c rvinterf/lowlevel/Makefile rvinterf/lowlevel/client.h rvinterf/lowlevel/clientcmd.c rvinterf/lowlevel/format.c rvinterf/lowlevel/format_fc.c rvinterf/lowlevel/localsock.c rvinterf/lowlevel/logsent.c rvinterf/lowlevel/openport.c rvinterf/lowlevel/output.c rvinterf/lowlevel/packetrx.c rvinterf/lowlevel/packettx.c rvinterf/lowlevel/pktfwd.c rvinterf/lowlevel/rviflcd.c rvinterf/lowlevel/rvifmain.c rvinterf/lowlevel/rvtdump.c rvinterf/lowlevel/tfc139.c rvinterf/old/before-rvinterf/Makefile rvinterf/old/before-rvinterf/etmsend.c rvinterf/old/before-rvinterf/log.c rvinterf/old/before-rvinterf/openport.c rvinterf/old/before-rvinterf/packetrx.c rvinterf/old/before-rvinterf/packettx.c rvinterf/old/before-rvinterf/pktmux.h rvinterf/old/before-rvinterf/rvtdump.c rvinterf/old/before-rvinterf/rvtdump_tx.c rvinterf/old/before-rvinterf/trdump.c rvinterf/old/before-rvinterf/txpkt.h rvinterf/old/format_g23.c rvinterf/old/g23sh/Makefile rvinterf/old/g23sh/init.c rvinterf/old/g23sh/main.c rvinterf/old/g23sh/pktsort.c rvinterf/old/g23sh/sendsp.c rvinterf/old/g23sh/usercmd.c rvinterf/old/sendsp/Makefile rvinterf/old/sendsp/rvifsend.c rvinterf/old/sendsp/sendsp.c rvinterf/tmsh/Makefile rvinterf/tmsh/abb.c rvinterf/tmsh/etmbasic.c rvinterf/tmsh/ffs2.c rvinterf/tmsh/ffs2resp.c rvinterf/tmsh/ffs2wr.c rvinterf/tmsh/init.c rvinterf/tmsh/main.c rvinterf/tmsh/misc.c rvinterf/tmsh/omr.c rvinterf/tmsh/pktsort.c rvinterf/tmsh/tmcore.c rvinterf/tmsh/usercmd.c target-utils/Makefile target-utils/README target-utils/c139-lldbg/Makefile target-utils/c139-lldbg/cmdtab.c target-utils/c139-lldbg/entry.S target-utils/c139-lldbg/entryinfo.c target-utils/c139-lldbg/lldbg.lds target-utils/c139-lldbg/main.c target-utils/c139-lldbg/mygetchar.c target-utils/c139-lldbg/uartbase.S target-utils/c139explore/Makefile target-utils/c139explore/backlight.c target-utils/c139explore/cmdtab.c target-utils/c139explore/lcd.c target-utils/c139explore/main.c target-utils/c139explore/mygetchar.c target-utils/c139explore/uartbase.S target-utils/c139explore/uwire.c target-utils/compalstage/Makefile target-utils/compalstage/README target-utils/compalstage/compalstage.S target-utils/env/compalram.lds target-utils/env/crt0.S target-utils/env/iram.lds target-utils/helloapp/Makefile target-utils/helloapp/cmdtab.c target-utils/helloapp/main.c target-utils/helloapp/mygetchar.c target-utils/include/abbdefs.h target-utils/include/cmdtab.h target-utils/include/halt.h target-utils/include/ns16550.h target-utils/include/romvars.h target-utils/include/rtc.h target-utils/include/types.h target-utils/libbase/Makefile target-utils/libbase/abbdrv.c target-utils/libbase/osmodelay.S target-utils/libbase/serio.S target-utils/libbase/spidrv.c target-utils/libcommon/Makefile target-utils/libcommon/abbcmd.c target-utils/libcommon/cmd_baud_switch.c target-utils/libcommon/cmd_dieid.c target-utils/libcommon/cmd_jump.c target-utils/libcommon/cmd_memdump_human.c target-utils/libcommon/cmd_memdump_machine.c target-utils/libcommon/cmd_r16.c target-utils/libcommon/cmd_r32.c target-utils/libcommon/cmd_r8.c target-utils/libcommon/cmd_w16.c target-utils/libcommon/cmd_w32.c target-utils/libcommon/cmd_w8.c target-utils/libcommon/cmdentry.c target-utils/libcommon/dispatch.c target-utils/libcommon/hexarg.c target-utils/libcommon/parseargs.c target-utils/libcommon/uartsel.c target-utils/libload/Makefile target-utils/libload/amdflash.c target-utils/libload/cmd_blankchk.c target-utils/libload/cmd_crc32.c target-utils/libload/cmd_memload.c target-utils/libload/hexstrings.c target-utils/libload/intelflash.c target-utils/libprintf/Makefile target-utils/libprintf/README target-utils/libprintf/doprnt.c target-utils/libprintf/printf.c target-utils/libprintf/putchar.c target-utils/libprintf/puts.c target-utils/libprintf/sprintf.c target-utils/libprintf/sprintf_putchar.c target-utils/libprintf/vprintf.c target-utils/libprintf/vsprintf.c target-utils/libtiffs/Makefile target-utils/libtiffs/basicfind.c target-utils/libtiffs/cmd_find.c target-utils/libtiffs/findfile.c target-utils/libtiffs/globals.c target-utils/libtiffs/globals.h target-utils/libtiffs/init.c target-utils/libtiffs/macros.h target-utils/libtiffs/rdinmem.c target-utils/libtiffs/struct.h target-utils/loadagent/Makefile target-utils/loadagent/cmdtab.c target-utils/loadagent/main.c target-utils/loadagent/mygetchar.c target-utils/pirexplore/Makefile target-utils/pirexplore/cmdtab.c target-utils/pirexplore/ffsparam.c target-utils/pirexplore/flashid.c target-utils/pirexplore/lcd.c target-utils/pirexplore/main.c target-utils/pirexplore/mygetchar.c target-utils/pirexplore/rtc.c target-utils/tf-breakin/Makefile target-utils/tf-breakin/mkembed.c target-utils/tf-breakin/payload.S toolchain/binutils-patches/elf32-arm.patch toolchain/build+install.sh toolchain/clean.sh toolchain/t-arm-elf
diffstat 337 files changed, 27054 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,15 @@
+SUBDIR=	ffstools lcdemu loadtools miscutil rvinterf
+
+all:	${SUBDIR}
+
+${SUBDIR}: FRC
+	cd $@; ${MAKE} ${MFLAGS}
+
+clean: FRC
+	rm -f a.out core errs
+	for i in ${SUBDIR}; do (cd $$i; ${MAKE} ${MFLAGS} clean); done
+
+install: FRC
+	for i in ${SUBDIR}; do (cd $$i; ${MAKE} ${MFLAGS} install); done
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,49 @@
+You are looking at the top level of the FreeCalypso host tools package.  All
+tools in this package have been written to run on a Unix-based or Unix-like
+host system, such as a GNU/Linux PC or laptop, with the expectation that the
+user will compile them from the source using her regular system C compiler.
+
+Most of these tools interface to and operate on Calypso-based GSM devices,
+while a few perform some ancillary functions.  Please see
+doc/Host-tools-overview for the listing of what tools are available and what
+they do.  These tools are built in the following source directories:
+
+ffstools	tiffs, mokoffs and pirffs are built here.
+
+loadtools	fc-loadtool, fc-iram, fc-xram and fc-compalram form the part of
+		FC host tools called loadtools, which used to be its own
+		package.  In common with the rest of FC host tools, loadtools
+		run on a PC or whatever host system, but they also require two
+		target-side components called loadagent (for all targets) and
+		compalstage (for Compal phones only).  If you are working with
+		a packaged release of FC host tools, as opposed to a random
+		snapshot of the source tree, precompiled binaries for loadagent
+		and compalstage will be included under
+		loadtools/target-binaries.
+
+lcdemu		fc-lcdemu is built here.
+
+miscutil	fc-rgbconv, fc-serterm and imei-luhn are built here.
+
+rvinterf	Everything dealing with the RVTMUX interface to running GSM
+		firmwares and everything based on the rvinterf framework is
+		built under rvinterf.
+
+		The tfc139 utility, which logically belongs with loadtools, is
+		built in the rvinterf subtree because it is built from mostly
+		the same source components as rvtdump and rvinterf.
+
+The 5 directories listed above contain all of FC host tools; they are all you
+need in order to get a fully working installation of these tools if you are
+using the provided precompiled binaries for loadagent and compalstage.  However,
+if you would like to recompile these components from source, you will need the
+following additional stuff:
+
+target-utils	The source for loadagent and compalstage lives here, along with
+		a few extra target utilities that are of interest only to
+		developers.
+
+toolchain	Scripts and patches for building the gcc+binutils toolchain
+		targeting ARM7, the CPU core of the Calypso GSM/GPRS baseband
+		processor.  You'll need to build and install this toolchain
+		first before you can build target-utils.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/Compal-unlock	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,389 @@
+Using FreeCalypso tools to unlock Motorola C1xx phones
+======================================================
+
+The ultimate goal of the FreeCalypso project is to produce our own complete GSM
+dumbphone firmware which We the People fully own, control and compile from
+source ourselves, running at first on some selected pre-existing hardware
+targets, and then ultimately on our own Free Dumb Phone hardware.  While that
+goal is still far past the visible horizon, what can we do in the meantime to
+make our current forced use of existing proprietary dumbphone firmwares a
+little more tolerable?  This article presents one such hack: using FreeCalypso
+loadtools to dump the flash content of Compal phones for analysis, including
+TIFFS, and to replace one existing proprietary fw version with another, e.g.,
+to remove carrier branding and the associated SIM restriction.
+
+Serial access
+
+Mot C1xx (Compal) phones have a 2.5 mm headset jack that dual-functions as a
+debug/programming serial port.  In hardware terms, there is an electrically
+controlled switch (MUX) inside that switches the external jack between the
+analog headset signals and the digital serial ones; this switch is controlled
+by a GPIO signal from the Calypso.  The hardware power-up state of this switch
+is serial; Mot/Compal's standard fw switches it to headset upon boot, but the
+serial setting persists long enough to use it to break into the bootloader.
+
+Bootloader
+
+The Calypso DBB (digital baseband) chip used in these phones has an on-chip
+boot ROM, but it also has a hardware pin that enables or disables this boot
+ROM, and unfortunately these phones have it disabled.  If the boot ROM were
+enabled in hardware, it would provide an unstoppable and unbrickable way to
+take control of the device through the externally-accessible serial port like
+we have on Openmoko and Pirelli phones, but unfortunately the hardware we have
+available is not wired that way.
+
+However, Mot/Compal's standard firmware on these phones includes a bootloader,
+a part that executes before any of the rest of the fw image is allowed to
+execute or is made use of in any way, and this Compal-specific bootloader has a
+provision for interrupting the boot process and diverting it to an externally-
+supplied piece of code loaded over the serial line.  Older fw versions have
+this feature enabled unconditionally, but some of the newer versions have a
+malfeature whereby the serial boot interrupt and code download possibility may
+be disabled.  Some C1xx phones out in the wild, particularly all North American
+C139s with TracFone branding and some of the Cingular-branded ones as well,
+have such maliciously-locked firmware in them.
+
+Fortunately though, these maliciously-locked firmwares (or at least all versions
+we've encountered so far) have been found to have another hole through which we
+can break in, as described in the TFC139-breakin article.  We can exploit this
+hole in the firmware to gain code execution access to the Calypso, and then use
+the latter to reprogram the flash, replacing the ultra-malicious firmware with
+some other version that, although still proprietary, is a little less evil.
+
+Making first contact
+====================
+
+If you have a C1xx phone which you are seeking to free, your first step should
+be to try breaking in with fc-loadtool, using the Compal bootloader method.
+With the phone powered off, but containing a charged battery (SIM present or
+absent, doesn't matter), proceed as follows:
+
+1. Connect the serial or USB-serial cable between your PC or other host and the
+   target phone's headset jack.
+
+2. On the host end, run fc-loadtool like this:
+
+C11x/123: fc-loadtool -h compal /dev/ttyXXX
+C139/140: fc-loadtool -h compal -c 1003 /dev/ttyXXX
+C155/156: fc-loadtool -h c155 /dev/ttyXXX
+
+3. Press the power button on the phone.  A momentary press is sufficient and
+   recommended: the hardware powers up and causes the boot code to run exactly
+   the same whether the power button is pressed momentarily or held down.
+
+   Normal phone power-up requires the button to be held down because the
+   standard firmware does a check fairly late in the boot process to see if the
+   power button is still held down, and commands the hardware (the ABB) to
+   power off if it is not - it is a standard feature to prevent phones from
+   turning themselves on inadvertently from accidental momentary presses of
+   that button.  But if the goal is to cause the boot code to run, but not to
+   boot the regular fw all the way, a momentary press is ideal.
+
+If your phone has a bootloader without the malicious lock in it, the above
+procedure should result in fc-loadtool gaining full access to the target and
+landing you at a loadtool> prompt.  You can dump the flash content and analyse
+it, etc.  If you would like to change to a different fw version (to remove the
+SIM lock / carrier branding or for any other reason), see the corresponding
+later section of this article.
+
+Alternative method
+==================
+
+If the above procedure fails to gain access to the Calypso because the boot
+code in the phone never offers a serial download opportunity, the alternate
+break-in method should be tried, going through the full running firmware
+instead of just the bootloader part thereof.  Proceed as follows:
+
+1. Remove the SIM (if there was one to begin with) and put the charged battery
+   back in.  Charge the battery if necessary, using the standard charging
+   function of the existing fw.
+
+2. Power the phone up for normal boot: hold the power button down like a
+   regular user would, without fc-loadtool or other serial break-in tools.
+   The fw will boot up, notice the lack of a SIM, and the display will read
+   "SIM card absent" or something to that effect, depending on the fw version.
+
+3. Key in this magic sequence: **16379#.  A hidden "Trace Switch" menu should
+   appear, with the choices being "Trace On" and "Earphone".  Select "Trace On".
+   The electrically controlled hardware switch mentioned earlier in this article
+   should now be set back to the UART, bringing the latter out to the headset
+   jack.  Because Mot/Compal's firmware is based on TI's reference architecture,
+   the interface presented by the running fw on this serial port is TI's RVTMUX,
+   albeit at 57600 baud instead of TI's default of 115200.
+
+4. Connect the headset jack serial cable if it wasn't already connected, and
+   run this FreeCalypso utility:
+
+   tfc139 /dev/ttyXXX
+
+(The name tfc139 is historical; the current version is expected to work with
+ all Mot C1xx firmwares.)
+
+Compal's TI-based firmware implements some of TI's Test Mode commands, and one
+of these commands is a raw memory write.  It also implements some of TI's GPF
+"system primitive" commands, including the MEMCHECK command that causes the
+firmware to report some info on all running GPF tasks, including the location
+of each task's stack.  Our tfc139 utility will try to break into the phone
+(gain code execution access) by querying the target fw for the location of the
+L1A task's stack, and then using Test Mode memory write commands to write a
+piece of shellcode into an unused RAM location and to make this code execute by
+overwriting a function return address on the stack of the L1A task that
+processes these Test Mode commands.
+
+If the stack smashing hack succeeds, the shellcode injected by tfc139 will send
+a message out the serial port indicating this success, and then re-enable the
+Calypso boot ROM and jump to it.  Once the boot ROM code gains control, it will
+wait forever for a serial code download following its standard protocol.  If
+tfc139 gets the success indication from the target, it will announce this
+success and direct you to run:
+
+fc-loadtool -h compal -c none /dev/ttyXXX
+
+Do as it says.  The -c none option tells fc-loadtool to skip compalstage and
+proceed directly to feeding loadagent to the Calypso boot ROM.  You should now
+be in full control of the phone via fc-loadtool.
+
+There is one additional quirk worth mentioning.  It appears that Mot/Compal's
+main fw keeps resetting the RTC alarm registers in the Calypso DBB as it runs,
+always keeping the alarm time in the near future relative to the current time.
+When one breaks into this firmware with tfc139 and takes over the control of
+the device with fc-loadtool, this alarm time will almost certainly be reached,
+and the RTC alarm will go off.  This alarm has no effect on loadtool operation
+(i.e., it cannot reset the CPU or otherwise wrestle control away from loadtool,
+so it doesn't add any bricking risk), but it has one quite surprising effect
+upon exit, i.e., when you are done with your loadtool session and give it the
+exit command.
+
+Loadtool's configured default exit action for this target is to send a power-off
+command to the Iota ABB, leaving the device cleanly powered off.  However, if
+the RTC alarm has gone off previously during the session, the ABB will instantly
+power the phone back on, and put it through a new boot cycle.  The firmware
+handles this special form of boot rather oddly: it proceeds to the same end
+state it would have reached via a normal power button hold-down boot (powered
+on with the "Insert SIM" message on the LCD), but it reaches this state almost
+instantly, without going through the power-on LCD logo and buzz phase.  Odd,
+but harmless.  This explanation has been included to save other hackers the
+hours of bewildered head-scratching I spent chasing this quirk down.
+
+Dumping and reloading flash
+===========================
+
+Once you break in with fc-loadtool (either through the bootloader or through
+tfc139), the first step you should do is make a dump (backup) of the flash:
+
+loadtool> flash dump2bin flashdump.bin
+
+Before you do any flash write (erase or program) operations, please realise
+that these phones are brickable.  Because the Calypso boot ROM is disabled at
+the board level (Calypso DBB's nIBOOT configuration input is tied high directly
+underneath the BGA package!), when the phone powers up, the ARM7 core starts
+executing instructions directly out of the flash, from address 0.  Therefore,
+flash sector 0 must contain good working boot code (one that allows serial code
+download access for recovery) at all times.  If you erase this sector or fill
+it with some garbage (anything other than good working boot code) and then power
+the phone off or otherwise lose control of it, the phone will be unrecoverably
+bricked!
+
+On most C1xx models there seems to be no way to access the Calypso's JTAG
+signals, hence no possibility of using JTAG to unbrick a bricked phone.  And
+because the flash chip is a micro-BGA, it is quite unlikely that one could
+successfully desolder it, program it in a standalone flash chip programmer,
+and then put it back on the board.  Thus if you brick your C1xx phone, then
+most likely it is truly toast.  You've been warned!
+
+That being said, if your phone came with a maliciously locked bootloader, such
+that you had to use tfc139 to break in, then replacing that bootloader with a
+non-malware version is pretty much a necessity, and taking the chance of
+bricking the phone becomes a necessary risk.  Even if the bootloader version in
+your C1xx is free of the locking malfeature, if you need to reflash the main fw
+to a different version, one still needs to erase and reprogram the dangerous
+sector: on C11x/123 and C139/140 the main fw image starts at 0x2000, but the
+erase block boundary doesn't come until 0x10000.
+
+The good news, however, is that fc-loadtool has special support for rewriting
+the boot sector on Compal phones with minimal risk of bricking.  The command is:
+
+flash erase-program-boot binfile [length]
+
+The first argument is the name of the file (in straight binary format)
+containing the new boot code; the second argument (always interpreted as hex)
+is the number of bytes to program, always starting at 0.  If only one argument
+is given, the length of the file is used instead, which must not exceed the
+length of flash sector 0: 64 KiB on C11x/123 and C139/140, or 8 KiB on C155/156.
+
+This special command minimizes the bricking vulnerability window by loading the
+entirety of the new boot code to be programmed into a scratchpad RAM buffer on
+the target first (no problem because it's 64 KiB max), then commanding loadagent
+(the code that actually runs on the Calypso when you use fc-loadtool) to perform
+the "atomic" operation of erasing flash sector 0, then immediately reprogramming
+it with the bits that are already in scratchpad RAM on the phone.
+
+With this approach the phone will only be bricked if the battery dies or is
+physically yanked out of the phone in the time window between the beginning of
+the erase operation and the last critical bit of the new boot code being
+programmed - on the order of a second or two, or if the flash operations fail
+for some reason.  However, the phone will *not* be bricked with this approach
+if the serial connection between fc-loadtool or the target gets broken during
+the window in question, or if the host machine running fc-loadtool crashes: no
+flash operations start until loadtool gives the go-ahead command to loadagent,
+and once loadagent receives the latter command, it will proceed till completion
+without caring if loadtool is still there or not.
+
+Of course the conventional flash erase and flash program-bin commands will be
+happy to operate on flash sector 0 just like any other sector, but doing so is
+NOT recommended, as the window of vulnerability for bricking would then be
+considerably greater.
+
+Unlocked firmware for C139
+==========================
+
+If your phone is a North American (1900+850 MHz) C139, and you are reading this
+article because it came with Cingular or TracFone branding, whereas you would
+like to use it with SIMs and networks of your own choosing instead, you've come
+to the right place.  We have an unlocked and non-carrier-branded (Mot branding
+only) version of the fw that runs on these phones, and you can use FreeCalypso
+loadtools to flash this version into your C139 whether it came with Cingular or
+TF branding originally.  Download this file:
+
+ftp.freecalypso.org:/pub/GSM/Compal/c139-unlocked-fw.zip
+
+Unzip it, and you'll get c139-unlocked-fw.bin - that is the image you'll need
+to flash into your phone.  Get in with fc-loadtool (using tfc139 if necessary
+for bootloader-locked phones) and make a backup of the original flash content.
+Then reflash the firmware as follows:
+
+flash erase-program-boot c139-unlocked-fw.bin 2000
+flash erase 10000 360000
+flash program-bin 2000 c139-unlocked-fw.bin 2000
+
+The 3 commands given above will reflash the phone as follows:
+
+* The first 0x2000 bytes of the firmware image in c139-unlocked-fw.bin comprise
+  the boot code.  This fw version features the "good" boot code *without* the
+  access locking malfeature.  The erase-program-boot command will erase flash
+  sector 0 (the entire 64 KiB sector, as the physics of the flash chip dictates)
+  and then immediately reprogram its first 8 KiB with the "good" boot code from
+  the unlocked fw image file.  The remaining 56 KiB of this sector will be blank
+  after this step.
+
+* The following "regular" flash erase command is to erase the following 54
+  sectors (also of 64 KiB each) in preparation for programming the main fw
+  image in there.
+
+* The last command programs the bulk of the fw image into blank flash that has
+  been erased by the first two commands.
+
+I also recommend erasing the old FFS that was maintained by the old fw version,
+so that the new fw will automatically format a "virgin" FFS the first time it
+boots:
+
+flash erase 370000 50000
+
+After this procedure the phone should retain its original IMEI and factory RF
+calibration values, as these are stored in the 8 KiB sector at 0x3FC000 which
+is not touched per the above procedure - not in the FFS.
+
+The same procedure should be followed for flashing all firmwares for C11x/123
+and C139/140 phones.  In the case of C11x/123, adjust the length for the "main"
+erase and program operations appropriately for the flash configuration in your
+phone.
+
+Flashing newer firmware versions
+================================
+
+The flashing procedure given above, where the first 0x2000 bytes of the new fw
+image (the bootloader part) are written with the flash erase-program-boot
+command and the regular flash program-bin command writes everything from 0x2000
+onward, is only correct for older firmware versions whose bootloader portion is
+completely free from the access locking malfeature: not only unlocked, but with
+no provision for locking at all.  In these older fw versions the boot code is
+fully contained in the first 0x2000 bytes and nothing from 0x2000 onward affects
+the ability to perform a new serial boot, hence the bricking vulnerability
+window ends at 0x2000.  However, this flashing procedure should NOT be used for
+newer fw versions that have the provision for locking the bootloader - it's the
+provision that matters in this case, even if the lock hasn't been activated -
+if you flash one of these newer fw versions as above, you will risk bricking
+your phone!
+
+If you need to flash one of the newer fw versions that includes the bootloader
+lock provision, you need to take some additional precautionary steps:
+
+1. Examine the fw image you wish to flash with a hex dump viewer.  Look starting
+   at offset 0x2000.  You should see 3 identifying ASCII strings: one right at
+   0x2000, another at 0x2020 and one more at 0x2040.  Then look at 4 bytes at
+   offset 0x2060.  If they contain 0xFFFFFFFF (blank flash) like the surrounding
+   unused bytes, then you have an older fw version without the bootloader lock
+   provision - you can safely flash it as in the previous section.  If it's a
+   newer fw version with the bootloader lock provision, the word at 0x2060 will
+   contain either 0x00000000 or 0xDDDDDDDD, corresponding to the activated
+   (access disabled) and non-activated (access enabled) states of the lock,
+   respectively.
+
+2. If the fw image you wish to flash has 0x00000000 at 0x2060, you must patch
+   it to 0xDDDDDDDD with a hex editor before flashing.  Just because our tfc139
+   utility can recover phones with maliciously locked bootloaders does NOT mean
+   that you should *ever* deliberately flash such a bootloader-locked fw image
+   into your phone!  Recovery of locked phones via tfc139 depends on the
+   complete fw image being present and working, not just the bootloader part,
+   hence if you were to flash an image that has a lockable bootloader with the
+   lock activated, the bricking vulnerability window will extend until the
+   *entire* fw image has been programmed - far too dangerous.
+
+3. When flashing the image with fc-loadtool, use a slightly different command
+   sequence compared to the previous section:
+
+   flash erase-program-boot new-fw-image.bin 10000
+   flash erase 10000 360000
+   flash program-bin 10000 new-fw-image.bin 10000 360000
+
+The difference is that the boundary between the part handled with flash
+erase-program-boot and the part handled with flash program-bin has been moved
+from 0x2000 to 0x10000.  Because the word at 0x2060 is part of the bricking
+vulnerability window with these newer fw versions, one should rewrite the
+entire boot sector of the flash (including the beginning of the main fw image)
+with flash erase-program-boot for safety.
+
+Unlocking while keeping the same fw version
+===========================================
+
+Suppose you have a phone with a locked bootloader such that you had to break in
+with tfc139, you would like to unlock it so you can use RAM-based (non-flash)
+tools such as c139explore or OsmocomBB with it, but you have no particular need
+to change the main fw from the original version to a different one.  If you
+need to perform such a cisversion unlock, you can do it as follows:
+
+1. Break in with tfc139;
+2. Use fc-loadtool's flash dump2bin command to save the first 64 KiB sector
+   of the flash to a file;
+3. Using a hex editor, patch the word at 0x2060 from 0x00000000 to 0xDDDDDDDD;
+4. Use fc-loadtool's flash erase-program-boot command to flash the patched
+   (unlocked) boot sector back into the phone.
+
+C155/156 differences
+====================
+
+C155/156 phones are nicer than the others in that they use a flash chip with a
+"bottom boot" configuration.  C11x/123 and C139/140 use "top boot" flash chips,
+which is why the boot code and the first 56 KiB of the main fw image live in
+the same erase block on those phones.  The boot code and the control hand-off
+interface between it and the main fw have also been revamped in C155/156 fw,
+and the new structure is:
+
+8 KiB sector at 0: contains the boot code
+7 more 8 KiB sectors starting at 0x2000: blank and unused
+64 KiB sector at 0x10000: also blank and unused
+64 KiB sector at 0x20000: beginning of main fw image
+
+With this new flash layout, it is now possible to erase and program the main fw
+region starting at 0x20000 without ever erasing the boot code sector or doing
+any writes to it, so there is no bricking vulnerability window at all.  (The
+phone can still be bricked though if one types the wrong command and erases the
+boot sector inadvertently, so be careful.)
+
+So far the only phones in this family that I laid my hacking hands on have been
+North American C156 units, all from the same seller and batch (hence identical),
+so I don't know if there exist any maliciously-locked boot code versions in
+this family - the boot code in my C156 is free of any malfeatures.  But if "bad"
+versions of C155/156 boot code do exist, and if you can break into the phone
+somehow, you can use the flash erase-program-boot command to rewrite the boot
+code with minimal risk of bricking just like on the other Compal families.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/Compiling	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,140 @@
+There are 3 parts to the complete FreeCalypso software suite which are built
+independently of each other:
+
+* The tools that run on a GNU/Linux PC or other host system are the most
+  straightforward: there is a top level Makefile (named Makefile.hosttools if
+  you looking at a development source snapshot, will be renamed to just
+  Makefile in packaged releases of the host tools) that coordinates building
+  and installing all of them.
+
+* The gsm-fw tree, which will eventually become our main GSM firmware, needs to
+  be built with a GNU cross-compiler toolchain for ARM7.  This firmware can be
+  built for several different target devices and with different feature
+  configurations, hence there is no singular build for it - it's more like the
+  Linux kernel in terms of its build configuration management.
+
+* We also have a few utilities which need to be compiled to run on Calypso
+  targets, but which are not part of gsm-fw; they are gathered in the
+  target-utils tree.  They are built with the same GNU toolchain for ARM7 as
+  gsm-fw, but don't have any fancy configuration system.
+
+Building and installing FreeCalypso host tools
+==============================================
+
+If you are working with a packaged FC host tools release, just run 'make', then
+'make install' as root.  If you are working with a development source snapshot,
+do 'make -f Makefile.hosttools' instead.
+
+The "standard" install directories are /usr/local/bin for binaries and
+/usr/local/share/freecalypso for helper files.  If you need to change these
+paths to something else, you'll need to edit a bunch of individual component
+Makefiles, and possibly also some source files like loadtools/defpath.c -
+sorry, FreeCalypso is not GNU and does not use autotools.
+
+All FreeCalypso host tools are written in plain C, and with the exception of a
+few utilities in the "special-purpose hacks" category, they have absolutely no
+library dependencies beyond libc.  In other words, they are very friendly to
+those who like bare bones minimalist systems.  The only exceptions are
+fc-getpirimei and fc-pirhackinit which use libcrypto from OpenSSL for DES
+functions, and fc-lcdemu which needs libX11 to compile and an X11 display to
+run.  But as you can read in Host-tools-overview, these utilities are not
+particularly important, so if your system lacks those libraries, just edit the
+Makefiles to not build these utilities - it is very unlikely that you will miss
+them.
+
+To those who are going to build distro packages from these fc-host-tools: it
+is recommended that you leave fc-getpirimei, fc-pirhackinit and fc-lcdemu out
+of the basic package - please don't create extra dependencies just to support a
+few odd hacks which are unlikely to ever be used by anyone other than the
+developer who needed them at one time and no longer even uses them herself as
+their original one-time purpose has already been served.
+
+Building and installing the ARM7 toolchain
+==========================================
+
+The current "official" GNU ARM toolchain for FreeCalypso consists of
+binutils-2.21.1, gcc-4.5.4 and newlib-2.0.0 with a specific set of patches and
+build configuration options.  Build it as follows:
+
+1. Download these 3 source tarballs for the standard GNU+newlib components:
+
+   binutils-2.21.1a.tar.bz2
+   gcc-core-4.5.4.tar.bz2
+   newlib-2.0.0.tar.gz
+
+2. Run the build+install.sh script in the toolchain directory.  Read the
+   comments in the script first for the usage instructions.
+
+The toolchain thus built will need to be in your PATH before you can compile
+gsm-fw or target-utils.
+
+Please note: the toolchain that is prescribed for FreeCalypso as above is
+*believed* to be equivalent to the one used by OsmocomBB, but there are no
+guarantees.  Use any other toolchain at your own risk.
+
+Compiling target-utils
+======================
+
+Running 'make' in the target-utils tree with the ARM7 toolchain present in your
+PATH will result in compalstage and loadagent being built; these are the two
+components needed in order to use FreeCalypso loadtools.  Run 'make install' to
+install these target binaries in /usr/local/share/freecalypso, which is where
+loadtools will look for them.
+
+Run 'make all' in target-utils to build some other components that aren't
+really needed.
+
+Compiling FreeCalypso GSM firmware
+==================================
+
+The firmware in our gsm-fw tree can be built in many different configurations,
+hence there is no singular build for it.  The configuration choices consist of:
+
+* Which target device the firmware should be built for: the target device
+  selection is made at compile time; do not attempt to take a firmware image
+  built for one target device and flash or fc-xram it into another!
+
+* What functionality is to be included.  As the FreeCalypso firmware subproject
+  moves forward, we gradually add chunks of functionality, slowly approaching
+  what each target device is ultimately capable of.  However, each time we add
+  a new piece of functionality, the ability to build a firmware image that works
+  like before, without the newly added functionality, still remains.  Each
+  feature to be included needs to be explicitly selected.
+
+* Miscellaneous configuration: which Calypso UART should be used for what,
+  should the firmware use a real FFS (flash file system) in flash or a fake one
+  in RAM, etc.
+
+The GSM firmware build configuration is set by way of an editable text file
+named build.conf; the configuration and build procedure is as follows:
+
+1. Look at the available repertoire of standard configurations under
+   gsm-fw/configs and choose which one you would like to use, either as-is or
+   as a basis for your own;
+
+2. Copy the configuration you selected to build.conf in the gsm-fw directory;
+
+3. Optionally edit it to taste - the configuration language is Bourne shell;
+
+4. Run 'make' in the gsm-fw directory.
+
+Depending on the configuration, either a flashable or a RAM-loadable image will
+be built by default.  A flashable image will appear in finlink/flashImage.bin;
+these images are meant to be programmed with fc-loadtool's flash program-bin
+command; the starting flash address at which the image needs to be programmed
+depends on the target device - see target-specific notes.  A RAM-loadable image
+will appear in finlink/ramImage.srec; these images are meant to be loaded and
+run with the fc-xram utility.
+
+It is possible to build either a flashable or a RAM-loadable image, or both,
+without changing build.conf: run 'make flashImage' or 'make ramImage' as
+desired.  (The compilation of each module from source into a .o and all
+intermediate linking steps are agnostic to whether a flashImage or a ramImage
+is being built, only the very final link step differs.)  Any otherwise working
+configuration can be built into a flashImage, even if it makes no logical sense
+to do so, but the ability to build a ramImage for a given configuration depends
+on the code image size (which in turn depends on the selected feature set) and
+the amount of RAM available on the target in question: most Calypso GSM devices
+have small RAM, enough to satisfy a GSM firmware's data space requirements, but
+not enough to hold the entire firmware code in RAM as well.  Please see target-
+specific notes for more details.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/High-speed-serial	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,111 @@
+The highest baud rate supported by "standard" PC serial ports is 115200 bps,
+but Calypso UARTs can go quite a bit faster.  Being clocked with 13 MHz (a
+standard frequency in the GSM world), these UARTs can produce non-standard
+(outside of the GSM world) baud rates of 203125, 406250 and 812500 bps.  When
+working with Motorola C1xx and Openmoko GTA01/02 phones which present a debug
+and programming serial interface on a 2.5 mm headset jack, one can make use of
+these high serial baud rates by using a USB to headset jack programming cable
+based on one of the better USB-serial chips that can support these GSM special
+baud rates well above 115200.  The two USB-serial chips that are known to work
+in this manner are CP2102 and FTDI, although each of the two requires its own
+special quirks described below.  Other USB to serial cables use chips which
+don't support the high baud rates in question, and therefore are limited to
+115200 baud max like a "standard" PC serial port.
+
+FreeCalypso tools can use these high serial baud rates in the following ways:
+
+* When you use fc-loadtool to dump and program GSM device flash memory
+  (flashing firmware images), the transfers get annoyingly slow at 115200 baud
+  if you have to do it a lot.  Switching to 406250 or even better 812500 baud
+  makes them go considerably faster.
+
+* Some of our target devices have large enough RAM to execute a GSM firmware
+  image entirely from RAM without flashing - very handy for development and
+  experimentation.  The tool used to run these RAM-based images is fc-xram,
+  and it also supports the option of using high serial baud rates for the image
+  transfer for the same reason: repeatedly transferring 1.5 MiB images over
+  115200 baud gets tiresome.
+
+* If you are building your own GSM firmware (either FC GSM fw or one of our
+  TCS211-based hacks), you can make it run its RVTMUX interface at 406250 or
+  812500 baud.  We used this trick when we tried to make TCS211 with D-Sample-
+  targeting UI (176x220 pix LCD, 16 bits per pixel) send its virtual LCD raster
+  blits out the serial port.  Our rvtdump and rvinterf utilities support this
+  mode of operation by providing options to select different baud rates.
+
+Using CP2102 adapters
+=====================
+
+CP2102 chips have a built-in EEPROM that contains (among other things) a
+32-entry table in which the supported serial baud rates are programmed.  In
+order to support the special GSM baud rates, these rates need to be added to
+that table, displacing some other entries.  The convention established by the
+Pirelli DP-L10 phone (has a CP2102 built in and programmed at the factory for
+GSM baud rates) is that 203120 baud takes the place of 230400, 406250 takes the
+place of 460800, and 812500 takes the place of 921600.
+
+Because you need a special cable anyway to make the necessary physical
+connection to the debug/programming serial port presented on a 2.5 mm headset
+jack, you will probably be buying the requisite cable from a specialized
+professional vendor.  In that case it is that vendor's responsibility to sell
+you the cable with the CP2102 chip already programmed with GSM baud rates:
+because the physical construction of the cable (2.5 mm headset jack on the
+serial end) makes it specific to GSM devices, and all known GSM devices use a
+13 MHz clock or some integer multiple thereof, it is pointless for a
+physically-GSM-specific cable to be set up for 230400/460800/921600 baud when
+all known GSM devices will need 203125/406250/812500 baud instead.
+
+If you making a CP2102-based serial cable yourself (either for your own personal
+use or professionally/commercially), please follow these instructions for baud
+rate programming:
+
+http://bb.osmocom.org/trac/wiki/Hardware/CP210xTutorial
+
+If you follow the procedure given on that page, your CP2102 will be programmed
+the same way as the one in the Pirelli DP-L10 (Foxconn's original factory
+programming).
+
+The serial port handling code in FreeCalypso host tools is written to request
+B230400 from termios when 203125 baud is desired, likewise B460800 for 406250
+baud and B921600 for 812500 baud.  Therefore, if you have a CP2102-based cable
+with properly programmed EEPROM, everything will Just Work.
+
+Using FTDI adapters
+===================
+
+Unlike CP2102, FTDI adapters don't require any non-volatile EEPROM programming
+for GSM baud rates, but they have a different pain point - arguably a worse one
+- that is entirely a software issue.  The API which the Linux kernel provides
+to userspace applications for opening and configuring serial ports provides no
+clean, sensible way for an application to request a particular baud rate that
+is not in the predefined-once-and-for-all list, and to make it unambiguous to
+the in-kernel driver exactly what it wants.
+
+The method provided by the ftdi_sio driver in the standard Linux kernel is
+gross, and I (Space Falcon) refuse to use it.  The serial port handling code in
+FreeCalypso host tools is written for the clean CP2102 way, and is *not* muddied
+with the muck that would be necessary to get the high GSM baud rates with an
+unpatched ftdi_sio driver.  Therefore, if you would like to use one of the high
+GSM baud rates with FreeCalypso with an FTDI adapter, you will need to dirty
+your Linux host system with a hacky kernel patch.  The patch provided in
+linux-2.6.37.6-ftdi_sio.c.patch (made against Linux 2.6.37.6, which is what I
+use - came with Slackware 13.37 - adapt as necessary for your kernel version)
+makes the ftdi_sio driver behave like a GSM-programmed CP2102: termios B230400
+turns into 203125 baud, B460800 turns into 406250 and B921600 turns into 812500.
+
+This patch won't break other software (*cough* osmocom-bb *cough*) that does
+use the "standard" ftdi_sio way of requesting high GSM baud rates, i.e., both
+ways of selecting these baud rates should still work, but if you have other
+(non-GSM) serial devices on the same system which need 230400, 460800 or 921600
+baud, those will break.
+
+Using adapters built into phones
+================================
+
+The Calypso chip has no native USB capabilities, thus if a Calypso phone
+presents a USB charging+data port to the user, it must have a USB to serial
+converter built in.  The only phone we currently know of that does this is
+Pirelli DP-L10, and its built-in USB-serial adapter chip is CP2102.  It has
+already been programmed with the correct GSM baud rates on Foxconn's original
+production line, thus one can always use 812500 baud with FreeCalypso tools on
+this phone and it will Just Work.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/Host-tools-overview	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,125 @@
+FreeCalypso host tools suite features the following tools that are potentially
+useful to end users:
+
+fc-loadtool	This is the tool used to read and write the non-volatile flash
+		memory of supported GSM devices.  It can be used to reflash
+		these devices with new firmware (whether pre-existing or new
+		firmwares developed within our project), and to save and restore
+		flash backups.  This tool operates on the target device (phone
+		or modem) while its regular firmware is shut down.
+
+fc-fsio		This tool connects to GSM devices running one of the supported
+		firmware versions while the fw is running (unlike fc-loadtool
+		which operates on a device while its regular fw is shut down)
+		and allows you to manipulate (read and write) the device's
+		flash file system.  It is thus a higher-level tool than
+		fc-loadtool.  It is intended primarily for working with our own
+		firmwares, but it also works with Pirelli's original fw.
+
+fc-shell	FreeCalypso firmwares have a feature of our own invention (not
+		present in any pre-existing ones) to accept AT commands over
+		the RVTMUX interface.  It is useful when no second UART is
+		available for a dedicated standard AT command interface.
+		fc-shell is the tool that allows you to send AT commands to the
+		firmware in this manner; it also allows a few other kinds of
+		asynchronous commands to be sent.
+
+tfc139		This tool breaks into Mot C1xx phones via shellcode injection,
+		a method that works despite any bootloader locks, allowing you
+		to reflash locked phones with new firmware with fc-loadtool.
+		The name of the utility is historical: previously it was
+		specific to TFC139 phones (C139s sold with TracFone branding),
+		but the current version is expected to work with all Mot C1xx
+		firmware versions.
+
+imei-luhn	A simple utility for computing or verifying the Luhn check
+		digit of an IMEI number.
+
+The following host tools are primarily for developers, but may be useful to
+end users as well:
+
+rvtdump		This tool produces a human-readable dump of all output emitted
+		by a TI-based GSM fw on the RVTMUX binary packet interface.  It
+		can also log this dump to a file.
+
+rvinterf	This tool is a superset of rvtdump: it not only dumps and/or
+		logs all output from the GSM fw, but also provides a mechanism
+		for sending command packets to it.  Rvinterf is the engine
+		behind fc-fsio, fc-shell and fc-tmsh.
+
+tiffs,		These tools perform "in vitro" analysis of flash file system
+mokoffs,	(FFS) images read out of GSM devices with TI-based firmwares.
+pirffs		You can list and extract the FFS content captured as a raw
+		flash image, and even perform a few "forensic" operations along
+		the lines of reading deleted files and seeing the history of
+		FFS modifications.  tiffs is the main program, whereas mokoffs
+		and pirffs are convenience wrappers for the common FFS
+		configurations from Openmoko and Pirelli.
+
+fc-getpirimei	This utility retrieves the factory-programmed IMEI of a Pirelli
+		DP-L10 phone by quering its running firmware over the RVTMUX
+		interface.
+
+fc-serterm	This tool is a trivial serial terminal program.  Its special
+		feature is that any output coming the serial port that isn't
+		printable ASCII is displayed as by cat -v.  It is useful for
+		talking to serially-interfaced devices that mix ASCII with
+		binary in their serial talk.
+
+The following tools are really just for developers:
+
+ctracedec	GSM firmwares built in TI's Windows environment (official ones
+		as well as our own hacks based on the TCS211 semi-src) have a
+		"compressed trace" misfeature whereby many of the ASCII strings
+		in debug trace messages get replaced with numeric indices at
+		build time, and these numeric indices are all that gets emitted
+		on the RVTMUX serial channel.  This numeric trace output can be
+		turned back into ASCII strings if you have the str2ind.tab file
+		corresponding to the fw version that emitted the output in
+		question; this ctracedec utility performs that decoding.
+
+fc-iram,	Reprogramming the non-volatile flash memory is not the only way
+fc-xram,	to run your own code on a Calypso GSM device.  If your code is
+fc-compalram	small enough to fit entirely into the available RAM on the
+		device, and you would like to just run it without flashing it
+		permanently, these tools do the job of loading code images into
+		different kinds of RAM through different download protocols.
+
+fc-tmsh		TI had a tool called TMSH that stood for "test mode shell".  We
+		don't know exactly how it worked, hence we make no claim of our
+		own test mode shell being anything like TI's original, but we
+		do have a test mode shell of our own.  It sends command packets
+		to the ETM (Enhanced Test Mode) component in the GSM firmware
+		and displays its responses in a purely asynchronous manner,
+		i.e., our tool has no knowledge of any correspondence between
+		the commands it sends and the responses they elicit.  (In
+		contrast, fc-fsio described above also talks to ETM, but it
+		does so synchronously.)
+
+fc-olddump	This tool captures a memory dump from a GSM device whose
+		firmware implements the old non-enhanced Test Mode memory read
+		command.  It works with Mot C1xx original firmwares.
+
+fc-rgbconv	A simple aid for phone UI development that converts RGB color
+		values between human-intuitive 8:8:8 format and the 5:6:5 format
+		used by the color LCDs in the phones targeted by FreeCalypso.
+
+The following tools are really just special-purpose hacks:
+
+fc-dspapidump	This utility uses ETM in synchronous mode to read and dump the
+		contents of the DSP API RAM in a target Calypso GSM device
+		while the firmware is running.
+
+fc-lcdemu	We have TI's TCS211 firmware semi-src that includes TI's
+		demo/prototype phone UI targeting the 176x220 pixel LCD on TI's
+		D-Sample development kit, but no suitable hardware on which we
+		could run this fw with this UI and see it in action.  We built
+		a hacked-up version of the fw that emits all raster blits
+		intended for the big LCD on the RVTMUX serial interface, and
+		this fc-lcdemu utility is a plug-in for rvinterf that actually
+		displays these LCD blits in an X11 window.
+
+fc-pirhackinit	This fc-pirhackinit utility is highly specific to the
+		TCS211-on-Pirelli exercise.  DO NOT run it against Pirelli's
+		stock firmware, nor is it needed when using our full-source
+		FreeCalypso firmware.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/RVTMUX	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,236 @@
+TI's Calypso GSM/GPRS baseband processor chip has not one but two UART serial
+ports, called "MODEM" and "IrDA" in the hardware documentation.  In hardware
+terms, both support basic data-leads-only UART operation at a fixed baud rate,
+but their extended capabilities differ: the IrDA UART adds IrDA capability (no
+surprise), whereas the MODEM UART adds hardware flow control and autobaud.  If
+one is not implementing an actual IrDA interface, then the so-called "IrDA"
+UART becomes a strict subset of the MODEM one in terms of hw capabilities -
+just an extra UART, but a somewhat less capable one.
+
+In a classic modem design such as that present in the GTA0x smartphones made by
+FIC/Openmoko, the Calypso presents a standard AT command interface on its MODEM
+UART port.  (In the case of GTA0x phones this serial channel is wired to the
+phone's application processor; in a standalone modem it would be wired to a
+USB-serial chip or even to a classic RS-232 DB25 port.)  However, what is less
+known is that the standard firmware for such modems simultaneously presents an
+entirely different interface on the IrDA UART - an interface intended for
+development, debugging and factory production testing (which includes RF
+calibration and IMEI etc programming), rather than for "normal" end users.
+
+Normally this debug/development serial interface (called RVTMUX as will be
+explained momentarily) is hidden from "ordinary" users - for example, on FIC
+GTA0x phones it is wired to the analog headset jack through a hardware switch
+which needs to be enabled through a GPIO signal from the AP.  On Mot C139 and
+its siblings the situation is similar: one needs to key in the secret magic
+sequence **16379#, and then the firmware presents a hidden menu for switching
+the analog headset jack between its "normal" function and the UART carrying
+RVTMUX.
+
+But there also exist some oddball devices on which the RVTMUX interface is
+presented "in your face".  The Pirelli DP-L10 phone has a USB charging port
+which is also wired (through a CP2102 USB-serial chip) to the IrDA UART on the
+Calypso - that's right, IrDA, not MODEM - a design decision with which this
+hacker strongly disagrees.  (It'll definitely be wired to the MODEM UART
+instead on our own semi-clone of this phone, but I digress.)  Apparently Foxconn
+(the designers of this phone) had no desire to provide a standard AT command
+interface, and instead the only "official" way to use the "data" function of
+their USB port (rather than the charging function) is for their "PC sync"
+feature, i.e., their proprietary Weendoze software.  And guess what, their
+proprietary "PC sync" feature works over TI's RVTMUX interface, as that is
+what's presented on Calypso's IrDA UART behind the CP2102!
+
+OK, so what is this RVTMUX?  RV stands for RiViera, an application framework
+which TI added to their GSM firmware suite in the early 2000s, T stands for
+trace, and MUX stands for multiplexor.  It's a binary packet interface, although
+many of these packets contain ASCII debug messages inside.  The framing format
+is the same in both directions: each packet begins and ends with an STX (0x02)
+byte, all payload bytes except 0x02 and 0x10 are sent literally, and there is a
+DLE (0x10) byte prepended before any 0x02 or 0x10 in the payload.  It's the same
+general principle as asynchronous HDLC (RFC 1662): packets can contain any
+binary data, and the framing provides packet boundaries - although TI's version
+is a little less robust than async-HDLC when it comes to recovering after lost
+synchronization.
+
+The firmware suite component responsible for actually sending and receiving
+these packets over the assigned UART port (usually IrDA, but can be MODEM too,
+as on Compal phones for example) is called RVT (RiViera Trace), and it
+implements a MUX function.  There are several logical channels multiplexed over
+one physical serial port, and the first byte of every packet indicates which
+logical channel it belongs to.  Any component within the GSM firmware suite can
+send packets to RVT for transmission on this serial interface, and can also
+register to receive packets beginning with a particular type ID byte.
+
+Debug trace output
+==================
+
+All GSM device firmwares that are based on TI's Calypso chipset reference fw
+continuously emit quite voluminous debug trace output on their RVTMUX serial
+port, whether it is hidden or exposed on a given device.  Like all RVTMUX
+traffic, this debug trace output takes the form of binary packets as explained
+above, but the content of these packets is mostly ASCII with some binary header
+bytes prepended.  FreeCalypso host utility rvtdump captures all serial output
+from a GSM device's RVTMUX port, parses the packet structure and displays this
+output in line-oriented pure ASCII with all binary parts decoded.
+
+Test Mode commands
+==================
+
+The other major use of the RVTMUX interface is sending so-called Test Mode
+commands from an external host to a running GSM device.  Depending on the
+firmware version, a GSM device can be commanded to do any of the following
+things through this mechanism:
+
+* Exercise RF test modes, e.g., transmit continuously at a set frequency and
+  power level;
+* Read and write arbitrary memory locations in the Calypso ARM7 address space;
+* Read and write ABB chip registers;
+* Reboot or power off;
+* Access and manipulate the device's flash file system (FFS).
+
+In the segment of history of interest to us TI has produced two different
+target firmware components that can receive, interpret and act upon Test Mode
+command packets:
+
+* The original Test Mode component of Layer 1, called L1TM or TML1: this
+  component handles all RF test modes (needed for RF calibration on device
+  production lines), and originally it also implemented memory and ABB register
+  read and write commands, and provided access to TMFFS1 (see below).  In the
+  original implementation this component registered itself as the handler for
+  the "TM" RVTMUX channel (RVT packet type 0x14), so it would receive all TM
+  packets sent to the device.
+
+* Enhanced Test Mode (ETM) is a later invention.  It registers itself (instead
+  of the old TM in L1) with RVT as the handler for the "TM" RVTMUX channel, and
+  then provides a registration service of its own, such that various components
+  in the fw suite can register to receive external command packets passing
+  first through RVT, then through ETM, and can send responses passing through
+  ETM, then through RVT back to the external host.  If a given fw version
+  contains both ETM and L1TM, then L1TM registers itself with ETM; an external
+  host would send exactly the same binary command packets to exercise RF test
+  modes, but inside the firmware they now pass through ETM on their way to L1TM.
+
+The ETM_CORE module contained within ETM itself provides some low-level debug
+commands: by sending the right binary command packets to the GSM device via the
+RVTMUX serial channel, an external host can examine or modify any memory
+location and any hardware register, cause the device to reset, etc.  Prior to
+ETM some of these functions (but not all) could be exercised through older TM3
+commands, but in FreeCalypso we became familiar with the ETM versions of these
+commands long before the older ones because we got the ETM component in full
+source form, whereas our copy of TCS211 (TI's reference fw) has L1TM in a
+binary library.
+
+Our TCS211/leo2moko reference fw has both ETM and L1TM, thus it accepts both
+ETM and TM3 command packets.  ETM commands (including TMFFS2, see below) work
+on Pirelli's fw, but Mot/Compal's original fw for the C139 has only the
+original non-enhanced Test Mode, not ETM.
+
+FFS access via TM/ETM
+=====================
+
+One of the essential facilities provided in one form or another in all known
+incarnations of the Test Mode mechanism is the ability to access and manipulate
+the GSM device's flash file system (FFS).  See TIFFS-Overview for a description
+of this file system.  TI's TMFFS1 and TMFFS2 protocols provide a command and
+response packet interface to the FFS API functions inside the fw, and enable an
+external host connected to the GSM device via the RVTMUX channel to perform
+arbitrary read and write operations on the device file system.
+
+In the segment of history of interest to us TI has produced two different
+and entirely incompatible versions of the TMFFS protocol: TMFFS1 and TMFFS2.
+Or rather, what is now called TMFFS1 was originally just TMFFS, and then came
+TMFFS2.  TMFFS2 works only through ETM, whereas TMFFS1 predates ETM: in the
+original implementation the tm_ffs() function in the FFS code was called from
+L1TM code.
+
+Our copy of TCS211 reference fw includes the source for both TMFFS1 and TMFFS2;
+it is theoretically possible to build a firmware image that includes both TMFFS
+versions (they won't conflict because they respond to different command
+packets), but it is pretty clear that TI never intended to have both enabled
+at the same time.  Our copy of TCS211 came with TMFFS1 enabled and we didn't
+change it when we made the moko12 (leo2moko-r1) fw release for the Openmoko
+community (the previous proprietary mokoN firmwares also implement TMFFS1),
+but we have subsequently switched to TMFFS2 for our current TCS211-based work.
+
+Pirelli's fw implements TMFFS2: we don't have any source for this fw, but our
+FreeCalypso host utilities written to talk the TMFFS2 protocol based on our
+available TCS211 source work beautifully when run against Pirelli's fw.
+
+Use in FreeCalypso
+==================
+
+The FreeCalypso project has adopted the same general firmware architecture as
+that exhibited by TI's standard firmwares from the Moko/Pirelli time frame.  We
+use TI's RiViera framework lifted directly out of the TCS211 reference fw, and
+that includes the RVT module and the RVTMUX interface it presents.  Our GSM fw
+emits the same 3 kinds of debug traces (RV, L1 and GPF) as the pre-existing
+firmwares with which we are seeking functional parity, and for Test Mode
+functionality we have the option of including ETM, TMFFS1 and/or TMFFS2 in our
+firmware builds.  (Both TMFFS versions require ETM in our implementation, and
+it is possible to build a firmware image with both included.)
+
+We have adopted ETM and TMFFS2 as the standard combination for FreeCalypso,
+i.e., ETM_CORE for memory and ABB register reads and writes and TMFFS2 for
+external FFS access.  We needed to develop our own host tools for operating on
+GSM device FFS via one of the two TMFFS protocols, and after studying the fw
+source implementing both, I (Space Falcon) came to the conclusion that TMFFS2
+is both more capable and more reliable; my guess is that TMFFS1 was likely kept
+around only because some of TI's crappy Weendoze host software depended on it.
+(See gsm-fw/services/ffs/tmffs.c if you would like to judge for yourself.)
+
+We have the following host tools for communicating with TI-based GSM firmwares
+(both our own and some of the existing proprietary ones):
+
+rvtdump		This tool produces a human-readable dump of all output emitted
+		by a TI-based GSM fw in the form of RVTMUX binary packets.  It
+		can also log this dump to a file.
+
+rvinterf	This tool is a superset of rvtdump: it not only dumps and/or
+		logs all output from the GSM fw, but also provides a mechanism
+		for sending command packets to it.
+
+Rvinterf is the engine behind the following host tools that send Test Mode
+commands to a target:
+
+fc-tmsh		This is our basic tool for sending Test Mode commands to a
+		running GSM fw.  It is strictly asynchronous in that commands
+		entered by the operator get sent to the target, and any response
+		packets received from the target are displayed as they come in.
+		The tool has no knowledge of any correspondence between commands
+		being sent and whatever responses they should elicit, i.e., it
+		is perfectly suited for experimental discovery of firmware
+		behaviour in response to Test Mode commands.
+
+		This tool was written before we realized that there was/is an
+		older, more basic Test Mode predating ETM, hence in many place
+		we say "ETM" when we really should have said "TM".  Oh well...
+
+fc-fsio		This tool speaks the TMFFS2 protocol and allows a user or
+		developer to perform a wide range of operations on the file
+		system of a GSM device.  It operates synchronously, i.e., it
+		sends ETM/TMFFS2 commands and expects responses in strict
+		lock-step; a single user command may translate into a large
+		number of ETM/TMFFS2 command packet exchanges.
+
+AT commands over RVTMUX
+=======================
+
+There is one more use to which we put the RVTMUX debug serial interface that is
+an original FreeCalypso invention: communicating with the AT command interpreter
+(ATI).  TI's original architecture assumes that if a product is to offer a
+standard AT command interface (the product is either a GSM/GPRS modem for which
+this AT command interface is the sole mode of usage or a feature phone that
+offers a data port as one of its features), then it will be presented on a
+dedicated UART separate from RVTMUX.
+
+However, many of our target devices have only one UART practically accessible,
+and even when we use Openmoko's modem as our development platform, the RVTMUX
+interface is more convenient because it connects externally, whereas the MODEM
+UART is connected to the application processor of the smartphone.  Therefore,
+we developed a way to pass AT commands over RVTMUX.  We created a new RVTMUX
+channel for this interface and assigned it RVT packet type 0x1A.  Packets sent
+from an external host to the GSM device carry AT commands and SMS string input,
+whereas packets flowing the other way carry ATI's responses to commands and
+asynchronous notifications such as incoming calls.
+
+The host utility for talking AT commands to a FreeCalypso GSM device via RVTMUX
+is fc-shell; it works via rvinterf just like fc-fsio and fc-tmsh.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/TFC139-breakin	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,120 @@
+Maliciously locked bootloader
+=============================
+
+When Compal (Motorola's ODM who designed and built their C1xx phones for them)
+designed the firmware architecture and flash memory layout for their phones,
+they made a bad design decision by putting the boundary between their bootloader
+and the main fw image at 0x2000, even though the flash erase block boundary
+doesn't come until 0x10000 - thus every time the main fw needs to be reflashed
+to a different version, the dangerous boot sector has to be reflashed too.
+
+But then they made things even worse in the newer versions of their fw by
+introducing a bootloader lock malfeature whereby the ability to interrupt boot
+and load code serially may be artificially disabled.  This malfeature is
+implemented as follows:
+
+* In the original firmware layout (before the addition of the malfeature in
+  question) the boot code occupies the flash range from 0 through 0x1FFF, then
+  there are some ID strings at 0x2000, 0x2020 and 0x2040, and then the part of
+  the firmware that used to be at 0x10000 in TI's reference fw starts at 0x20A0,
+  with the entry point at 0x20F8 (corresponding to TI's 0x10058).
+
+  With the addition of the bootloader lock malfeature the 32-bit word at 0x2060
+  (previously unused and filled with 0xFFFFFFFF) became a control word telling
+  the bootloader whether diversion of the boot path to serial code download
+  should be allowed or not.
+
+* When firmware images with this malfeature present are first built, the word
+  at 0x2060 contains 0xDDDDDDDD.  (Does D stand for debug or development, or
+  was the developer who implemented this malfeature fascinated by large bra
+  cups?  We may never know.)  This word MUST read as 0xDDDDDDDD in order for
+  the boot code to allow serial download: if it reads as any other value (e.g.,
+  if it contains 0xFFFFFFFF because only the 8192 byte boot code has been
+  programmed into flash sector 0, with blank flash from 0x2000 onward), no
+  serial download opportunity will ever be offered and the phone is effectively
+  bricked!
+
+* For as long as the word at 0x2060 still contained 0xDDDDDDDD, Compal's
+  developers could continue gaining access through the bootloader and reflashing
+  their firmware.  But when phones were to be shipped to customers with the
+  malicious bootloader lock activated, they probably sent some Test Mode command
+  (see RVTMUX write-up) to their running fw that caused it to write 0x00000000
+  into the flash word at 0x2060.  (Remember that any bit in a NOR flash memory
+  can be programmed from 1 to 0 at any time in any combination, but changing
+  bits from 0 back to 1 is only possible with full sector erasure.)
+
+* Once the word at 0x2060 has been programmed (in the flash memory sense) from
+  0xDDDDDDDD down to 0x00000000, the phone is irreversibly locked and has lost
+  its ability to ever run a different firmware version, like a kamikaze pilot's
+  plane that has discarded its landing gear and can only crash now.
+
+Recovery procedure
+==================
+
+While it probably was Compal's, Motorola's and various carriers' intent that the
+bootloader lock on their phones be truly irreversible, the unlocking community
+has now developed a method for recovering these phones (restoring their ability
+to run any firmware of the user's choice) which (we hope) will work with all of
+the existing locked-down firmware versions.  It works as follows:
+
+* Even though the bootloader is locked down, if one boots the full fw regularly,
+  one can still access the RVTMUX interface which the TI-based fw implements
+  for debug trace and factory programming functions.  One needs to key in the
+  magic sequence **16379# into the running fw, and a hidden menu will appear,
+  giving the operator the option to enable trace.  Selecting this option will
+  cause the fw to switch the headset jack to the UART carrying RVTMUX.
+
+* Mot/Compal's firmware is based on a quite old version of TI's chipset
+  reference fw (relative to late TCS211 from the Openmoko/Pirelli era), and it
+  does not feature the Enhanced Test Mode (ETM) component with which we are
+  most familiar.  However, it does implement the older set of non-enhanced
+  Test Mode commands, and these TM commands just happen to include raw memory
+  read and write operations at an arbitrary address.  (For a while we were
+  under a mistaken belief that these commands were Compal's inventions, until
+  we discovered TI's original TM predating ETM.)
+
+* The ability to write arbitrary bytes into arbitrary RAM locations while the
+  phone firmware is running means that we can inject a piece of shellcode into
+  an unused RAM location and then cause this shellcode to gain execution by
+  overwriting a function return address on the stack.
+
+* Once you can execute your own code on the Calypso, everything becomes possible
+  once again.  At that point one can trivially reverse the bootloader lock by
+  erasing flash sector 0 and rewriting it with 0xDDDDDDDD in the 0x2060 word,
+  or even better, rewriting this boot sector with an older version of the boot
+  code that lacks the locking malfeature altogether.
+
+Procedure variations: old mot931c.exe vs. new tfc139
+====================================================
+
+We first became aware of the possibility of recovering locked-down phones as
+described above in the spring of 2014 when FreeCalypso developer Space Falcon
+became aware of the existence of Windows utility mot931c.exe (binary w/o source)
+that performs a variant of this unlocking procedure specific to one particular
+locked-down firmware version: C139 phones with TracFone branding, fw version
+8.8.17.  At first we had replicated the operation of this Windows tool verbatim
+in our own Unix/Linux-based tfc139 libre tool; this variant of the shellcode-
+based unlocking procedure worked well on TFC139 units, but could not crack other
+locked-down fw versions, e.g., Cingular-branded C139 phones with fw version
+1.9.24.
+
+Subsequent investigation revealed that whoever wrote that mot931c.exe Windows
+tool had not studied the operation of Motorola/Compal's TI-based firmware deeply
+enough, and implemented their shellcode injection quite suboptimally: the stack
+smashing process is hitting the wrong stack (not the stack of the L1A task in
+whose context the Test Mode commands sent over the UART are executing), and it
+is only through dumb luck that this version of the break-in procedure worked
+at all.  The limitation of working only with one specific fw version results
+from this poor method of shellcode injection (mindless choice of the wrong stack
+for smashing), and instead of adapting it in a version-specific manner to other
+particular locked-down fw versions at hand, I (Space Falcon) reimplemented our
+tfc139 utility to smash the right stack (that of the L1A task), and thereby
+made it generic to all Mot C1xx firmware versions.
+
+Our Compal firmware break-in utility is still called tfc139, but it is no longer
+specific to TFC139 phones; instead it should work with all Mot C1xx firmwares.
+The shellcode injected by tfc139 re-enables the Calypso chip's own boot ROM and
+jumps to it; this boot ROM will endlessly wait for a serial download because
+the word at 0x2000 contains neither 0 nor 1 (it is part of an identifying ASCII
+string in Mot/Compal's fw), and the operator can then run fc-loadtool to
+perform arbitrary flash operations.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/TIFFS-Overview	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,395 @@
+All TI GSM firmwares known to this author (FreeCalypso developer Space Falcon)
+implement some kind of flash file system, or FFS.  Several different FFS code
+implementations, and correspondingly several different on-flash data formats,
+have been used throughout the history of TI's involvement in the wireless
+terminal business.  The FFS incarnation of primary interest to the FreeCalypso
+project is the one invented by Mads Meisner-Jensen at TI in the early 2000s
+(at least according to the comments in the sources available to us), and it is
+relevant to us in the following ways:
+
+* When targeting the GSM modem in Openmoko's GTA01/02 smartphones, we need to
+  work with the original FFS from the factory (call it MokoFFS), the same FFS
+  as used by the mokoN firmwares: this FFS contains the IMEI and the RF
+  calibration values from the factory, which we most certainly don't want to go
+  without.
+
+* The Leonardo firmware semi-src which we are using as the reference for
+  building our own full source, multi-target GSM fw contains a turnkey-working
+  implementation of this very FFS, using the on-flash format in question and
+  providing run-time APIs expected by the rest of the GSM fw suite.  Following
+  the principle of ``if it ain't broke, don't fix it'', we can use this FFS not
+  only on the gtamodem target, but also on other targets, including those where
+  we would be starting from a blank state and thus have the freedom to use
+  whatever FFS we like.
+
+* The original proprietary fw on the Pirelli DP-L10 phone also happens to use
+  an FFS in the same format.  Pirelli's FFS does *not* contain the IMEI or any
+  of the RF calibration values though, and trying to reuse it directly for our
+  own FC GSM fw seems to be more trouble than benefit - so we'll probably have
+  our fw start with a blank TIFFS instead - but there is still insight to be
+  gained from in-vitro examination of captured Pirelli FFS images.
+
+Naming
+======
+
+I have previously referred to the FFS format in question as Mokopir-FFS or
+MPFFS, from "Moko" and "Pirelli".  I was originally hesitant to call it TIFFS,
+as lacking the source code, I had no way of knowing whether the FFS format and
+implementation were of TI's own invention, or something that TI licensed as a
+black box from one of their many proprietary software partners.  (I was unable
+to identify it as any well-known, industry-standard FFS format, but absence of
+evidence is not evidence of absence.)  But now that we have TI's original source
+code which implements this FFS (first the MV100-0.1.rar source, then the full
+Leonardo one), complete with comments and a HISTORY file, we know that our FFS
+was invented and implemented by someone named Mads Meisner-Jensen at TI - I'm
+guessing in the SSA group in Nice, France.
+
+I am now making a naming transition from MPFFS to TIFFS: there is really no
+link between this FFS format and the Openmoko+Pirelli duo, other than the
+happenstance of me having first encountered this FFS on these two GSM device
+brands, and the name TIFFS is more neutrally-descriptive.
+
+What it is
+==========
+
+In a rare departure from TI's norm (most of TI's GSM firmware and associated
+development tools suffer from heavy Windows poisoning), what I call TIFFS is
+very Unixy.  It is a file system with a hierarchical directory tree structure
+and with Unixy forward-slash-separated, case-sensitive pathnames; the semantics
+of "what is a file" and "what is a directory" are exactly the same as in UNIX;
+and TIFFS even supports symlinks, although that support is a little under-
+developed, and apparently no FFS symlinks were ever used in any production GSM
+device.  Thus the FFS implemented in TI-based GSM devices (modems and
+"dumbphones") is really no different from, for example, JFFS2 in embedded Linux
+systems.
+
+(The only traditional UNIX file system features which are missing in TIFFS are
+ the creation/modification/access timestamps and the ownership/permission
+ fields.)
+
+The FFS in a GSM device typically stores two kinds of content:
+
+* Factory data: IMEI, RF calibration values, device make/model/revision
+  ID strings etc.  These files are expected to be programmed on the factory
+  production line and not changed afterward.
+
+* Dynamic data written into the FFS in normal device operation: when you use a
+  "dumbphone" running TI-based firmware, every time you store something "on the
+  phone" or in "non-volatile memory", that item is actually stored in the FFS.
+  (Where else, if you think of it?)  That includes contacts and received SMS
+  stored "on the phone" instead of the SIM, any selections you make in the
+  settings/preferences menus which persist across reboots (power cycles), call
+  history etc.
+
+It needs to be noted that the "dynamic data" aspect of FFS usage applies not
+only to complete phones, but also to modems like the one used in the GTA01/02.
+One would naively think that non-volatile storage of data in flash outside of
+factory programming would be needed only in a device with its own UI, and that
+a modem subservient to external AT commands would be completely stateless
+across reboot/power cycles; but that is not the case in actuality.  TI's GSM
+firmwares, including the Openmoko ones (the "standard" mokoN), are designed to
+always "mount" their FFS with read/write access; TI's FFS implementation in the
+firmware has no concept of a "read-only mount".
+
+I am still investigating just what kinds of data are routinely written into the
+non-volatile FFS by the firmware in normal operation on devices like the GTA0x
+modem, but there most definitely are some.
+
+There is no hard separation between "static" and "dynamic" data in the file
+system structure; TIFFS is thus akin to an embedded Linux system with just a
+single root file system containing both "static" files like userland binaries
+and "dynamic" ones like configuration files under /etc which the user is
+expected to edit with vi after logging into the box, or log and similar files
+created by the system itself under /var, for example.
+
+Where it lives
+==============
+
+The type of flash memory used in Calypso GSM modems and "dumbphones" is called
+NOR flash.  This NOR flash memory is physically divided (by the design of the
+flash chip itself) into units called "sectors" or more descriptively, erase
+blocks.  The typical NOR flash sector size (in Calypso GSM devices) ranges from
+64 KiB in the GTA02 modem's NOR flash (4 MiB total) to 256 KiB in the
+S71PL129NC0 flash+RAM chip used in the Pirelli DP-L10 (16 MiB of flash total).
+The key physical property is that any bit may be changed from a '1' to a '0' at
+any time, in any combination, but resetting of '0' bits back to ones can be
+done only on the granularity of these largish sectors, in an operation called
+"sector erase".
+
+The location of TIFFS within the flash memory of a given GSM device is defined
+by the firmware design of that device, but is always some integral number of
+contiguous flash sectors.  Some examples:
+
+* On the GTA01/02 GSM modem, FFS occupies 7 sectors of 64 KiB each, starting at
+  flash offset 0x380000.
+
+* On the Pirelli DP-L10, the FFS used by the original proprietary fw occupies
+  18 sectors of 256 KiB each (for 4.5 MiB in total), starting at the beginning
+  of the 2nd flash chip select (0x02000000 in the ARM7 address space).
+
+* On Motorola/Compal C139/140 phones, the FFS used by the original proprietary
+  fw occupies 5 sectors of 64 KiB each (320 KiB in total), starting at 0x370000.
+  C11x/123 use smaller FFS configurations, whereas C155/156 seem to have
+  switched to some other FFS format, different from our familiar TIFFS.
+
+* The smallest real FFS configuration called for by the table in dev.c in TI's
+  original Leonardo fw source is 3 sectors of 64 KiB each; the same table also
+  sports a 4 KiB x 4 configuration for RAM-based testing (emulation of FFS in
+  RAM without real flash).
+
+* The largest FFS configuration that has been envisioned by the original
+  designers seems to be somewhere around 128 sectors.
+
+Each flash sector used for TIFFS begins with this 6-byte signature:
+
+46 66 73 23 10 02
+
+The first 4 bytes are 'Ffs#' in ASCII, and the following two bytes are the
+format version number of 0x0210 in little-endian byte order.  The following two
+bytes give a count of how many times that sector has been erased and rewritten
+(FF FF in "fresh" or "virgin" FFS images), and the following byte indicates
+that block's role and status in the FFS life cycle.
+
+How it works
+============
+
+Just like JFFS2 and other high-quality flash file systems, TIFFS is designed to
+recover gracefully from any possible power failure or crash: one can yank the
+battery from the GSM device (or induce a firmware crash) at the most mis-
+opportune moment in the middle of an FFS write operation, and the FFS is
+expected to recover on the next boot cycle.  I won't be able to document here
+all gory details of exactly how this goal is achieved, partly because I haven't
+studied the code to the requisite level of depth myself yet, but all of the
+responsible code lives under gsm-fw/services/ffs in this freecalypso-sw source
+tree; feel free to study it.
+
+In its "normal" or "clean" state (i.e., when not in the middle of a write
+operation or recovery from an ungracefully interrupted one), a TIFFS instance
+consists of the following 3 types of blocks:
+
+* One block containing inode records, indicated by AB in its type/flags/status
+  byte in the block header;
+* N-2 blocks (where N is the total number of flash sectors allocated for the
+  FFS) containing (or waiting to be filled with) data chunks - indicated by BD
+  in the type/flags/status byte;
+* One "free" block, indicated by BF - destined to become a new AB or a new BD
+  at some point.
+
+Each object written into the FFS (file, directory or symlink) consists of a
+16-byte inode record written into the AB block and a data chunk written into
+one of the BD blocks.  The data chunk includes the name of the object, hence
+one is required even for directories.  Data chunks are contiguous, uncompressed,
+and subject to an upper size limit of 2048 or 8192 bytes, depending on the FFS
+configuration.  Files larger than this limit are stored in a "segmented" form,
+giving rise to a 4th inode or object type (after file, directory and symlink):
+segment.  Each segment of a segmented file consists of not only a data chunk,
+but also an inode record for the segment, which gives the location of the data
+chunk and ties the segment object into the overall FFS structure, making it
+accessible.
+
+Because aside from complete sector erasure, flash memory bits can only
+transition from '1' to '0' but not the other way around, overwriting an existing
+file with some new content (an operation which any reasonable file system must
+implement in some way) cannot be done in place.  Instead like most flash file
+systems, TIFFS implements this common operation by writing the new version of
+the file to a new location (previously blank flash) and then invalidating the
+old version - and doing all that while keeping in mind the possibility of an
+ungraceful crash or powerdown at any moment, and the requirement of recovering
+gracefully from any such event.
+
+Of course as an FFS receives more write activity, even if one keeps overwriting
+some existing files with new content of the same size, without adding to the
+visible total content size (think du(1) command), eventually all remaining blank
+flash space will fill up.  At that point (or at some earlier point, depending
+on the FFS design and/or configuration) the FFS has to invoke a compaction or
+reclamation or garbage collection procedure: any "mixed" blocks containing both
+valid and stale data are transitioned into a "stale-only" state by having the
+active data moved to a new block, and then the "all stale" blocks are subjected
+to sector erasure, becoming new blank sectors.  The logic responsible for these
+operations once again needs to be resilient to the possibility of a crash or
+powerdown occurring at the most mis-opportune moment, and it also needs to
+implement flash wear leveling: there is a physical limit to how many times a
+given flash sector can be erased and rewritten before it goes bad.
+
+All of the above are common and well-known principles, successfully implemented
+in well-known flash file systems such as JFFS2 in Linux.  TIFFS is absolutely
+no different in this regard; for the implementation details, read the source
+code.
+
+How this FFS comes into being
+=============================
+
+(This section is only relevant to you if you plan on physically producing your
+ own GSM phones or modems on your own factory production line, like this author
+ fancies doing in the not-too-distant future, or if you simply enjoy knowing
+ how it is done.)
+
+To my knowledge, TI never used or produced a tool akin to mkfs.jffs2 in the
+embedded Linux world, which would produce a TIFFS image complete with some
+initial directory and file content "in vitro".  Instead it appears that the FFS
+instances found in shipped products such as Openmoko phones have been created
+"in vivo" by TI's firmware running on the device itself during the "production
+test" phase.
+
+The process seems to go like this:
+
+* When the printed circuit board is physically populated with components such
+  as the Calypso chip and the flash chip, the latter can be blank - if the
+  board design has the nIBOOT pin pulled low, enabling the Calypso boot ROM
+  (Openmoko and Pirelli both good on this one, but shame on Compal!), there is
+  no need to preprogram the flash chip with anything prior to populating it on
+  the board, and the device remains fully unbrickable at all times afterward.
+
+* When the assembled board is powered up for the first time, with completely
+  blank flash, the Calypso boot ROM will sit there and patiently wait for a
+  code download on either of its two UARTs.
+
+* Using TI's FLUID (Flash Loader Utility Independent of Device) or FreeCalypso's
+  fc-loadtool free replacement, the factory production station loads the main
+  firmware image into the flash.  Note, it is just the firmware image at this
+  step, and the FFS sectors remain blank.
+
+* The board is commanded to reboot (or power-cycled), and the firmware image
+  boots for the first time.
+
+* TI's FFS implementation code in their standard firmware reacts to all blank
+  flash in the FFS sectors as follows: it performs what they call the preformat
+  operation, writing the TIFFS signature and a BF state byte into every FFS
+  sector, but the main "format" operation, which sets up the AB/BD block roles,
+  creates the root inode and makes the FFS ready to accept the creation of its
+  first directories and files, is not done automatically.
+
+In order to perform the FFS format operation and then fill the new FFS with
+whatever directories and files are deemed needed to be present in "fresh"
+shipping products, the factory production station connects to the just-booted
+firmware running on the target via the RVT/ETM protocol (see the RVTMUX
+write-up), and sends "test mode" commands to this running firmware.  These
+"FFS test mode" (or TMFFS) commands include the format operation, an mkdir
+operation to create directories, and a "file write" operation akin to doing
+'cat > /dir/whatever/file', creating files in FFS and storing any desired data
+in them.
+
+The IMEI is assigned and written into FFS in this process, but it is not the
+only data item that will be unique for each individual device made.  Much more
+important are the RF calibration values: I have yet to learn exactly what is
+being (or needs to be) measured, how these measurements are performed (under
+what conditions; what external test equipment is needed), and how these measured
+and recorded RF calibration values affect GSM device operation, but this TI
+presentation gives some clues:
+
+ftp://ftp.ifctf.org/pub/GSM/Calypso/rf_calibration.pdf
+
+All of these calibration values are stored in a bunch of files under the
+/gsm/rf subtree, and these files seem to be "owned" by the L1 code.  The latter
+has RAM data structures which correspond to these files; upon normal boot the
+initialization code looks in FFS, and if it finds any of the RF calibration
+files, it reads each present file into the corresponding RAM data structure,
+overwriting the compiled-in defaults.  It appears (slightly uncertain because I
+have not yet reintegrated the code in question into our own gsm-fw) that the RF
+calibration files in FFS come into being as follows:
+
+* The RF calibration code in L1 (i.e., part of the main GSM fw) performs the
+  measurements and stores results in its RAM data structures as commanded by
+  the production test station through the "test mode" interface;
+
+* A final test mode command directs the above L1 code to write its RAM data
+  structures into FFS.
+
+Once I actually learn this RF calibration process properly in connection with
+building my own Calypso-based GSM "dumbphone", I'll be able to say exactly what
+it would take to recreate these RF calibration values if they are lost.  But
+until then the only advice I can give is to make a backup copy of your modem
+FFS with fc-loadtool, and to save it securely.
+
+Compal and Pirelli differences
+==============================
+
+The above description refers to TI's vanilla reference version, and it seems
+like Openmoko (FIC) was the only phone/modem manufacturer who followed it
+without major deviations.  In contrast, both Compal (Mot C1xx) and Foxconn
+(Pirelli DP-L10) moved the vital per-unit factory data (IMEI and RF calibration)
+out of the FFS into their own ad hoc flash data structures (which are very
+difficult to reverse-engineer and make use of, unfortunately), leaving their FFS
+only for less critical data.
+
+In Compal's case (at least on the C139 model with which I have extensive
+personal experience) the FFS stores only users' personal information and nothing
+more.  One can turn the phone off, use fc-loadtool to erase the FFS sectors, and
+boot the regular fw back up; the fw will automatically do a new FFS format (it
+even displays a message on the LCD as it does so) and carry on happily as a
+"fresh" or "blank", perfectly functional and usable phone.
+
+In Pirelli's case, booting their official fw with blank FFS sectors will also
+result in the FFS being automatically formatted, but their fw expects some
+static "asset" files to be present in this FFS: UI graphics and language
+strings, ringtones, firmware images for the WiFi and VoIP processors and some
+static configuration files, about 3 MiB in total.  Thus although the firmware
+will auto-format the blank FFS sectors, it won't function normally with all of
+these "asset" files missing.  Foxconn's original factory production line station
+must have uploaded these files to each phone via the TMFFS2 protocol, and our
+FreeCalypso suite now features a tool that can replicate this feat: fc-fsio.
+
+FreeCalypso support for TIFFS
+=============================
+
+Aside from implementing and using it in our own gsm-fw, FreeCalypso offers
+the following support for TIFFS:
+
+1. We have a utility for "in vitro" examination of FFS images read out of GSM
+   devices with fc-loadtool.  This tiffs utility (along with mokoffs and pirffs
+   wrappers) lives in the ffstools top-level directory of the freecalypso-sw
+   source tree.  This TIFFS "in vitro analyzer" utility supplants the earlier
+   mpffs-* tools, and adds some additional examination functionality.  It is
+   strictly a "read only" tool, however - it is not designed for "in vitro"
+   editing of TIFFS images.
+
+2. A number of FC tools may be strung together into a kit for editing the FFS
+   content of a GSM device, e.g., for changing the IMEI.  The following pieces
+   will be involved:
+
+* What is destined to eventually become our totally free GSM fw (the gsm-fw
+  source subtree at the top of freecalypso-sw) does not contain any of the
+  actual GSM protocol stack (or even L1) functionality yet, but it already
+  contains both the FFS code and those components (ETM and TMFFS[12]) which
+  are needed for interfacing an external "test mode shell" to this FFS
+  implementation through the RVTMUX interface.  And when our gsm-fw does gain
+  the actual GSM functionality, the ability to build a minimal FFS+ETM-only
+  configuration will still be retained.
+
+* The minimal FFS+ETM subset of gsm-fw can be built into a ramImage (runs
+  entirely from RAM via fc-xram, no flashing), and run on a physical device
+  such as the GTA0x GSM modem via the fc-xram host utility;
+
+* After loading the ramImage, fc-xram will immediately exec our rvinterf host
+  utility (see rvinterf/README);
+
+* Once the GSM device is running what is effectively an FFS editing agent out
+  of RAM, accessed via rvinterf over the serial channel, the user can run
+  fc-tmsh or fc-fsio, and this "test mode shell" provides commands for writing
+  things to FFS exactly like one would do in the factory production line
+  environment for which TI taylored their tools.
+
+The "in vivo" method of editing the FFS content of a GSM device described above
+will probably sound very convoluted, and you may find yourself asking for a way
+to do it "in vitro" instead: read the FFS out of flash with fc-loadtool, edit
+that image "in vitro" with some utility on your PC, and then use fc-loadtool
+again to program it back into your device.  But consider that an "in vitro" FFS
+modification would involve erasing and rewriting all sectors of your FFS,
+whereas an "in vivo" modification of some small file like the IMEI would be
+just a short flash write operation without any erasures at all, i.e., kinder
+on the flash.
+
+In any case, the "in vivo" method is already available now because all of the
+components involved therein are also needed for other development uses in the
+FreeCalypso project, whereas developing a fully-functional "in vitro"
+alternative (one that can create an FFS image "de novo" from a tree of files
+and directories a la mkfs.jffs2, or add new files to an existing TIFFS image
+etc) would be a good amount of extra work which we otherwise don't need - hence
+the latter is not very likely to be written any time soon.
+
+However, if the "in vitro" modification you seek is something trivial like
+changing the byte content of a file such as /pcm/IMEI or /gsm/com/rfcap without
+changing its length, you can use the existing "in vitro, read-only" tiffs host
+utility to find the exact byte location of the file data within the TIFFS image,
+and then use your favourite hex editor to whack whatever new byte content you
+like at that offset.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/TIFFS-old-description	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,461 @@
+The description of TIFFS that follows was originally written in the summer of
+SE52 (A.D. 2013), before the major TI source discoveries which happened later
+that year.  The text following the dividing line below has not been edited in
+content since it was written; for a newer write-up based on the current source-
+enabled understanding and reflecting the current FreeCalypso plans with respect
+to this FFS, see TIFFS-Overview.
+
+-------------------------------------------------------------------------------
+
+This is a description, based on reverse engineering, of the flash file system
+(FFS) implemented in Pirelli's original firmware for the DP-L10 GSM/WiFi dual
+mode mobile phone, and in the Closedmoko GTA0x modem firmware.  Not knowing the
+"proper" name for this FFS, and needing _some_ identifier to refer to it, I
+have named it Mokopir-FFS, from "Moko" and "Pirelli" - sometimes abbreviated
+further to MPFFS.
+
+(I have previously called the FFS in question MysteryFFS; but now that I've
+ successfully reverse-engineered it, it isn't as much of a mystery any more :-)
+
+At a high functional level, Mokopir-FFS presents the following features:
+
+* Has a directory tree structure like UNIX file systems;
+
+* The file system API that must be implemented inside the proprietary firmware
+  appears to use UNIX-style pathnames; doing strings on firmware images reveals
+  pathname strings like these:
+
+  /var/dbg/dar
+  /gsm/l3/rr_white_list
+  /gsm/l3/rr_medium_rxlev_thr
+  /gsm/l3/rr_upper_rxlev_thr
+  /gsm/l3/shield
+
+  Parsing the corresponding FFS image with tools included in the present
+  package has confirmed that the directory structure implied by these pathnames
+  does indeed exist in the FFS.
+
+* Absolutely no DOS-ish semantics seen anywhere: no 8.3 filenames and no
+  colon-separated device names (seen in the TSM30 file system source, for
+  example) are visible in the Closedmoko/Pirelli FFS.
+
+* File contents are stored uncompressed, but not necessarily contiguous: one
+  could probably store a file in FFS which is bigger than the flash sector
+  size, it which case it can never be contiguous in a writable FFS (see below),
+  and the firmware implementation seems to limit chunk sizes to a fairly small
+  number: on the Pirelli phones all largish files are divided into chunks of
+  8 KiB each, and on my GTA02 the largest observed chunk size is only 2 KiB.
+
+  The smaller files, like the IMEI and the firmware ID strings in my GTA02 FFS,
+  are contiguous.
+
+* The FFS structure is such that the length of "user" payload data stored in
+  each chunk (and consequently, in each file) can be known exactly in bytes,
+  with the files/chunks able to contain arbitrary binary data.  (This property
+  may seem obvious or trivial, as all familiar UNIX and DOS file systems have
+  it, but contrast with RT-11 for example.)
+
+* The flash file system is a writable one: the running firmware can create,
+  delete and overwrite files (and possibly directories too) in the live FFS;
+  thus the FFS design is such that allows these operations to be performed
+  within the physical constraints of NOR flash write operations.
+
+I have reverse-engineered this Mokopir-FFS on a read-only level.  What it means
+is that I, or anyone else who can read this document and the accompanying
+source for the listing/extraction utilities, can take a Mokopir-FFS image read
+out of a device and see/extract its full content: the complete directory tree
+and the exact binary byte content of all files contained therein.
+
+However, the knowledge possessed by the present hacker (and conveyed in this
+document and the accompanying source code) is NOT sufficient for constructing a
+valid Mokopir-FFS image "in vitro" given a tree of directories and files, or
+for making modifications to the file or directory content of an existing image
+and producing a content-modified image that is also valid; valid as in suitable
+for the original proprietary firmware to make its normal read and write
+operations without noticing anything amiss.
+
+Constructing "de novo" Mokopir-FFS images or modifying existing images in such
+a way that they remain 100% valid for all read and write operations of the
+original proprietary firmware would, at the very minimum, require an
+understanding of the meaning of *all* fields of the on-media FFS format.  Some
+of these fields are still left as "non-understood" for now though: a read-only
+implementation can get away with simply ignoring them, but a writer/generator
+would have to put *something* in those fields.
+
+As you read the "read-only" description of the Mokopir-FFS on-media format in
+the remainder of this document, it should become fairly obvious which pieces
+are missing before our understanding of this FFS can be elevated to a
+"writable" level.
+
+However, when it comes to writing new code to run on the two Calypso phones in
+question (Closedmoko and Pirelli), it seems, at least to the present hacker,
+that a read-only understanding of Mokopir-FFS should be sufficient:
+
+* In the case of Closedmoko GTA0x modems, the FFS is seen to contain the IMEI
+  and the RF calibration data.  The format of the former is obvious; the latter
+  not so much - but in any case, the information of interest is clearly of a
+  read-only nature.  It's difficult to tell (or rather, I haven't bothered to
+  experiment enough) whether the Closedmoko firmware does any writes to FFS or
+  if the FFS is treated as read-only outside of the production line environment,
+  but in any case, it seems to me that for any 3rd party replacement firmware,
+  the best strategy would be to treat the FFS as a read-only source of IMEI and
+  RF calibration data, and nothing more.
+
+* In the case of Pirelli phones, the FFS is used to store user data: sent and
+  received SMS (and MMS/email/whatever), call history, UI settings, pictures
+  taken with the camera, and whatever else.  It also stores a ton of files
+  which I can only presume were meant to be immutable except at the time of
+  firmware updates: graphics for the UI, ringtones, i18n UI strings, and even
+  "helper" firmware images for the WiFi and VoIP processors.  However, no IMEI
+  or RF calibration data are anywhere to be found in the FFS - instead this
+  information appears to be stored in the "factory block" at the end of the
+  flash (in its own sector) outside of the FFS.
+
+  Being able to parse FFS images extracted out of Pirelli phones "in vitro"
+  allows us to steal some of these helper files (UI artwork, ringtones,
+  WiFi/VoIP helpers), and some of these might even come useful to firmware
+  replacement projects, but it seems to me that a replacement firmware would
+  be better off using its own FFS design for storing user data, and as to
+  retrieving the original IMEI and RF calibration data, the original FFS isn't
+  of any use for that anyway.
+
+=======================
+Moko/Pirelli FFS format
+=======================
+
+OK, now that I'm done with the introduction, we can get to the actual
+Mokopir-FFS format.
+
+* On the GTA0x modem (or at least on my GTA02; my sample size is 1) the FFS
+  occupies 7 flash sectors of 64 KiB each at offsets 0x380000 through 0x3E0000,
+  inclusive.
+
+(The 4 MiB NOR flash chip used by Closedmoko has an independent R/W bank
+ division between the first 3 MiB and the last 1 MiB.  The first 3 MiB are used
+ to hold the field-flashable closed firmware images distributed as *.m0 files;
+ the independent last megabyte holds the FFS, and thus the FW could be
+ implemented to do FFS writes while running from flash in the main bank.
+ Less than half of that last megabyte appears to be used for the FFS though;
+ the rest appears to be unused - blank flash observed.)
+
+* On the Pirelli the FFS occupies 18 sectors of 256 KiB each at offsets 0
+  through 0x440000 (inclusive) of the 2nd flash chip select, the one wired to
+  nCS3 on the Calypso.
+
+Each flash sector allocated to FFS begins with the following signature:
+
+00000000:  46 66 73 23 10 02 xx yy  zz FF FF FF FF FF FF FF  Ffs#............
+
+The bytes shown as xx and yy above serve a non-understood purpose; as a guess,
+they may hold some info for the flash wear leveling algorithm: in a "virgin"
+FFS image like that found in my GTA02 (which never had a SIM card in it and
+never made or received a call) or read out of a "virgin" Pirelli phone that
+hasn't seen any active use yet, both of these bytes are FFs, but when I look at
+FFS images read out of the Pirelli which I currently use as my everyday-use
+cellphone, I see other values in sectors which must have been erased and
+rewritten.  A read-only implementation can ignore these bytes, as mine does.
+
+The byte shown as zz is more important though, even to a read-only
+implementation.  The 3 values I've encountered in this byte so far are AB, BD
+and BF.  Per my current understanding, in a "healthy" FFS exactly one sector
+will have AB in its header, exactly one will have BF, and the rest will have
+BD.  The meanings are (or appear to be):
+
+AB: the sector holds a vital data structure which I have called the active
+    index block;
+BD: the sector holds regular data;
+BF: the sector is blank except for the header, can be turned into a new AB or
+    BD.
+
+(Note that a flash program operation, which can turn 1s into 0s but not the
+ other way around, can turn BF into either AB or BD - but neither AB nor BD can
+ be turned into any other valid value.)
+
+In a "virgin" FFS image (as explained above) the first FFS sector is AB, the
+last one is BF, and the ones in between are BDs.
+
+An FFS read operation (a search for a given pathname, or a listing of all
+present directories and files) needs to start with locating the active index
+block - the FFS sector with AB in the header.  Following this header, which is
+treated as being 16 bytes long (almost everything in Mokopir-FFS is aligned on
+16-byte boundaries), the active index block contains a linear array of 16-byte
+records, each record describing an FFS object: directory, file or file
+continuation chunk.
+
+Here is my current understanding of the 16-byte index block record structure:
+
+2 bytes: Length of the described chunk in bytes
+1 byte:	 Purpose/meaning not understood, ignored by my current code
+1 byte:	 Object type
+2 bytes: Descendant pointer
+2 bytes: Sibling pointer
+4 bytes: Data pointer
+4 bytes: Purpose/meaning not understood, ignored by my current code
+
+(On the Calypso phones of interest, all multibyte fields are in the native
+ little-endian byte order of the ARM7TDMI processor.)
+
+The active index block gets filled with these records as objects are created;
+the first record goes right after the 'Ffs#'...AB header (padded to 16 bytes);
+the last record (at any given moment) is followed by blank flash for the
+remainder of the sector.  Records thus appear in the order in which they are
+created, which bears no direct relation to the directory tree structure.
+
+The objects, each described by a record in the index block, are organized into
+a tree structure by the descendant and sibling pointers, plus the object type
+indicator byte.  Let's start with the latter; the following objtype byte values
+have been observed:
+
+00: deleted object - a read-only implementation should ignore everything except
+    the descendant and sibling pointers.  (A write-capable implementation would
+    need more care - it would need a way of reclaiming dirty flash space taken
+    up by deleted/overwritten files.)
+
+E1: a special file - see the description of the /.journal file further down
+F1: a regular file (head chunk thereof)
+F2: a directory
+F4: file continuation chunk (explained below)
+
+Each record in the index block has an associated chunk in one of the data
+sectors; the index record contains fields giving the address and length of this
+chunk.  The length of a chunk is always a nonzero multiple of 16 bytes, and is
+stored (as a number in bytes) in the first 16-bit field of the 16-byte index
+entry.  The address of each chunk is given by the data pointer field of the
+index record, and it is reckoned in 16-byte units (thereby 16-byte alignment is
+required) from the beginning of the FFS sector group in the flash address space.
+
+For objects of type F1 and F2 (regular files and directories) the just-described
+chunk begins with the name of the file or subdirectory as a NUL-terminated ASCII
+string.  This name is just for the current level of the directory tree, just
+like in UNIX directories, thus one will have chunk names like gsm, l3, eplmn
+etc, rather than /gsm/l3/eplmn.  One practical effect is that one can't readily
+see pathnames or any of the directory structure by looking at an FFS image as a
+raw hex dump; the structure is only revealed when one uses a parsing program
+like those which accompany this document.
+
+In the case of directories, the "chunk" part of the object contains only the
+name of the directory itself, padded with FFs to a 16-byte boundary.  For
+example, an FFS directory named /gsm would be represented by an object
+consisting of two flash writes: a 16-byte entry in the active index block, with
+the object type byte set to F2, and a corresponding 16-byte chunk in one of the
+data sectors, with the 16 bytes containing "gsm", a terminating NUL byte, and
+12 FF bytes to pad up to 16.  In the case of files, this name may be followed
+by the first chunk of file data content, as explained further down.
+
+In order to parse the FFS directory tree (whether the objective is to dump the
+whole thing recursively or to find a specific file given a pathname), one needs
+to first (well, after finding the active AB block) find the root directory node.
+The root directory object is similar to other directory objects: it has a type
+of F2, and an associated chunk of 16 bytes in one of the data sectors.  The
+latter contains the name of the root node: on the Pirelli it is "/", whereas on
+my GTA02 it is "/ffs-root".
+
+The astute reader should notice that it really makes no sense to store a name
+for the root node, and indeed, this name plays no part in the traversal of the
+directory tree given an absolute pathname.  But instead this name, or rather
+its first character, appears to be used for the purpose of locating the root
+node itself.  At first I had assumed that the index record for the root node is
+always the first record in the active index block right after the signature
+header - that is how it is in "virgin" FFS images, and also in some quite non-
+virgin ones I have pulled from my daily-use Pirelli.  Naturally my first version
+of the Mokopir-FFS (then called MysteryFFS) extraction utility expected the root
+node to always be at index #1.  But then I got some additional Pirelli phones,
+and discovered that in certain cases, index record #1 is a deleted object (the
+original root node which has been deleted), and the new active root node is
+somewhere in the middle of the index!
+
+Thus it appears that in order to find the active root node, one needs to scan
+the active index block linearly from the beginning (disregarding the tree
+structure pointers in this initial pass), looking for a non-deleted object of
+type F2 (a directory) whose corresponding name chunk sports a name beginning
+with the '/' character.  (Anyone who's been raised in UNIX will immediately
+know that the path separator character '/' is the only character other than NUL
+that's absolutely forbidden in the individual filenames - so this special
+"root node name" is the only case of a '/' character appearing in what would
+otherwise be a regular filename.)
+
+[What causes the root node to be somewhere other than at index #1?  I assume it
+ has to do with the dirty space reclamation / data movement algorithm.  In a
+ "virgin" FFS image the very first sector is the active index block, and the
+ following sector is the first to hold chunks, beginning with the name chunk of
+ the root node.  Now what happens if all data in that sector aside from the
+ root node name and some other mostly-static directory names becomes dirty,
+ i.e., belonging to deleted or overwritten files?  How would that flash space
+ get reclaimed?  I assume that the FFS firmware algorithm moves all still-active
+ chunks to a new flash sector, invalidating the old copies - turning the latter
+ into deleted objects.  The root node will be among them.  Then at some point
+ the active index block is going to fill up too, and will need to be rewritten
+ into a new sector - at which point the previously-deleted index entries are
+ omitted and the root node becomes #1 again...]
+
+Tree structure
+
+Once the root node has been found, the descendant and sibling pointers are used
+to traverse the tree structure.  For each directory object, including the root
+node, the descendant pointer points to the first child object of this directory:
+the first file or subdirectory contained therein.  (Descendant and sibling
+pointers take the form of index numbers in the active index block.  A "nil"
+pointer is indicated by all 1s (FFFF) - the usual all-0s NULL pointer convention
+couldn't be used because it's flash, where the blank state is all 1s.)  If the
+descendant pointer of a directory object is nil, that means an empty directory.
+The sibling pointer of each file or directory points to its next sibling, i.e.,
+the next member of the same parent directory.  The sibling pointer of the root
+node is nil.
+
+Data content of files
+
+Objects of type F1 are the head chunks of files.  Each file has a head chunk,
+and may or may not have continuation chunks.  More precisely, the head chunk
+may contain only the name (or viewed alternatively, 0 bytes of data), or it may
+contain a nonzero number of payload bytes; orthogonally to this variability,
+there may or may not be continuation chunk(s) present.
+
+Continuation chunks
+
+The descendant pointer of each file head object (the object of type F1, the one
+reached by traversing the directory tree) indicates whether or not there are
+any continuation chunks present.  If this descendant pointer is nil, there are
+no continuation chunks; otherwise it points to the first continuation chunk
+object.  File continuation objects have type F4, don't have any siblings (the
+sibling pointer is nil - but see below regarding relocated chunks), and the
+descendant pointer of each continuation object points to the next continuation
+object, if there is one - nil otherwise.
+
+Payload data delineation
+
+Each chunk, whether head or continuation, always has a length that is a nonzero
+multiple of 16 bytes.  The length of the chunk here means the amount of flash
+space it occupies in its data sector - which is NOT equal to the payload data
+length.
+
+The head chunk of each file begins with the filename, terminated by a NUL byte.
+If there are any payload data bytes present in this head chunk (I'll explain
+momentarily how you would tell), the byte immediately after the NUL that
+terminates the filename is the first byte of the payload.  In the case of a
+continuation chunk, there is no filename and the first byte of the chunk is the
+first byte of that chunk's portion of the user data payload.
+
+Each data-containing chunk (head or continuation) has the following termination
+after the last byte of that chunk's payload data: one byte of 00, followed by
+however many bytes are needed ([0,15] range) of FFs to pad to a 16-byte
+boundary.  A file head chunk that has no payload data has the same format as a
+directory name chunk: filename followed by its terminating NUL followed by
+[0,15] bytes of FFs to pad to the next 16-byte boundary.
+
+When working with a head chunk, find the beginning of possible payload data (1
+byte after the filename terminating NUL) and find the end per the standard
+termination logic: scanning from the end of the chunk, skip FFs until 00 is
+found (encountering anything else is an error).  If the head chunk has no data,
+the effective data length (end_pointer - start_pointer) will be 0 or -1.  (The
+latter possibility is the most likely, as there will normally be a "shared" 00
+byte, serving as both the filename terminator and the 00 before the padding
+FF bytes.)
+
+Relocated chunks
+
+Let's go back to the scenario in which a particular data sector is full (no more
+usable free space left) and contains a mixture of active and dirty (deleted or
+invalidated) data.  How does the dirty flash space get reclaimed, so that the
+amount of available space (blank flash ready to hold new data) becomes equal to
+the total FFS size minus the total size of active files and overhead?  It can
+only be done by relocating the still-active objects from the full sector to a
+new one, invalidating the old copies, and once the old sector consists of
+nothing but invalidated data, subjecting it to flash erasure.
+
+So how do the active FFS objects get relocated from a "condemned" sector to a
+new one?  If the object is a directory, a new index entry is created, pointing
+to the newly relocated name chunk, but it is then made to fit into the old tree
+structure without disrupting the latter: the new index entry is added at the
+tail of the sibling-chain of the parent directory's descendants, the old index
+entry for the same directory is invalidated (as if the directory were rmdir'ed),
+and the descendant pointer of the newly written index entry is set to a copy of
+the descendant pointer from the old index entry for the same directory.  The
+same approach is used when the head chunk of a file needs to be relocated; in
+both cases a read-only FFS implementation doesn't need to do anything special to
+support reading file and directory objects that have been relocated in this
+manner.
+
+However, if the relocated object is a file continuation chunk, then the manner
+in which such objects get relocated does affect file reading code.  What if a
+chunk in the middle of a chain linked by "descend" pointers needs to be moved?
+What happens in this case is that the old copy of the chunk gets invalidated
+(the object type byte turned to 00) like in the other object relocating cases,
+and the sibling pointer of that old index entry (which was originally FFFF as
+continuation objects have no siblings) is set to point to the new index entry
+for the same chunk.  The "descend" pointer in the new index entry is a copy of
+that pointer from the old index entry.
+
+The manner of chunk relocation just described has been observed in the FFS
+images read out of my most recent batch of Pirelli phones - the same ones in
+which the root directory object is not at index #1.  Thinking about it as I
+write this, I've realized that the way in which continuation objects get
+relocated is exactly the same as for other object types - thus the compaction
+code in the firmware doesn't need to examine what object type it is moving.
+However, the case of continuation chunk relocation deserves special attention
+because it affects a read-only implementation like ours - the utilities whose
+source accompanies this document used to fail on these FFS images until I
+implemented the following additional handling:
+
+When following the chunk chain of a file, normally the only object type that's
+expected is F4 - any other object type is an error.  However, as a result of
+chunk relocation, one can also encounter deleted objects, i.e., type == 00.
+If such a deleted object is encountered, follow its sibling pointer, which must
+be non-nil.
+
+Journal file
+
+Every Mokopir-FFS image I've seen so far contains a special file named
+/.journal; this file is special in the following ways:
+
+* The object type byte is E1 instead of F1;
+* Unlike regular files, this special file is internally-writable.
+
+What I mean by the above is that regular files are mostly immutable: once a
+file has been created with some data content in the head chunk, it can only be
+either appended to (one or more continuation chunks added), or overwritten by
+creating a new file with the same name at the same level in the tree hierarchy
+and invalidating the old one.  But the special /.journal file is different: I
+have never observed it to consist of more than the head chunk, and this head
+chunk is pre-allocated with some largish and apparently fixed length (4 KiB on
+my GTA02, 16 KiB on the Pirelli).  This pre-allocated chunk contains what look
+like 16-byte records at the beginning (on the first 4-byte boundary after the
+NUL terminating the ".journal" name), followed by blank flash for the remainder
+of the pre-allocated chunk - so it surely looks like new flash writes happen
+within this chunk.
+
+I do not currently know the purpose of this /.journal file or the meaning of the
+records it seems to contain.  This understanding would surely be needed if one
+wanted to create FFS images from scratch or to implement FFS write operations,
+but I reason that a read-only implementation can get away with simply ignoring
+this file.  I reason that this file can't be necessary in order to parse an FFS
+image for reading because one needs to parse the tree structure first in order
+to locate this journal file itself.
+
+-------------------------------------------------------------------------------
+
+That's all I can think of right now.  If anything is unclear, see the
+accompanying source code for the listing/extraction utilities: with the general
+explanation given by this document, it should be clear what my code does and
+why.  And if a given piece of knowledge is found neither in this document nor
+in my source code, then I don't know it myself either, and my read-only
+Mokopir-FFS implementation makes do without it.
+
+All knowledge contained herein has been recovered by reverse engineering.
+Believe it or not, I have figured it out by staring at the hex dump of FFS
+sectors, reasoning about how one could possibly implement an FFS given the
+requirement of dynamic writability and the physical constraints of flash memory,
+and writing listing/extraction test code iteratively until I got something that
+appears to correctly parse all FFS images available to me - the result is the
+code in this package.
+
+I never got as far as attempting to locate the FFS implementation routines
+within the proprietary firmware binary code images, and I haven't found an
+implementation of this particular FFS in any of the leaked sources yet either.
+The TSM30 code doesn't seem to be of any use as its FFS appears to be totally
+different.  As to the more recently found LoCosto code leak, I found that one a
+few days *after* I got the Moko/Pirelli "MysteryFFS" reverse-engineered on my
+own, and when I did look at the FFS in the LoCosto code later, I saw what seems
+to be a different FFS as well.
+
+Michael Spacefalcon
+SE 52 Mes 16
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/linux-2.6.37.6-ftdi_sio.c.patch	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,29 @@
+--- ftdi_sio.c.orig	2011-03-27 11:01:41.000000000 -0800
++++ ftdi_sio.c	2015-10-30 13:18:40.879000032 -0800
+@@ -949,7 +949,7 @@
+ 	static const unsigned char divfrac[8] = { 0, 3, 2, 4, 1, 5, 6, 7 };
+ 	__u32 divisor;
+ 	/* divisor shifted 3 bits to the left */
+-	int divisor3 = base / 2 / baud;
++	int divisor3 = (base / 2 + baud / 2) / baud;
+ 	divisor = divisor3 >> 3;
+ 	divisor |= (__u32)divfrac[divisor3 & 0x7] << 14;
+ 	/* Deal with special cases for highest baud rates. */
+@@ -1087,6 +1087,17 @@
+ 	baud = tty_get_baud_rate(tty);
+ 	dbg("%s - tty_get_baud_rate reports speed %d", __func__, baud);
+ 
++	/*
++	 * FreeCalypso hack: translate non-std high
++	 * baud rates for GSM like CP2102 does.
++	 */
++	if (baud == 230400)
++		baud = 203125;
++	else if (baud == 460800)
++		baud = 406250;
++	else if (baud == 921600)
++		baud = 812500;
++
+ 	/* 2. Observe async-compatible custom_divisor hack, update baudrate
+ 	   if needed */
+ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,15 @@
+SUBDIR=	tiffs-rd tiffs-wrappers
+
+all:	${SUBDIR}
+
+${SUBDIR}: FRC
+	cd $@; ${MAKE} ${MFLAGS}
+
+clean: FRC
+	rm -f a.out core errs
+	for i in ${SUBDIR}; do (cd $$i; ${MAKE} ${MFLAGS} clean); done
+
+install: FRC
+	for i in ${SUBDIR}; do (cd $$i; ${MAKE} ${MFLAGS} install); done
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,31 @@
+You are looking at the source for the TIFFS In Vitro Analyzer utility.  You may
+have downloaded it either as a separate package or as part of the larger
+freecalypso-sw suite.
+
+See TIFFS-Overview (in ../doc if you are working with the full freecalypso-sw
+source tree) for a general description of what TIFFS is and why it matters.
+
+The utility contained in the present package runs on a general purpose GNU/Linux
+(or other Unix) host and enables "in vitro" examination of Flash File System
+images read out of TI-based GSM devices.  Using this utility, you can list the
+directory and file content of an FFS image, cat any individual file in the FFS,
+or extract the complete FFS content into your regular Unix file system.  Some
+"forensic" operations are also supported: by listing the inode array, one can
+deduce the order in which the present FFS content got created, and see what
+files have been overwritten or deleted in the span of still-visible history.
+One can then cat the old byte content of those overwritten or deleted files,
+if those data chunks are still in the FFS image (i.e., if the flash sector in
+question has not been reclaimed yet).
+
+Compilation and installation are straightforward: run 'make' to compile the
+source; you should get 3 executable binaries named tiffs, mokoffs and pirffs;
+then run 'make install' as root to install them in /usr/local/bin.  The binary
+named tiffs is the main program; mokoffs and pirffs are wrappers that simplify
+the most common current use cases.
+
+To install somewhere other than /usr/local/bin, edit the INSTBIN= setting in
+the subdirectory Makefiles.  You will also need to edit
+tiffs-wrappers/installpath.c accordingly, as the mokoffs and pirffs wrappers
+are designed to exec tiffs by its absolute installed pathname.
+
+See Usage for the usage instructions.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/Usage	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,148 @@
+The generic tiffs utility needs to be invoked as follows:
+
+tiffs [global-options] <imgfile> <org> <cmd> [command-args]
+
+The first 3 non-optional arguments are the filename of the TIFFS image under
+examination, the FFS organization being examined, and the operation to be
+performed.  The present utility is designed in the classic Unix manner in that
+each invokation performs a single operation and exits, such that invokations of
+tiffs (or one of the wrappers described below) may be plumbed into pipes and
+the like.
+
+The 2nd argument to tiffs after the FFS image filename describes how the TIFFS
+instance under study is organized in terms of flash sectors.  The syntax of
+this argument is KxN, where K is the flash sector size in KiB and N is the
+number of sectors occupied by the FFS.  For MokoFFS images the correct
+organization argument is 64x7 (7 sectors of 64 KiB each); for Pirelli's FFS
+images it is 256x18 (18 sectors of 256 KiB each).
+
+The following global options may be given before the image filename argument:
+
+-a num
+
+	Use the specified flash block (sector) as the inode array block.
+
+-o offset
+
+	The FFS image begins at the specified offset within the file, rather
+	than at the beginning.  This option is useful when working with complete
+	device flash dumps of which FFS is only a part, starting somewhere
+	other than at 0.
+
+-r ino
+
+	Use the specified inode as the root.  Per Falcon's convention, TIFFS
+	inode numbers are always given in hex, hence this argument is
+	interpreted as hex without needing a 0x prefix.
+
+The invokation syntax for mokoffs and pirffs wrappers is the same as for tiffs,
+except that the FFS organization argument (64x7 or 256x18) is omitted; the
+wrapper fills that argument in before passing the command to the main tiffs
+program.  The only other difference is that instead of the generic -o global
+option, mokoffs takes a -f global option (no argument) which indicates that one
+is working with a complete flash dump image, rather than just the FFS portion;
+mokoffs -f gets translated into tiffs -o0x380000.  (pirffs has no such option
+at all because Pirelli's FFS starts at offset 0 within its respective flash
+chip select.)
+
+The next argument after the FFS organization for tiffs (or after the image
+filename for mokoffs/pirffs) is the command (or operation) to be performed.
+The following tiffs commands are currently available:
+
+General information commands
+============================
+
+These commands display general or summary information about the FFS image:
+
+tiffs <...> blkhdr
+
+This command displays the basic information contained in the header of each
+flash erase block comprising the FFS image.
+
+tiffs <...> fsinfo
+
+This command displays some general information about the file system.
+
+Standard listing/extraction commands
+====================================
+
+These commands list or extract the normally-visible content of the FFS, i.e.,
+the content which is visible when the FFS is "mounted" normally, and which the
+FFS promises to preserve - as opposed to deleted or overwritten content.
+
+tiffs <...> ls [-v[v]] [pathname...]
+
+Tiffs ls without additional arguments yields a listing of the complete FFS
+directory tree, akin to tar tv.  Example output fragment:
+
+fr    4096 /.journal
+d          /gsm
+d          /gsm/rf
+d          /gsm/rf/tx
+f      512 /gsm/rf/tx/ramps.900
+f      128 /gsm/rf/tx/levels.900
+f      128 /gsm/rf/tx/calchan.900
+
+The first character is 'f' for files or 'd' for directories.  An 'r' following
+immediately afterward means that the object has the read-only attribute set.
+For files the listing includes the content size in bytes, and the last part is
+the pathname of the object within the FFS.
+
+With a single -v option added after ls, the output will include verbose
+information as to the segmentation structure of each file.  With two -v options
+or with -vv, this additional output will also include the byte offset of each
+data chunk, relative to the beginning of the FFS image.
+
+Tiffs ls with a pathname argument yields information about the specified FFS
+object; -v and -vv options act as already described, but are arguably more
+useful when listing single files.
+
+tiffs <...> cat [-v|-h] pathname
+
+Just like the standard Unix cat(1) command, but cat'ing files from the FFS image
+under study.  The non-standard -h option means hex dump - it is handy because
+almost all files in TI's GSM device FFS are binary, rather than ASCII.
+
+tiffs <...> xtr dest-dir
+
+This command extracts the complete content of the FFS into your ordinary Unix
+file system.  The sole argument is the local directory into which the root of
+the GSM device FFS should be extracted.
+
+Forensic analysis commands
+==========================
+
+Unlike the "standard" listing/extraction commands which present TIFFS as a
+"normal" Unix file system, using the "forensic" commands effectively requires
+that the operator understands how TIFFS works, in particular, what an inode is
+in TIFFS.
+
+tiffs <...> lsino [-v[v]]
+
+This command lists the FFS inode array from first to last; this listing order
+will normally correspond to the forward chronological order of object creation.
+-v and -vv options add verbosity.
+
+'.' in the object type column means segment, '~' means a deleted object.  The
+lsino command only lists the inode array, and does not try to recover the
+original type of deleted/overwritten objects from the journal or other clues.
+The program attempts to recover the pathname of each inode, but because such
+reverse mapping from inodes to pathnames is not an operation which TIFFS was
+properly designed to support, and the pathname recovery algorithm in this TIFFS
+IVA tool is made as generic as possible (doesn't look at the object types), the
+lsino listing will occasionally include some bogus pathnames.  Once again, it
+is expected that the operator knows what s/he is doing when using these forensic
+commands.
+
+tiffs <...> lsino [-v[v]] [-f] ino...
+
+This command works just like ls with an explicit pathname argument, but takes
+one or more inode numbers instead.  The -f option matters only if the requested
+inode is in the deleted/overwritten state; it tells the lsino command to assume
+that the object is/was the head inode of a file; -vf and -vvf combinations are
+particularly useful.
+
+tiffs <...> catino [-v|-h] ino
+
+Just like regular cat, but takes an inode number instead of a pathname.  Can be
+used to cat the old content of deleted or overwritten files.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	tiffs
+OBJS=	basics.o cat.o globals.o inode.o ls.o main.o object.o tree.o xtr.o
+HDRS=	globals.h pathname.h struct.h types.h
+INSTBIN=/usr/local/bin
+
+all:	${PROG}
+
+${PROG}:	${OBJS}
+	${CC} -o $@ ${OBJS}
+
+${OBJS}:	${HDRS}
+
+install:	${PROG}
+	mkdir -p ${INSTBIN}
+	install -c ${PROG} ${INSTBIN}
+
+clean:
+	rm -f ${PROG} *.o *.out *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/basics.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,155 @@
+/*
+ * This C module implements the "basics" of TIFFS image analysis.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+
+u8 tiffs_header[6] = {'F', 'f', 's', '#', 0x10, 0x02};
+
+read_ffs_image()
+{
+	int fd;
+	struct stat st;
+
+	fd = open(imgfile, O_RDONLY);
+	if (fd < 0) {
+		perror(imgfile);
+		exit(1);
+	}
+	fstat(fd, &st);
+	if (!S_ISREG(st.st_mode)) {
+		fprintf(stderr, "error: %s is not a regular file\n", imgfile);
+		exit(1);
+	}
+	if (st.st_size < imgfile_offset) {
+		fprintf(stderr,
+		"error: offset given with -o exceeds the size of the file\n");
+		exit(1);
+	}
+	if (st.st_size - imgfile_offset < total_ffs_size) {
+		fprintf(stderr,
+			"error: %s is shorter than FFS size of 0x%lx bytes\n",
+			imgfile, (u_long)total_ffs_size);
+		exit(1);
+	}
+	image = mmap(NULL, total_ffs_size, PROT_READ, MAP_PRIVATE, fd,
+			imgfile_offset);
+	if (image == MAP_FAILED) {
+		perror("mmap");
+		exit(1);
+	}
+	close(fd);
+}
+
+cmd_blkhdr()
+{
+	int blk;
+	u8 *blkhdr;
+
+	read_ffs_image();
+	for (blk = 0; blk < total_blocks; blk++) {
+		printf("Block %3d: ", blk);
+		blkhdr = image + blk * eraseblk_size;
+		if (bcmp(blkhdr, tiffs_header, sizeof tiffs_header)) {
+			printf("No TIFFS header\n");
+			continue;
+		}
+		printf("age %02X%02X, type/status %02X\n",
+			blkhdr[7], blkhdr[6], blkhdr[8]);
+	}
+	exit(0);
+}
+
+find_inode_block()
+{
+	int i, abcnt;
+	u8 *ptr;
+
+	if (index_blk_num >= 0) {
+		if (index_blk_num >= total_blocks) {
+			fprintf(stderr,
+				"invalid block # given with the -a option\n");
+			exit(1);
+		}
+		ptr = image + index_blk_num * eraseblk_size;
+		if (bcmp(ptr, tiffs_header, sizeof tiffs_header)) {
+			fprintf(stderr,
+			"error: block specified with -a has no TIFFS header\n");
+			exit(1);
+		}
+		if (ptr[8] != 0xAB) {
+			fprintf(stderr,
+			"error: block specified with -a is not an AB block\n");
+			exit(1);
+		}
+		inode_block = ptr;
+		return(0);
+	}
+	abcnt = 0;
+	for (ptr = image, i = 0; i < total_blocks; i++, ptr += eraseblk_size) {
+		if (bcmp(ptr, tiffs_header, sizeof tiffs_header)) {
+			fprintf(stderr,
+		"warning: no TIFFS signature in erase block #%d (offset %x)\n",
+				i, ptr - image);
+			continue;
+		}
+		switch (ptr[8]) {
+		case 0xAB:
+			if (verbose)
+				fprintf(stderr,
+			"Found AB index in erase block #%d (offset %x)\n",
+					i, ptr - image);
+			index_blk_num = i;
+			inode_block = ptr;
+			abcnt++;
+			continue;
+		case 0xBD:
+		case 0xBF:
+			continue;
+		}
+		fprintf(stderr,
+		"warning: unexpected block type/status %02X at offset %x\n",
+			ptr[8], ptr - image);
+	}
+	if (!inode_block) {
+		fprintf(stderr,
+			"error: could not find an active inode block in %s\n",
+			imgfile);
+		exit(1);
+	}
+	if (abcnt > 1) {
+		fprintf(stderr,
+			"error: found more than one AB block; use -a\n");
+		exit(1);
+	}
+	return(0);
+}
+
+cmd_fsinfo()
+{
+	read_ffs_image();
+	find_inode_block();
+	printf("Active inode block (AB) is block #%d\n", index_blk_num);
+	alloc_inode_table();
+	find_root_inode();
+	printf("Root inode is #%x\n", root_inode);
+	if (validate_obj_name(root_inode, 1)) {
+		printf("Root inode (format) name: %s\n",
+			inode_info[root_inode]->dataptr);
+		exit(0);
+	} else {
+		printf("No valid name found in the root inode!\n");
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/cat.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,233 @@
+/*
+ * This C module implements the cat and catino commands.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "pathname.h"
+
+int cat_mode;
+
+static u8 hex_buf[16];
+static int hex_fill;
+static u32 hex_offset;
+
+cat_hex_flush()
+{
+	int i, c;
+
+	printf("%08X:  ", hex_offset);
+	for (i = 0; i < 16; i++) {
+		if (i < hex_fill)
+			printf("%02X ", hex_buf[i]);
+		else
+			fputs("   ", stdout);
+		if (i == 7 || i == 15)
+			putchar(' ');
+	}
+	for (i = 0; i < hex_fill; i++) {
+		c = hex_buf[i];
+		if (c < ' ' || c > '~')
+			c = '.';
+		putchar(c);
+	}
+	putchar('\n');
+}
+
+cat_hex_byte(inb)
+{
+	hex_buf[hex_fill++] = inb;
+	if (hex_fill >= 16) {
+		cat_hex_flush();
+		hex_offset += hex_fill;
+		hex_fill = 0;
+	}
+}
+
+void
+cat_chunk(ch)
+	struct chunkinfo *ch;
+{
+	u8 *p;
+	int c;
+
+	if (!ch->len)
+		return;
+	switch (cat_mode) {
+	case 0:
+		write(1, ch->start, ch->len);
+		return;
+	case 1:
+		for (p = ch->start; p < ch->end; p++) {
+			c = *p;
+			if (c >= ' ' && c <= '~' || c == '\n')
+				putchar(c);
+			else {
+				if (c & 0x80) {
+					putchar('M');
+					putchar('-');
+					c &= 0x7F;
+				}
+				putchar('^');
+				if (c == 0x7F)
+					putchar('?');
+				else
+					putchar(c + '@');
+			}
+		}
+		return;
+	case 2:
+		for (p = ch->start; p < ch->end; p++)
+			cat_hex_byte(*p);
+		return;
+	}
+}
+
+void
+cat_finish()
+{
+	switch (cat_mode) {
+	case 1:
+		putchar('\n');
+		return;
+	case 2:
+		if (hex_fill)
+			cat_hex_flush();
+		return;
+	}
+}
+
+static void
+segment_cat_callback(inf, opaque)
+	struct inode_info *inf;
+	u_long opaque;
+{
+	struct chunkinfo chi;
+
+	size_extra_chunk(inf, &chi);
+	cat_chunk(&chi);
+}
+
+cmd_cat(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	int c, headino;
+	struct inode_info *inf;
+	struct chunkinfo chi;
+
+	optind = 0;
+	while ((c = getopt(argc, argv, "hv")) != EOF)
+		switch (c) {
+		case 'h':
+			cat_mode = 2;
+			continue;
+		case 'v':
+			cat_mode = 1;
+			continue;
+		default:
+usage:			fprintf(stderr, "usage: cat [-v|-h] pathname\n");
+			exit(1);
+		}
+	if (argc != optind + 1)
+		goto usage;
+
+	read_ffs_image();
+	find_inode_block();
+	alloc_inode_table();
+	find_root_inode();
+
+	headino = find_pathname(argv[optind]);
+	inf = inode_info[headino];
+	switch (inf->type) {
+	case 0xE1:
+	case 0xF1:
+		break;
+	case 0xF2:
+		fprintf(stderr, "error: the requested object is a directory\n");
+		exit(1);
+	case 0xF3:
+		fprintf(stderr,
+"error: the requested object is a symlink; use readlink instead of cat\n");
+		exit(1);
+	default:
+		fprintf(stderr, "error: unexpected object type %02X\n",
+			inf->type);
+		exit(1);
+	}
+	size_head_chunk(inf, &chi);
+	cat_chunk(&chi);
+	iterate_seg_file(headino, segment_cat_callback, 0L, 0, 0);
+	cat_finish();
+	exit(0);
+}
+
+cmd_catino(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	int c, headino;
+	struct inode_info *inf;
+	struct chunkinfo chi;
+
+	optind = 0;
+	while ((c = getopt(argc, argv, "hv")) != EOF)
+		switch (c) {
+		case 'h':
+			cat_mode = 2;
+			continue;
+		case 'v':
+			cat_mode = 1;
+			continue;
+		default:
+usage:			fprintf(stderr, "usage: catino [-v|-h] ino\n");
+			exit(1);
+		}
+	if (argc != optind + 1)
+		goto usage;
+	headino = strtoul(argv[optind], 0, 16);
+
+	read_ffs_image();
+	find_inode_block();
+	alloc_inode_table();
+	if (!validate_inode(headino)) {
+		fprintf(stderr, "catino: specified inode number is invalid\n");
+		exit(1);
+	}
+	inf = inode_info[headino];
+	switch (inf->type) {
+	case 0x00:
+	case 0xE1:
+	case 0xF1:
+	case 0xF3:
+		break;
+	case 0xF2:
+		fprintf(stderr, "error: the requested object is a directory\n");
+		exit(1);
+	default:
+		fprintf(stderr, "error: unexpected object type %02X\n",
+			inf->type);
+		exit(1);
+	}
+	if (!inf->len) {
+		fprintf(stderr, "error: requested inode has been reclaimed\n");
+		exit(1);
+	}
+	if (!validate_obj_name(headino, 0)) {
+		fprintf(stderr,
+"error: no valid name at the beginning of the requested seghead chunk\n");
+		exit(1);
+	}
+	size_head_chunk(inf, &chi);
+	cat_chunk(&chi);
+	iterate_seg_file(headino, segment_cat_callback, 0L, !inf->type, 0);
+	cat_finish();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/globals.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+/*
+ * Definitions of global variables for the tiffs IVA program.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+#include "struct.h"
+
+char *imgfile;
+off_t imgfile_offset;
+u32 eraseblk_size;
+int total_blocks;
+u32 total_ffs_size;
+int index_blk_num = -1, root_inode;
+int inode_limit;
+int verbose, verbose2;
+int old_16bit_location;
+
+u8 *image, *inode_block;
+struct inode_info **inode_info;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/globals.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,16 @@
+/*
+ * extern declarations of global variables
+ */
+
+extern char *imgfile;
+extern off_t imgfile_offset;
+extern u32 eraseblk_size;
+extern int total_blocks;
+extern u32 total_ffs_size;
+extern int index_blk_num, root_inode;
+extern int inode_limit;
+extern int verbose, verbose2;
+extern int old_16bit_location;
+
+extern u8 *image, *inode_block;
+extern struct inode_info **inode_info;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/inode.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,164 @@
+/*
+ * This C module implements the reading and decoding of inode information.
+ */
+
+#include <sys/types.h>
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+
+u8 blank_flash_line[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+			   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+alloc_inode_table()
+{
+	inode_info = malloc(sizeof(struct inode_info *) * inode_limit);
+	if (!inode_info) {
+		perror("malloc of inode table");
+		exit(1);
+	}
+	bzero(inode_info, sizeof(struct inode_info *) * inode_limit);
+}
+
+static int
+convert_ptr(in, infp)
+	int in;
+	int *infp;
+{
+	if (in == 0xFFFF) {
+		*infp = 0;
+		return(0);
+	}
+	if (in < 1 || in >= inode_limit)
+		return(-1);
+	*infp = in;
+	return(1);
+}
+
+validate_inode(ino)
+{
+	struct inode_flash *fl;
+	struct inode_info *inf;
+
+	if (ino < 1 || ino >= inode_limit)
+		return(0);
+	if (inode_info[ino])
+		return(1);
+	fl = (struct inode_flash *)inode_block + ino;
+	if (!bcmp(fl, blank_flash_line, sizeof blank_flash_line))
+		return(0);
+	inf = malloc(sizeof(struct inode_info));
+	if (!inf) {
+		perror("malloc of struct inode_info");
+		exit(1);
+	}
+	bzero(inf, sizeof(struct inode_info));
+	inf->ino = ino;
+	inf->len = le16toh(fl->len);
+	if (inf->len & 0xF) {
+		fprintf(stderr,
+			"warning: inode #%x: invalid length, skipping\n", ino);
+		free(inf);
+		return(0);
+	}
+	inf->type = fl->type;
+	switch (inf->type) {
+	case 0x00:
+		break;
+	case 0xE1:
+	case 0xF1:
+	case 0xF2:
+	case 0xF3:
+	case 0xF4:
+		if (!inf->len) {
+			fprintf(stderr,
+	"warning: inode #%x: non-deleted object has zero length, skipping\n",
+				ino);
+			free(inf);
+			return(0);
+		}
+		break;
+	default:
+		fprintf(stderr,
+		"warning: inode #%x: unexpected object type %02X, skipping\n",
+			ino, inf->type);
+		free(inf);
+		return(0);
+	}
+	if (convert_ptr(le16toh(fl->descend), &inf->descend) < 0) {
+		fprintf(stderr,
+		"warning: inode #%x: invalid descend pointer, skipping\n",
+			ino);
+		free(inf);
+		return(0);
+	}
+	if (convert_ptr(le16toh(fl->sibling), &inf->sibling) < 0) {
+		fprintf(stderr,
+		"warning: inode #%x: invalid sibling pointer, skipping\n",
+			ino);
+		free(inf);
+		return(0);
+	}
+	if (inf->len) {
+		inf->rawloc = le32toh(fl->dataptr);
+		if (old_16bit_location)
+			inf->rawloc >>= 16;
+		if (inf->rawloc > 0x0FFFFFFF) {
+invdptr:		fprintf(stderr,
+			"warning: inode #%x: invalid data pointer, skipping\n",
+				ino);
+			free(inf);
+			return(0);
+		}
+		inf->offset = inf->rawloc << 4;
+		if (inf->offset >= total_ffs_size)
+			goto invdptr;
+		if (inf->offset + inf->len > total_ffs_size) {
+			fprintf(stderr,
+"warning: inode #%x: data pointer + length > FFS total size, skipping\n",
+				ino);
+			free(inf);
+			return(0);
+		}
+		inf->dataptr = image + inf->offset;
+	}
+	inode_info[ino] = inf;
+	return(1);
+}
+
+find_root_inode()
+{
+	int ino;
+
+	if (root_inode) {
+		if (!validate_inode(root_inode)) {
+			fprintf(stderr,
+			"error: root inode specified with -r is invalid\n");
+			exit(1);
+		}
+		return(1);
+	}
+	for (ino = 1; ino < inode_limit; ino++) {
+		if (!validate_inode(ino))
+			continue;
+		if (inode_info[ino]->type != 0xF2)
+			continue;
+		if (*inode_info[ino]->dataptr != '/')
+			continue;
+		root_inode = ino;
+		if (verbose)
+			fprintf(stderr, "Found root inode at #%x\n", ino);
+		if (inode_info[ino]->sibling)
+			fprintf(stderr,
+		"warning: root inode #%x has a non-null sibling pointer\n",
+				ino);
+		return(0);
+	}
+	fprintf(stderr, "error: no root inode found; try -r\n");
+	exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/ls.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,338 @@
+/*
+ * This C module implements the ls and lsino commands.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "pathname.h"
+
+static void
+segment_size_callback(inf, opaque)
+	struct inode_info *inf;
+	u_long opaque;
+{
+	size_t *accump = (size_t *) opaque;
+	struct chunkinfo chi;
+
+	size_extra_chunk(inf, &chi);
+	*accump += chi.len;
+}
+
+size_t
+get_file_size(seghead_ino, deleted)
+{
+	struct chunkinfo chi;
+	size_t accum;
+
+	size_head_chunk(inode_info[seghead_ino], &chi);
+	accum = chi.len;
+	iterate_seg_file(seghead_ino, segment_size_callback, (u_long) &accum,
+			 deleted, 0);
+	return(accum);
+}
+
+static void
+segment_ls_callback(inf, opaque)
+	struct inode_info *inf;
+	u_long opaque;
+{
+	struct chunkinfo chi;
+
+	size_extra_chunk(inf, &chi);
+	if (verbose2 > 1)
+		printf("seg #%04x @%08x length=%lu\n", inf->ino, inf->offset,
+			(u_long) chi.len);
+	else
+		printf("seg #%04x length=%lu\n", inf->ino, (u_long) chi.len);
+}
+
+ls_seg_file(seghead_ino, deleted)
+{
+	struct inode_info *inf = inode_info[seghead_ino];
+	struct chunkinfo chi;
+
+	size_head_chunk(inf, &chi);
+	printf("%lu bytes in seghead", (u_long) chi.len);
+	if (verbose2 > 1)
+		printf(", starting at offset %lx",
+			(u_long)(inf->byte_after_name - image));
+	putchar('\n');
+	iterate_seg_file(seghead_ino, segment_ls_callback, 0L, deleted,
+			 verbose2 > 1);
+}
+
+void
+ls_tree_callback(pathname, ino, depth)
+	char *pathname;
+{
+	struct inode_info *inf = inode_info[ino];
+	u_long size;
+	char readonly;
+
+	if (inf->type & 0x10)
+		readonly = ' ';
+	else
+		readonly = 'r';
+	switch (inf->type) {
+	case 0xE1:
+	case 0xF1:
+		size = get_file_size(ino, 0);
+		printf("f%c %7lu %s\n", readonly, size, pathname);
+		if (verbose2)
+			ls_seg_file(ino, 0);
+		return;
+	case 0xE2:
+	case 0xF2:
+		printf("d%c         %s\n", readonly, pathname);
+		return;
+	case 0xE3:
+	case 0xF3:
+		printf("l%c         %s\n", readonly, pathname);
+		return;
+	default:
+		fprintf(stderr,
+			"BUG: bad inode byte %02X reached ls_tree_callback()\n",
+			inf->type);
+		exit(1);
+	}
+}
+
+ls_by_pathname(pathname)
+	char *pathname;
+{
+	int ino;
+	struct inode_info *inf;
+	char *type;
+
+	printf("%s\n", pathname);
+	ino = find_pathname(pathname);
+	printf("inode #%x\n", ino);
+	inf = inode_info[ino];
+	switch (inf->type) {
+	case 0xE1:
+		type = "read-only file";
+		break;
+	case 0xF1:
+		type = "file";
+		break;
+	case 0xF2:
+		type = "directory";
+		break;
+	case 0xF3:
+		type = "symlink";
+		break;
+	default:
+		type = "???";
+	}
+	printf("object type %02X (%s)\n", inf->type, type);
+	if (!validate_obj_name(ino, ino == root_inode)) {
+		printf("No valid object name in the chunk!\n");
+		exit(1);
+	}
+	printf("object name: %s\n", inf->dataptr);
+	if (inf->type == 0xF1 || inf->type == 0xE1) {
+		printf("total size: %lu bytes\n",
+			(u_long) get_file_size(ino, 0));
+		if (verbose2)
+			ls_seg_file(ino, 0);
+	}
+	putchar('\n');
+}
+
+cmd_ls(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	int c;
+
+	read_ffs_image();
+	find_inode_block();
+	alloc_inode_table();
+	find_root_inode();
+
+	optind = 0;
+	while ((c = getopt(argc, argv, "v")) != EOF)
+		switch (c) {
+		case 'v':
+			verbose2++;
+			continue;
+		default:
+			fprintf(stderr, "usage: ls [-v[v]] [pathname...]\n");
+			exit(1);
+		}
+	if (optind >= argc) {
+		traverse_visible_tree(ls_tree_callback);
+		exit(0);
+	}
+	for (; optind < argc; optind++)
+		ls_by_pathname(argv[optind]);
+	exit(0);
+}
+
+lsino_all()
+{
+	int ino, last_ino = 0;
+	struct inode_info *inf;
+	char pathname[PATHNAME_BUF_SIZE], typech;
+	int pathstat;
+	char descend_str[8], sibling_str[8];
+
+	for (ino = 1; ino < inode_limit; ino++) {
+		if (!validate_inode(ino))
+			continue;
+		if (ino != last_ino + 1)
+			printf("GAP in inode numbers\n");
+		inf = inode_info[ino];
+		pathstat = pathname_of_inode(ino, pathname);
+		if (pathstat < 0)
+			strcpy(pathname, "-nopath-");
+		switch (inf->type) {
+		case 0x00:
+			typech = '~';
+			break;
+		case 0xE1:
+		case 0xF1:
+			typech = 'f';
+			break;
+		case 0xF2:
+			typech = 'd';
+			break;
+		case 0xF3:
+			typech = 'l';
+			break;
+		case 0xF4:
+			typech = '.';
+			break;
+		default:
+			typech = '?';
+		}
+		printf("#%04x %c %s\n", ino, typech, pathname);
+		if (inf->type && !(inf->type & 0x10))
+			printf("\tread-only object\n");
+		if (ino == root_inode)
+			printf("\tactive root\n");
+		else if (inf->nparents < 1)
+			printf("\torphan\n");
+		else if (inf->nparents > 1)
+			printf("\tparent: #%x (%d)\n", inf->parent,
+				inf->nparents);
+		else if (pathstat < 0 || verbose2)
+			printf("\tparent: #%x\n", inf->parent);
+		if (verbose2 > 1) {
+			if (inf->descend)
+				sprintf(descend_str, "#%x", inf->descend);
+			else
+				strcpy(descend_str, "null");
+			if (inf->sibling)
+				sprintf(sibling_str, "#%x", inf->sibling);
+			else
+				strcpy(sibling_str, "null");
+			printf("\tchild: %s, sibling: %s\n",
+				descend_str, sibling_str);
+		}
+		if (!inf->len)
+			printf("\treclaimed\n");
+		last_ino = ino;
+	}
+	exit(0);
+}
+
+void
+lsino_one(ino, assume_file)
+{
+	struct inode_info *inf;
+	char pathname[PATHNAME_BUF_SIZE], *type;
+
+	if (!validate_inode(ino)) {
+		fprintf(stderr, "lsino: specified inode number is invalid\n");
+		exit(1);
+	}
+	printf("inode #%x\n", ino);
+	inf = inode_info[ino];
+	if (pathname_of_inode(ino, pathname) >= 0)
+		printf("Pathname: %s\n", pathname);
+	else
+		printf("No pathname found\n");
+	inf = inode_info[ino];
+	switch (inf->type) {
+	case 0x00:
+		type = "deleted";
+		break;
+	case 0xE1:
+		type = "read-only file";
+		break;
+	case 0xF1:
+		type = "file";
+		break;
+	case 0xF2:
+		type = "directory";
+		break;
+	case 0xF3:
+		type = "symlink";
+		break;
+	case 0xF4:
+		type = "segment";
+		break;
+	default:
+		type = "???";
+	}
+	printf("object type %02X (%s)\n", inf->type, type);
+	if (!inf->len) {
+		printf("This inode has been reclaimed\n\n");
+		return;
+	}
+	if (validate_obj_name(ino, 1))
+		printf("object name: %s\n", inf->dataptr);
+	else {
+		printf("No valid object name in the chunk\n\n");
+		return;
+	}
+	if (inf->type == 0xF1 || inf->type == 0xE1 ||
+	    !inf->type && assume_file) {
+		printf("total size: %lu bytes\n",
+			(u_long) get_file_size(ino, !inf->type));
+		if (verbose2)
+			ls_seg_file(ino, !inf->type);
+	}
+	putchar('\n');
+}
+
+cmd_lsino(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	int c, assume_file = 0, ino;
+
+	read_ffs_image();
+	find_inode_block();
+	alloc_inode_table();
+	find_root_inode();
+	treewalk_all();
+
+	optind = 0;
+	while ((c = getopt(argc, argv, "fv")) != EOF)
+		switch (c) {
+		case 'f':
+			assume_file++;
+			continue;
+		case 'v':
+			verbose2++;
+			continue;
+		default:
+			fprintf(stderr, "usage: lsino [-v[v]] [ino...]\n");
+			exit(1);
+		}
+	if (optind >= argc)
+		return lsino_all();
+	for (; optind < argc; optind++) {
+		ino = strtoul(argv[optind], 0, 16);
+		lsino_one(ino, assume_file);
+	}
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,128 @@
+/*
+ * This C module contains the main() function for the tiffs utility,
+ * dispatching control to different operation commands.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "types.h"
+#include "globals.h"
+
+parse_org_arg(arg)
+	char *arg;
+{
+	char *cp;
+
+	cp = index(arg, 'x');
+	if (!cp || !isdigit(cp[1]) || !isdigit(arg[0])) {
+		fprintf(stderr,
+		"error: TIFFS organization argument \"%s\" is invalid\n", arg);
+		exit(1);
+	}
+	*cp++ = '\0';
+	if (!strcmp(arg, "8"))
+		eraseblk_size = 0x2000;
+	else if (!strcmp(arg, "16"))
+		eraseblk_size = 0x4000;
+	else if (!strcmp(arg, "32"))
+		eraseblk_size = 0x8000;
+	else if (!strcmp(arg, "64"))
+		eraseblk_size = 0x10000;
+	else if (!strcmp(arg, "128"))
+		eraseblk_size = 0x20000;
+	else if (!strcmp(arg, "256"))
+		eraseblk_size = 0x40000;
+	else {
+		fprintf(stderr,
+			"error: \"%s\" is not a recognized flash sector size\n",
+			arg);
+		exit(1);
+	}
+	total_blocks = atoi(cp);
+	if (total_blocks < 1 || total_blocks > 128) {
+		fprintf(stderr,
+		"error: \"%s\" is not a reasonable number of FFS sectors\n",
+			cp);
+		exit(1);
+	}
+	total_ffs_size = eraseblk_size * total_blocks;
+	inode_limit = eraseblk_size >> 4;
+}
+
+extern int cmd_blkhdr();
+extern int cmd_cat();
+extern int cmd_catino();
+extern int cmd_fsinfo();
+extern int cmd_ls();
+extern int cmd_lsino();
+extern int cmd_xtr();
+
+static struct cmdtab {
+	char *cmd;
+	int (*func)();
+} cmdtab[] = {
+	{"blkhdr", cmd_blkhdr},
+	{"cat", cmd_cat},
+	{"catino", cmd_catino},
+	{"fsck", NULL},
+	{"fsinfo", cmd_fsinfo},
+	{"ls", cmd_ls},
+	{"lsino", cmd_lsino},
+	{"xtr", cmd_xtr},
+	{NULL, NULL}
+};
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c;
+	char *cmd;
+	struct cmdtab *tp;
+
+	while ((c = getopt(argc, argv, "+a:o:Or:v")) != EOF)
+		switch (c) {
+		case 'a':
+			index_blk_num = atoi(optarg);
+			continue;
+		case 'o':
+			imgfile_offset = strtoul(optarg, 0, 0);
+			continue;
+		case 'O':
+			old_16bit_location = 1;
+			continue;
+		case 'r':
+			root_inode = strtoul(optarg, 0, 16);
+			continue;
+		case 'v':
+			verbose++;
+			continue;
+		default:
+usage:			fprintf(stderr,
+			"usage: %s [global-options] <imgfile> <org> <op> ...\n",
+				argv[0]);
+			exit(1);
+		}
+	if (argc - optind < 3)
+		goto usage;
+	imgfile = argv[optind];
+	parse_org_arg(argv[optind+1]);
+	cmd = argv[optind+2];
+
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, cmd))
+			break;
+	if (!tp->func) {
+		fprintf(stderr,
+			"%s: operation \"%s\" is unknown or unimplemented\n",
+			argv[0], cmd);
+		exit(1);
+	}
+	optind += 2;
+	return tp->func(argc - optind, argv + optind);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/object.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,153 @@
+/*
+ * This C module implements object-level analysis.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "pathname.h"
+
+validate_obj_name(ino, root_special)
+{
+	struct inode_info *inf = inode_info[ino];
+	u8 *p, *endp;
+	int c;
+
+	if (!inf->len)
+		return(0);
+	p = inf->dataptr;
+	endp = p + inf->len;
+	for (; ; p++) {
+		if (p >= endp)
+			return(0);
+		c = *p;
+		if (!c)
+			break;
+		if (c < ' ' || c > '~')
+			return(0);
+		if (root_special || isalnum(c))
+			continue;
+		switch (c) {
+		case '.':
+		case ',':
+		case '_':
+		case '-':
+		case '+':
+		case '%':
+		case '$':
+		case '#':
+			continue;
+		default:
+			return(0);
+		}
+	}
+	if (!root_special) {
+		c = p - inf->dataptr;
+		if (c < 1 || c > MAX_FN_COMPONENT)
+			return(0);
+		if (!strcmp(inf->dataptr, ".") || !strcmp(inf->dataptr, ".."))
+			return(0);
+	}
+	inf->byte_after_name = p + 1;
+	return(1);
+}
+
+u8 *
+find_end_of_chunk(inf)
+	struct inode_info *inf;
+{
+	u8 *p;
+	int i;
+
+	p = inf->dataptr + inf->len;
+	for (i = 1; i <= 16; i++) {
+		if (!p[-i])
+			return(p - i);
+		if (p[-i] != 0xFF)
+			break;
+	}
+	fprintf(stderr,
+		"error: chunk @%x (inode #%x): no valid termination found\n",
+		inf->offset, inf->ino);
+	return(p);	/* bogon, allows the rest to continue */
+}
+
+size_head_chunk(inf, chi)
+	struct inode_info *inf;
+	struct chunkinfo *chi;
+{
+	chi->start = inf->byte_after_name;
+	chi->end = find_end_of_chunk(inf);
+	if (chi->start >= chi->end) {
+		chi->len = 0;
+		return(0);
+	} else {
+		chi->len = chi->end - chi->start;
+		return(1);
+	}
+}
+
+size_extra_chunk(inf, chi)
+	struct inode_info *inf;
+	struct chunkinfo *chi;
+{
+	chi->start = inf->dataptr;
+	chi->end = find_end_of_chunk(inf);
+	chi->len = chi->end - chi->start;
+}
+
+void
+iterate_seg_file(seghead, callback, callback_data, deleted, verbose)
+	void (*callback)();
+	u_long callback_data;
+{
+	int ino;
+	struct inode_info *inf;
+
+	for (ino = inode_info[seghead]->descend; ino; ino = inf->descend) {
+loop:		if (!validate_inode(ino)) {
+			fprintf(stderr,
+			"error: following seg file hit invalid inode #%x\n",
+				ino);
+			return;
+		}
+		inf = inode_info[ino];
+		switch (inf->type) {
+		case 0xF4:
+			callback(inf, callback_data);
+			continue;
+		case 0x00:
+			if (deleted) {
+				if (inf->len)
+					callback(inf, callback_data);
+				else
+					fprintf(stderr,
+	"error: presumed deleted segment inode #%x has been reclaimed\n",
+						ino);
+				continue;
+			}
+			if (!inf->sibling) {
+				fprintf(stderr,
+	"error: segment object at inode #%x: marked deleted, but no sibling\n",
+					ino);
+				return;
+			}
+			if (verbose)
+				printf("seg inode #%x deleted, moved to #%x\n",
+					ino, inf->sibling);
+			ino = inf->sibling;
+			goto loop;
+		default:
+			fprintf(stderr,
+	"error: inode #%x: unexpected type %02X when expecting segment (F4)\n",
+				ino, inf->type);
+			return;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/pathname.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,3 @@
+#define	MAX_FN_COMPONENT	20
+#define	MAX_DIR_NEST		6
+#define	PATHNAME_BUF_SIZE	((MAX_FN_COMPONENT+1) * (MAX_DIR_NEST+1) + 1)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/struct.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,47 @@
+/* actual TIFFS on-media structure */
+struct inode_flash {
+	u16	len;
+	u8	reserved1;
+	u8	type;
+	u16	descend;
+	u16	sibling;
+	u32	dataptr;
+	u16	sequence;
+	u16	updates;
+};
+
+struct journal_entry {
+	u8	status;
+	u8	objtype;
+	u16	this_ino;
+	u16	link_ptr;
+	u16	replacee;
+	u32	location;
+	u16	size;
+	u16	repli;	/* ??? */
+};
+
+/* our own distilled info struct */
+struct inode_info {
+	int	ino;
+	/* info from the inode record */
+	int	type;
+	int	descend;
+	int	sibling;
+	u16	len;
+	u32	rawloc;
+	u32	offset;
+	u8	*dataptr;
+	/* filled by treewalk */
+	int	nparents;
+	int	parent;
+	/* filled by misc */
+	u8	*byte_after_name;
+};
+
+/* chunk location and size info */
+struct chunkinfo {
+	u8	*start;
+	u8	*end;
+	size_t	len;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/tree.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,237 @@
+/*
+ * This C module implements operations on the tree level.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "pathname.h"
+
+static void
+visible_walk_dir(pathbuf_start, pathbuf_ptr, dirino, depth, callback)
+	char *pathbuf_start, *pathbuf_ptr;
+	void (*callback)();
+{
+	int ndepth = depth + 1;
+	int child;
+	struct inode_info *inf;
+
+	if (depth > MAX_DIR_NEST) {
+		fprintf(stderr,
+			"error: max dir nesting exceeded at inode #%x\n",
+			dirino);
+		return;
+	}
+	for (child = inode_info[dirino]->descend; child; child = inf->sibling) {
+		if (!validate_inode(child)) {
+			fprintf(stderr,
+			"error: walk of visible tree hit invalid inode #%x\n",
+				child);
+			return;
+		}
+		inf = inode_info[child];
+		switch (inf->type) {
+		case 0x00:
+			/* walking the *visible* tree: skip deleted objects */
+			continue;
+		case 0xF4:
+			fprintf(stderr,
+	"warning: directory #%x has child #%x of type segment (F4), skipping\n",
+				dirino, child);
+			continue;
+		}
+		if (!validate_obj_name(child, 0)) {
+			fprintf(stderr,
+		"visible tree walk error: no valid name for inode #%x\n",
+				child);
+			continue;
+		}
+		sprintf(pathbuf_ptr, "/%s", inf->dataptr);
+		callback(pathbuf_start, child, ndepth);
+		if (inf->type == 0xF2)
+			visible_walk_dir(pathbuf_start,
+					 index(pathbuf_ptr, '\0'), child,
+					 ndepth, callback);
+	}
+}
+
+traverse_visible_tree(callback)
+	void (*callback)();
+{
+	char pathbuf[PATHNAME_BUF_SIZE];
+
+	visible_walk_dir(pathbuf, pathbuf, root_inode, 0, callback);
+}
+
+/*
+ * The following function iterates through the descendants of a directory
+ * object looking for a specific directory-member filename.
+ *
+ * Arguments:
+ * - inode # of the parent directory
+ * - inode # of the first descendant (descendant pointer from the dir object)
+ * - filename to search for
+ *
+ * Returns: inode # of the sought descendant object if found, 0 otherwise.
+ */
+find_dir_member(dirino, first_descend, srchname)
+	char *srchname;
+{
+	int ino;
+	struct inode_info *inf;
+
+	for (ino = first_descend; ino; ino = inf->sibling) {
+		if (!validate_inode(ino)) {
+			fprintf(stderr,
+			"error: pathname search hit invalid inode #%x\n",
+				ino);
+			exit(1);
+		}
+		inf = inode_info[ino];
+		switch (inf->type) {
+		case 0x00:
+			/* walking the *visible* tree: skip deleted objects */
+			continue;
+		case 0xF4:
+			fprintf(stderr,
+	"warning: directory #%x has child #%x of type segment (F4), skipping\n",
+				dirino, ino);
+			continue;
+		}
+		if (!validate_obj_name(ino, 0)) {
+			fprintf(stderr,
+		"visible tree walk error: no valid name for inode #%x\n",
+				ino);
+			continue;
+		}
+		if (!strcmp(inf->dataptr, srchname))
+			return(ino);
+	}
+	return(0);
+}
+
+/*
+ * The following function searches for a pathname from the root down.
+ * Returns the inode # if found, otherwise exits with an error message
+ * indicating which step failed.
+ *
+ * Warning: the pathname in the argument buffer will be destroyed:
+ * 0s put in place of the slashes.
+ */
+find_pathname(pathname)
+	char *pathname;
+{
+	char *cur, *next;
+	int ino;
+	struct inode_info *inf;
+
+	cur = pathname;
+	if (*cur == '/')
+		cur++;
+	else {
+		fprintf(stderr,
+		"bad pathname \"%s\": TIFFS pathnames must be absolute\n",
+			pathname);
+		exit(1);
+	}
+	for (ino = root_inode; cur; cur = next) {
+		if (!*cur)
+			break;
+		next = index(cur, '/');
+		if (next == cur) {
+			fprintf(stderr,
+			"malformed pathname: multiple adjacent slashes\n");
+			exit(1);
+		}
+		if (next)
+			*next++ = '\0';
+		inf = inode_info[ino];
+		if (inf->type != 0xF2) {
+			fprintf(stderr,
+			"pathname search error: encountered a non-directory\n");
+			exit(1);
+		}
+		ino = find_dir_member(ino, inf->descend, cur);
+		if (!ino) {
+			fprintf(stderr,
+			"pathname search error: component name not found\n");
+			exit(1);
+		}
+	}
+	return(ino);
+}
+
+/*
+ * treewalk_all() walks the entire inode tree from the root down, without
+ * regard to object types, including deleted objects and even reclaimed ones.
+ * The output is the filling of the parent and nparents fields in the inode
+ * info array.
+ */
+
+static void
+treewalk_all_node(parent)
+{
+	int child;
+	struct inode_info *inf;
+
+	for (child = inode_info[parent]->descend; child; child = inf->sibling) {
+		if (!validate_inode(child)) {
+			fprintf(stderr,
+			"error: walk of complete tree hit invalid inode #%x\n",
+				child);
+			return;
+		}
+		inf = inode_info[child];
+		inf->parent = parent;
+		inf->nparents++;
+		if (inf->nparents >= inode_limit) {
+			fprintf(stderr,
+		"error: detected loop in inode tree at #%x, child of #%x\n",
+				child, parent);
+			return;
+		}
+		if (inf->nparents == 1)
+			treewalk_all_node(child);
+	}
+}
+
+treewalk_all()
+{
+	treewalk_all_node(root_inode);
+}
+
+pathname_of_inode(ino, pnbuf)
+	char *pnbuf;
+{
+	int level;
+	char *revpath[MAX_DIR_NEST+1];
+	struct inode_info *inf;
+	char *op;
+
+	for (level = 0; ino != root_inode; ino = inf->parent) {
+		if (!validate_obj_name(ino, 0))
+			return(-1);
+		inf = inode_info[ino];
+		if (!inf->parent)
+			return(-1);
+		if (level > MAX_DIR_NEST)
+			return(-1);
+		revpath[level++] = (char *) inf->dataptr;
+	}
+	op = pnbuf;
+	if (!level)
+		*op++ = '/';
+	while (level) {
+		level--;
+		*op++ = '/';
+		strcpy(op, revpath[level]);
+		op = index(op, '\0');
+	}
+	*op = '\0';
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/types.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,12 @@
+/*
+ * I like using u8/u16/u32, but they don't seem to be defined anywhere.
+ * So I solve the whole portability problem by defining them myself.
+ */
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed int s32;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-rd/xtr.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,105 @@
+/*
+ * This C module implements the xtr command.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "pathname.h"
+
+static void
+dump_head_chunk(fd, headino)
+{
+	struct inode_info *inf = inode_info[headino];
+	struct chunkinfo chi;
+
+	if (size_head_chunk(inf, &chi))
+		write(fd, chi.start, chi.len);
+}
+
+static void
+dump_extra_chunk(inf, opaque)
+	struct inode_info *inf;
+	u_long opaque;
+{
+	int fd = (int)opaque;
+	struct chunkinfo chi;
+
+	size_extra_chunk(inf, &chi);
+	write(fd, chi.start, chi.len);
+}
+
+extract_file(relpath, headino)
+	char *relpath;
+{
+	int fd;
+
+	fd = open(relpath, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+	if (fd < 0) {
+		perror(relpath);
+		exit(1);
+	}
+	dump_head_chunk(fd, headino);
+	iterate_seg_file(headino, dump_extra_chunk, (u_long)fd, 0, 0);
+	close(fd);
+}
+
+void
+xtr_tree_callback(pathname, ino, depth)
+	char *pathname;
+{
+	struct inode_info *inf = inode_info[ino];
+
+	switch (inf->type) {
+	case 0xE1:
+	case 0xF1:
+		/* skip /.journal; those who need it can cat it */
+		if (strcmp(pathname, "/.journal"))
+			extract_file(pathname + 1, ino);
+		return;
+	case 0xE2:
+	case 0xF2:
+		if (mkdir(pathname + 1, 0777) < 0) {
+			perror(pathname + 1);
+			exit(1);
+		}
+		return;
+	case 0xE3:
+	case 0xF3:
+		fprintf(stderr,
+	"symlink at %s ignored: symlink extraction not implemented yet\n",
+			pathname);
+		return;
+	default:
+		fprintf(stderr,
+		"BUG: bad inode byte %02X reached xtr_tree_callback()\n",
+			inf->type);
+		exit(1);
+	}
+}
+
+cmd_xtr(argc, argv)
+	char **argv;
+{
+	if (argc != 2) {
+		fprintf(stderr, "usage: xtr dest-dir\n");
+		exit(1);
+	}
+	read_ffs_image();
+	find_inode_block();
+	alloc_inode_table();
+	find_root_inode();
+	if (chdir(argv[1]) < 0) {
+		perror(argv[1]);
+		exit(1);
+	}
+	traverse_visible_tree(xtr_tree_callback);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-wrappers/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,22 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	mokoffs pirffs
+INSTBIN=/usr/local/bin
+
+MOKOFFS_OBJS=	installpath.o mokoffs.o
+PIRFFS_OBJS=	installpath.o pirffs.o
+
+all:	${PROGS}
+
+mokoffs:	${MOKOFFS_OBJS}
+	${CC} ${CFLAGS} -o $@ ${MOKOFFS_OBJS}
+
+pirffs:		${PIRFFS_OBJS}
+	${CC} ${CFLAGS} -o $@ ${PIRFFS_OBJS}
+
+install:	${PROGS}
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-wrappers/installpath.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,7 @@
+/*
+ * Our mokoffs and pirffs wrappers exec the main tiffs binary; in order
+ * to do it efficiently without execvp etc, we hard-code the pathname
+ * where that binary is installed.
+ */
+
+char tiffs_prog_pathname[] = "/usr/local/bin/tiffs";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-wrappers/mokoffs.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,86 @@
+/*
+ * mokoffs is a wrapper around tiffs: we pass the user's command along,
+ * together with any options, but insert the 64x7 FFS organization argument
+ * automatically.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+extern char tiffs_prog_pathname[];
+
+char *imgfile;
+char *aopt, *ropt;
+int fflag;
+char **passon_argv;
+int passon_argc;
+int output_argc;
+char **output_argv;
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c;
+	char **sp, **dp;
+
+	while ((c = getopt(argc, argv, "+a:fr:")) != EOF)
+		switch (c) {
+		case 'a':
+			aopt = optarg;
+			continue;
+		case 'f':
+			fflag++;
+			continue;
+		case 'r':
+			ropt = optarg;
+			continue;
+		default:
+usage:			fprintf(stderr,
+			"usage: %s [global-options] <imgfile> <op> ...\n",
+				argv[0]);
+			exit(1);
+		}
+	if (argc - optind < 2)
+		goto usage;
+	imgfile = argv[optind++];
+	passon_argv = argv + optind;
+	passon_argc = argc - optind;
+
+	output_argc = passon_argc + 3;
+	if (fflag)
+		output_argc++;
+	if (aopt)
+		output_argc += 2;
+	if (ropt)
+		output_argc += 2;
+	output_argv = malloc(sizeof(char *) * (output_argc + 1));
+	if (!output_argv) {
+		perror("malloc for tiffs argument list");
+		exit(1);
+	}
+	dp = output_argv;
+	*dp++ = "tiffs";
+	if (fflag)
+		*dp++ = "-o0x380000";
+	if (aopt) {
+		*dp++ = "-a";
+		*dp++ = aopt;
+	}
+	if (ropt) {
+		*dp++ = "-r";
+		*dp++ = ropt;
+	}
+	*dp++ = imgfile;
+	*dp++ = "64x7";
+	for (sp = passon_argv; *sp; sp++)
+		*dp++ = *sp;
+	*dp = 0;
+	execvp(tiffs_prog_pathname, output_argv);
+	perror(tiffs_prog_pathname);
+	exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ffstools/tiffs-wrappers/pirffs.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,78 @@
+/*
+ * pirffs is a wrapper around tiffs: we pass the user's command along,
+ * together with any options, but insert the 256x18 FFS organization argument
+ * automatically.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+extern char tiffs_prog_pathname[];
+
+char *imgfile;
+char *aopt, *ropt;
+char **passon_argv;
+int passon_argc;
+int output_argc;
+char **output_argv;
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c;
+	char **sp, **dp;
+
+	while ((c = getopt(argc, argv, "+a:r:")) != EOF)
+		switch (c) {
+		case 'a':
+			aopt = optarg;
+			continue;
+		case 'r':
+			ropt = optarg;
+			continue;
+		default:
+usage:			fprintf(stderr,
+			"usage: %s [global-options] <imgfile> <op> ...\n",
+				argv[0]);
+			exit(1);
+		}
+	if (argc - optind < 2)
+		goto usage;
+	imgfile = argv[optind++];
+	passon_argv = argv + optind;
+	passon_argc = argc - optind;
+
+	output_argc = passon_argc + 3;
+	if (aopt)
+		output_argc += 2;
+	if (ropt)
+		output_argc += 2;
+	output_argv = malloc(sizeof(char *) * (output_argc + 1));
+	if (!output_argv) {
+		perror("malloc for tiffs argument list");
+		exit(1);
+	}
+	dp = output_argv;
+	*dp++ = "tiffs";
+	if (aopt) {
+		*dp++ = "-a";
+		*dp++ = aopt;
+	}
+	if (ropt) {
+		*dp++ = "-r";
+		*dp++ = ropt;
+	}
+	*dp++ = imgfile;
+	*dp++ = "256x18";
+	for (sp = passon_argv; *sp; sp++)
+		*dp++ = *sp;
+	*dp = 0;
+	execvp(tiffs_prog_pathname, output_argv);
+	perror(tiffs_prog_pathname);
+	exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,17 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	fc-lcdemu
+OBJS=	globals.o main.o process.o window.o ximage.o xrm.o
+INSTBIN=/usr/local/bin
+
+all:	${PROG}
+
+${PROG}: ${OBJS}
+	${CC} ${CFLAGS} -o $@ ${OBJS} -lX11
+
+install:	${PROG}
+	mkdir -p ${INSTBIN}
+	install -c ${PROG} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROG}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/globals.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+/*
+ * LCDemu based on HECterm by the same author
+ * Definitions of global variables
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+
+char *progbasename, *proginstancename;
+char *mydisplayname;
+Display *mydisplay;
+Window mainwindow;
+GC mainwingc;
+int display_depth;
+
+XrmDatabase xrmdb_defaults, xrmdb_displayres, xrmdb_cmdline;
+XrmQuark xrmquark_topclass, xrmquark_topinstance;
+
+XImage *(*convert_function)();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/globals.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,16 @@
+/*
+ * LCDemu based on HECterm by the same author
+ * External declaratiions for global variables
+ */
+
+extern char *progbasename, *proginstancename;
+extern char *mydisplayname;
+extern Display *mydisplay;
+extern Window mainwindow;
+extern GC mainwingc;
+extern int display_depth;
+
+extern XrmDatabase xrmdb_defaults, xrmdb_displayres, xrmdb_cmdline;
+extern XrmQuark xrmquark_topclass, xrmquark_topinstance;
+
+extern XImage *(*convert_function)();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,166 @@
+/*
+ * LCDemu main module
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#include "globals.h"
+
+main(argc, argv)
+	char **argv;
+{
+	XrmInitialize();
+	process_cmdline(argc, argv);
+	open_display();
+	init_image_conversion();
+	load_resources();
+	create_our_window();
+	set_initial_window_title();
+	set_initial_icon_name();
+	create_mainwin_gc();
+	XMapWindow(mydisplay, mainwindow);
+	XFlush(mydisplay);
+
+	mainloop();
+	/* NOTREACHED */
+}
+
+process_cmdline(argc, argv)
+	char **argv;
+{
+	register char **ap, *opt;
+	char *rhost, *ruser;
+	int len;
+
+	if (argc < 1) {
+		fprintf(stderr, "fc-lcdemu: invalid invokation\n");
+		exit(1);
+	}
+	opt = rindex(argv[0], '/');
+	if (opt)
+		progbasename = opt + 1;
+	else
+		progbasename = argv[0];
+	proginstancename = progbasename;
+	for (ap = argv+1; *ap; ) {
+		if (**ap == '-')
+			opt = *ap++;
+		else
+			break;
+		if (!strcmp(opt, "-display")) {
+			if (!*ap) {
+argreq:				fprintf(stderr, "%s: %s requires an argument\n",
+					progbasename, opt);
+				exit(1);
+			}
+			mydisplayname = *ap++;
+			continue;
+		}
+		if (!strcmp(opt, "-name")) {
+			if (!*ap)
+				goto argreq;
+			proginstancename = *ap++;
+			continue;
+		}
+		if (!strcmp(opt, "-geometry") || !strcmp(opt, "-geom")) {
+			if (!*ap)
+				goto argreq;
+			XrmPutStringResource(&xrmdb_cmdline, "LCDemu.geometry",
+						*ap++);
+			continue;
+		}
+		if (!strcmp(opt, "-iconic")) {
+			XrmPutStringResource(&xrmdb_cmdline, "LCDemu.iconic",
+						"on");
+			continue;
+		}
+		if (!strcmp(opt, "-title")) {
+			if (!*ap)
+				goto argreq;
+			XrmPutStringResource(&xrmdb_cmdline, "LCDemu.title",
+						*ap++);
+			continue;
+		}
+		if (!strcmp(opt, "-borderwidth") || !strcmp(opt, "-bw")) {
+			if (!*ap)
+				goto argreq;
+			XrmPutStringResource(&xrmdb_cmdline, "*borderWidth",
+						*ap++);
+			continue;
+		}
+		if (!strcmp(opt, "-bordercolor") || !strcmp(opt, "-bd")) {
+			if (!*ap)
+				goto argreq;
+			XrmPutStringResource(&xrmdb_cmdline, "*borderColor",
+						*ap++);
+			continue;
+		}
+		if (!strcmp(opt, "-xrm")) {
+			if (!*ap)
+				goto argreq;
+			XrmPutLineResource(&xrmdb_cmdline, *ap++);
+			continue;
+		}
+		fprintf(stderr, "%s: %s: unrecognized option\n", progbasename,
+			opt);
+		exit(1);
+	}
+}
+
+open_display()
+{
+	if (!mydisplayname)
+		mydisplayname = getenv("DISPLAY");
+	if (!mydisplayname) {
+		fprintf(stderr, "%s: no X display available\n", progbasename);
+		exit(1);
+	}
+	mydisplay = XOpenDisplay(mydisplayname);
+	if (!mydisplay) {
+		fprintf(stderr, "%s: unable to open display %s\n", progbasename,
+			mydisplayname);
+		exit(1);
+	}
+}
+
+mainloop()
+{
+	register int i, cc;
+	XEvent event;
+	fd_set readfds;
+	int maxfd;
+	char buf[1024];
+
+	maxfd = ConnectionNumber(mydisplay) + 1;
+	for (;;) {
+		cc = XPending(mydisplay);
+		for (i = 0; i < cc; i++)
+			XNextEvent(mydisplay, &event);
+		XFlush(mydisplay);
+		FD_ZERO(&readfds);
+		FD_SET(0, &readfds);
+		FD_SET(ConnectionNumber(mydisplay), &readfds);
+		i = select(maxfd, &readfds, NULL, NULL, NULL);
+		if (i < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(0, &readfds)) {
+			cc = read(0, buf, sizeof buf);
+			if (cc > 0)
+				input_on_stdin(buf, cc);
+			else
+				exit(0);
+			XFlush(mydisplay);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/process.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,108 @@
+/*
+ * Processing of LCD output (input to us)
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#include "globals.h"
+
+#define	MAX_WIDTH	176
+
+static unsigned
+hexdecode(str)
+	char *str;
+{
+	unsigned accum = 0;
+	int i, c, n;
+
+	for (i = 0; i < 4; i++) {
+		c = str[i];
+		if (isdigit(c))
+			n = c - '0';
+		else if (isupper(c))
+			n = c - 'A' + 10;
+		else
+			n = c - 'a' + 10;
+		accum <<= 4;
+		accum |= n;
+	}
+	return(accum);
+}
+
+process_input_line(line)
+	char *line;
+{
+	int blitrow, blitcol, npix;
+	uint16_t pix16[MAX_WIDTH];
+	char *cp;
+	XImage *xi;
+
+	for (cp = line; isspace(*cp); cp++)
+		;
+	if (!isdigit(*cp)) {
+inv:		fprintf(stderr, "fc-lcdemu: invalid input line\n");
+		exit(1);
+	}
+	blitrow = atoi(cp);
+	while (isdigit(*cp))
+		cp++;
+	if (!isspace(*cp))
+		goto inv;
+	while (isspace(*cp))
+		cp++;
+	if (!isdigit(*cp))
+		goto inv;
+	blitcol = atoi(cp);
+	while (isdigit(*cp))
+		cp++;
+	if (!isspace(*cp))
+		goto inv;
+	while (isspace(*cp))
+		cp++;
+	if (!isxdigit(*cp))
+		goto inv;
+	for (npix = 0; *cp; ) {
+		if (!isxdigit(cp[0]) || !isxdigit(cp[1]) ||
+		    !isxdigit(cp[2]) || !isxdigit(cp[3]))
+			goto inv;
+		if (npix >= MAX_WIDTH) {
+			fprintf(stderr,
+		"fc-lcdemu error: input line exceeds MAX_WIDTH of %d pixels\n",
+				MAX_WIDTH);
+			exit(1);
+		}
+		pix16[npix++] = hexdecode(cp);
+		cp += 4;
+	}
+	xi = convert_function(pix16, npix);
+	XPutImage(mydisplay, mainwindow, mainwingc, xi, 0, 0, blitcol, blitrow,
+		  npix, 1);
+	XDestroyImage(xi);
+}
+
+input_on_stdin(inbuf, incount)
+	char *inbuf;
+{
+	char *input_end = inbuf + incount;
+	static char linebuf[1024];
+	static int linesz;
+	char *cp;
+
+	for (cp = inbuf; cp < input_end; cp++) {
+		if (*cp == '\n') {
+			linebuf[linesz] = '\0';
+			process_input_line(linebuf);
+			linesz = 0;
+			continue;
+		}
+		if (linesz < sizeof(linebuf) - 1)
+			linebuf[linesz++] = *cp;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/window.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,157 @@
+/*
+ * LCDemu based on HECterm by the same author
+ * X11 window creation functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#include "globals.h"
+
+extern char *xrm_lookup();
+
+create_our_window()
+{
+	XrmQuark instquarks[3], classquarks[3];
+	register char *cp;
+	register int i, geomask;
+	int pixwidth, pixheight, xpos, ypos;
+	XSetWindowAttributes xswa;
+	u_long xswamask;
+	XColor bdcolor;
+	XClassHint xclasshint;
+	XWMHints wmhints;
+	XSizeHints wm_normal_hints;
+
+	/* Determine our geometry */
+	instquarks[0] = xrmquark_topinstance;
+	classquarks[0] = xrmquark_topclass;
+	classquarks[1] = instquarks[1] = XrmStringToQuark("geometry");
+	instquarks[2] = classquarks[2] = NULLQUARK;
+	cp = xrm_lookup(instquarks, classquarks);
+	if (cp) {
+		geomask = XParseGeometry(cp, &xpos, &ypos, &pixwidth,
+					&pixheight);
+		free(cp);
+	} else
+		geomask = 0;
+	if (!(geomask & WidthValue))
+		pixwidth = 176;
+	if (!(geomask & HeightValue))
+		pixheight = 220;
+	if (!(geomask & XValue))
+		xpos = 0;
+	else if (geomask & XNegative)
+		xpos += DisplayWidth(mydisplay, DefaultScreen(mydisplay)) -
+			pixwidth;
+	if (!(geomask & YValue))
+		ypos = 0;
+	else if (geomask & YNegative)
+		ypos += DisplayHeight(mydisplay, DefaultScreen(mydisplay)) -
+			pixheight;
+	/* fill out XSetWindowAttributes */
+	xswa.event_mask = 0;	/* not interested in any events */
+	xswamask = CWEventMask;
+	/* border color */
+	classquarks[1] = instquarks[1] = XrmStringToQuark("borderColor");
+	cp = xrm_lookup(instquarks, classquarks);
+	if (cp) {
+		i = XParseColor(mydisplay, DefaultColormap(mydisplay,
+			DefaultScreen(mydisplay)), cp, &bdcolor);
+		free(cp);
+		if (i) {
+			i = XAllocColor(mydisplay, DefaultColormap(mydisplay,
+				DefaultScreen(mydisplay)), &bdcolor);
+			if (i) {
+				xswa.border_pixel = bdcolor.pixel;
+				xswamask |= CWBorderPixel;
+			}
+		}
+	}
+	/* border width */
+	classquarks[1] = instquarks[1] = XrmStringToQuark("borderWidth");
+	cp = xrm_lookup(instquarks, classquarks);
+	if (cp) {
+		i = atoi(cp);
+		free(cp);
+	} else
+		i = 2;
+	/* go for it! */
+	mainwindow = XCreateWindow(mydisplay, DefaultRootWindow(mydisplay),
+			xpos, ypos, pixwidth, pixheight, i, CopyFromParent,
+			InputOutput, CopyFromParent, xswamask, &xswa);
+	/* set window manager properties */
+	xclasshint.res_name = proginstancename;
+	xclasshint.res_class = "LEDemu";
+	XSetClassHint(mydisplay, mainwindow, &xclasshint);
+	wmhints.flags = InputHint | StateHint;
+	wmhints.input = False;
+	classquarks[1] = instquarks[1] = XrmStringToQuark("iconic");
+	cp = xrm_lookup(instquarks, classquarks);
+	if (cp) {
+		i = parse_boolean_resource(cp);
+		free(cp);
+	} else
+		i = 0;
+	wmhints.initial_state = i ? IconicState : NormalState;
+	XSetWMHints(mydisplay, mainwindow, &wmhints);
+	if (geomask & (WidthValue|HeightValue))
+		wm_normal_hints.flags = USSize;
+	else
+		wm_normal_hints.flags = PSize;
+	if (geomask & (XValue|YValue))
+		wm_normal_hints.flags |= USPosition;
+	XSetWMNormalHints(mydisplay, mainwindow, &wm_normal_hints);
+}
+
+set_initial_window_title()
+{
+	XrmQuark instquarks[3], classquarks[3];
+	register char *cp;
+	char buf[256];
+
+	instquarks[0] = xrmquark_topinstance;
+	classquarks[0] = xrmquark_topclass;
+	instquarks[1] = XrmStringToQuark("title");
+	classquarks[1] = XrmStringToQuark("Title");
+	instquarks[2] = classquarks[2] = NULLQUARK;
+	cp = xrm_lookup(instquarks, classquarks);
+	if (cp) {
+		XStoreName(mydisplay, mainwindow, cp);
+		free(cp);
+		return;
+	}
+	XStoreName(mydisplay, mainwindow, "Emulated LCD");
+}
+
+set_initial_icon_name()
+{
+	XrmQuark instquarks[3], classquarks[3];
+	register char *cp;
+
+	instquarks[0] = xrmquark_topinstance;
+	classquarks[0] = xrmquark_topclass;
+	instquarks[1] = XrmStringToQuark("iconName");
+	classquarks[1] = XrmStringToQuark("IconName");
+	instquarks[2] = classquarks[2] = NULLQUARK;
+	cp = xrm_lookup(instquarks, classquarks);
+	if (cp) {
+		XSetIconName(mydisplay, mainwindow, cp);
+		free(cp);
+		return;
+	}
+	XSetIconName(mydisplay, mainwindow, proginstancename);
+}
+
+create_mainwin_gc()
+{
+	XGCValues xgcval;
+
+	xgcval.graphics_exposures = False;
+	mainwingc = XCreateGC(mydisplay, mainwindow, GCGraphicsExposures,
+				&xgcval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/ximage.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,59 @@
+/*
+ * LCDemu based on HECterm by the same author
+ * XImage conversion muck
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#include "globals.h"
+
+XImage *
+convert_image_depth24(input, npix)
+	uint16_t *input;
+	int npix;
+{
+	uint32_t *imgbuf;
+	int i, in, r, g, b;
+	XImage *img;
+
+	imgbuf = malloc(npix * 4);
+	if (!imgbuf) {
+		perror("malloc");
+		exit(1);
+	}
+	for (i = 0; i < npix; i++) {
+		in = input[i];
+		r = (in & 0xF800) << 8;
+		g = (in & 0x07E0) << 5;
+		b = (in & 0x001F) << 3;
+		imgbuf[i] = r | g | b;
+	}
+	img = XCreateImage(mydisplay, CopyFromParent, display_depth, ZPixmap,
+			   0, (char *) imgbuf, npix, 1, 32, 0);
+	if (!img) {
+		perror("XCreateImage");
+		exit(1);
+	}
+	return(img);
+}
+
+init_image_conversion()
+{
+	display_depth = DefaultDepth(mydisplay, DefaultScreen(mydisplay));
+	switch (display_depth) {
+	case 24:
+		convert_function = convert_image_depth24;
+		break;
+	default:
+		fprintf(stderr,
+"error: fc-lcdemu has not been adapted for X11 depth != 24, yours is %d\n",
+			display_depth);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lcdemu/xrm.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,97 @@
+/*
+ * LCDemu based on HECterm by the same author
+ * Xrm functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <X11/Xlib.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+#include "globals.h"
+
+static char appdefaults_pathname[] =
+		"/usr/local/share/freecalypso/lcdemu-defaults";
+
+load_resources()
+{
+	xrmquark_topclass = XrmStringToQuark("LCDemu");
+	xrmquark_topinstance = XrmStringToQuark(proginstancename);
+	xrmdb_defaults = XrmGetFileDatabase(appdefaults_pathname);
+	xrmdb_displayres =
+		XrmGetStringDatabase(XResourceManagerString(mydisplay));
+}
+
+/*
+ * The following function looks up a resource in all of our databases
+ * and returns a pointer (char *) to the value in a malloced buffer that
+ * can be freed when it is no longer needed.  My reading of X11R4
+ * documentation indicates that resource values returned from Xrm functions
+ * are not necessarily NUL-terminated (no claim is made that they are
+ * and XrmValue structure has a size field), which is why I copy to
+ * my own buffer and NUL-terminate it there.
+ *
+ * Returns NULL pointer if not found in any of the databases.
+ */
+char *
+xrm_lookup(instquarks, classquarks)
+	XrmQuark *instquarks, *classquarks;
+{
+	XrmRepresentation reptype;
+	XrmValue value;
+	register char *buf;
+
+	if (XrmQGetResource(xrmdb_cmdline, instquarks, classquarks, &reptype,
+	    &value))
+		goto found;
+	if (XrmQGetResource(xrmdb_displayres, instquarks, classquarks, &reptype,
+	    &value))
+		goto found;
+	if (XrmQGetResource(xrmdb_defaults, instquarks, classquarks, &reptype,
+	    &value))
+		goto found;
+	return(NULL);
+found:	buf = malloc(value.size + 1);
+	if (!buf) {
+		perror("malloc");
+		exit(1);
+	}
+	bcopy(value.addr, buf, value.size);
+	buf[value.size] = '\0';
+	return(buf);
+}
+
+parse_boolean_resource(str)
+	register char *str;
+{
+	if (!strcasecmp(str, "on") || !strcasecmp(str, "true") ||
+	    !strcasecmp(str, "yes"))
+		return(1);
+	if (!strcasecmp(str, "off") || !strcasecmp(str, "false") ||
+	    !strcasecmp(str, "no"))
+		return(0);
+	return(atoi(str));
+}
+
+get_boolean_resource(resource, def)
+	char *resource;
+	int def;
+{
+	XrmQuark instquarks[3], classquarks[3];
+	register char *cp;
+	register int i;
+
+	instquarks[0] = xrmquark_topinstance;
+	classquarks[0] = xrmquark_topclass;
+	classquarks[1] = instquarks[1] = XrmStringToQuark(resource);
+	instquarks[2] = classquarks[2] = NULLQUARK;
+	cp = xrm_lookup(instquarks, classquarks);
+	if (cp) {
+		i = parse_boolean_resource(cp);
+		free(cp);
+	} else
+		i = def;
+	return(i);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/CHANGES	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,31 @@
+Changes from loadtools-r2 to fc-host-tools-r3:
+
+* Implemented support for Mot C1xx (Compal) targets in addition to the
+  previously supported Openmoko and Pirelli.  Compal phones have different
+  flash, require a different procedure for gaining code download access, and
+  have additional bricking concerns.
+
+* Updated loadagent includes Intel flash support and ABB commands needed for
+  Compal phones.
+
+* The range of IRAM addresses used by loadagent changed to allow gsm-fw
+  ramImage builds (chain-loaded with fc-xram) to load their IRAM code section
+  directly into place.
+
+* Loadtool's flash program-bin command now automatically performs a CRC-32
+  verification after programming.
+
+* Miscellaneous minor polish.
+
+Changes from loadtools-r1 to loadtools-r2:
+
+* A flash ID check has been implemented in fc-loadtool, invoked automatically
+  before doing any erase or program operations, or explicitly at any time with
+  the flash info command.  This check ensures that the type of flash chip in
+  the target GSM device is the same as what loadtool thinks it is, based on the
+  hardware parameters file.
+
+* fc-xram command line syntax changed slightly in order to support immediate
+  passing of the serial line to rvinterf/rvtdump.
+
+* Miscellaneous minor polish.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,43 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	fc-iram fc-loadtool fc-xram fc-compalram
+INSTBIN=/usr/local/bin
+
+EXTRA_OBJ=	compalload.o
+
+COMPALRAM_OBJS=	compalload.o compalram.o defpath.o sercomm.o ttypassthru.o
+
+IRAM_OBJS=	defpath.o hexdecode.o hwparam.o hwparamstubs.o romload.o \
+		sercomm.o sertool.o srecreader.o ttypassthru.o ${EXTRA_OBJ}
+
+LOADTOOL_OBJS=	crc32tab.o defpath.o flashops.o flcmplboot.o flmain.o flmisc.o \
+		flprogbin.o flprogsrec.o flutil.o hexdecode.o hwparam.o \
+		labaud.o ltdispatch.o ltdump.o ltexit.o lthelp.o ltmain.o \
+		ltmisc.o ltpassthru.o ltscript.o romload.o sercomm.o \
+		srecreader.o tpinterf.o tpinterf2.o tpinterf3.o ${EXTRA_OBJ}
+
+XRAM_OBJS=	chainload.o clmain.o defpath.o hexdecode.o hwparam.o \
+		hwparamstubs.o initscript.o labaud.o romload.o sercomm.o \
+		srecreader.o tpinterf.o ttypassthru.o ${EXTRA_OBJ}
+
+all:	${PROGS}
+
+fc-compalram:	${COMPALRAM_OBJS}
+	${CC} ${CFLAGS} -o $@ ${COMPALRAM_OBJS}
+
+fc-iram:	${IRAM_OBJS}
+	${CC} ${CFLAGS} -o $@ ${IRAM_OBJS}
+
+fc-loadtool:	${LOADTOOL_OBJS}
+	${CC} ${CFLAGS} -o $@ ${LOADTOOL_OBJS}
+
+fc-xram:	${XRAM_OBJS}
+	${CC} ${CFLAGS} -o $@ ${XRAM_OBJS}
+
+install:
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+	./install-helpers.sh
+
+clean:
+	rm -f *.o *.out *errs ${PROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,265 @@
+The set of host tools built in this directory consists of:
+
+fc-loadtool		The tool for operating on Calypso GSM devices at a low
+			level.  After "breaking" into the target GSM device in
+			its boot process and getting FreeCalypso loadagent
+			running on the target (out of Calypso internal RAM, aka
+			IRAM), loadtool presents an interactive command prompt
+			with commands for peeking and poking registers and most
+			importantly, reading and writing any part of the
+			device's non-volatile flash memory.
+
+fc-iram & fc-xram	These utilities are intended for FreeCalypso developers
+			only.  They load an S-record code image into IRAM or
+			XRAM, respectively, induce a transfer of control to the
+			loaded code, and then drop into a serial line pass-thru
+			mode for the operator to interact with the thus loaded
+			target code.
+
+The currently supported target devices are the Compal family of basic
+dumbphones, the Openmoko GTA0x GSM modem and the Pirelli DP-L10 feature phone.
+
+All tools in the FreeCalypso loadtools suite work by feeding pieces of code to
+the target device as it boots, preventing the booting of its regular firmware
+and diverting control to these externally-loaded code pieces.  These pieces of
+ARM7 target code need to be installed on the host system running loadtools,
+normally in /usr/local/share/freecalypso:
+
+loadagent	This is the "agent" code that runs on the target device when
+		fc-loadtool is operating on it: loadtool carries out its
+		operations by sending commands to loadagent.  There is only one
+		version of loadagent for all currently supported Calypso
+		targets: loadagent does not access any resources outside of the
+		Calypso chip itself unless commanded to do so, and loadtool
+		supports different target devices with different hardware
+		configurations by sending different commands to loadagent as
+		appropriate.
+
+compalstage	For Compal phones only: a little piece of code that is fed to
+		the original fw's bootloader via the serial download protocol
+		provided by the latter; it re-enables the Calypso chip boot ROM
+		and jumps to it, allowing our loadagent to be loaded in the
+		same way as on freedom-enabled devices.
+
+If you are working with a development snapshot of the freecalypso-sw source
+tree, you will need to compile and install a GNU cross-compiler toolchain
+targeting ARM7 (see ../toolchain) and then use that toolchain to compile
+loadagent and compalstage (see ../target-utils) before you can successfully use
+loadtools to operate on a target device.  End-user oriented releases of
+FreeCalypso host tools will include prebuilt loadagent and compalstage binaries
+in the target-binaries subdirectory.
+
+Installing
+==========
+
+Just run 'make' and 'make install' as usual.  If the target-binaries directory
+is present, your installation will be complete and ready to use.  If you are
+building these pieces yourself from source, do a 'make' and 'make install' in
+../target-utils, after you have the ARM7 gcc toolchain installed and working.
+
+Basic usage
+===========
+
+The steps for bringing up fc-loadtool to operate on a target Calypso device are
+as follows:
+
+1. If you are using a USB serial adapter, or operating on a Pirelli phone that
+   has one built in, connect the USB side first so that the necessary
+   /dev/ttyUSB* device node appears.
+
+2. Run fc-loadtool like this:
+
+   fc-loadtool $TARGETOPT /dev/ttyXXX
+
+   Change /dev/ttyXXX to the actual serial port you are using, and change
+   $TARGETOPT to:
+
+   Device		Needed options
+   -----------------------------------
+   Mot C11x/123		-h compal
+   Mot C139/140		-h compal -c 1003
+   Mot C155/156		-h c155
+   Openmoko GTA02	-h gta02
+   Pirelli DP-L10	-h pirelli
+
+3. Cause the target device to execute its boot path.  Openmoko GTA0x and
+   Pirelli DP-L10 targets have the Calypso boot ROM enabled, and will interrupt
+   and divert their normal boot path when they "hear" the beacons which
+   fc-loadtool will be sending down the serial line.  Compal phones have this
+   boot ROM disabled at the board level, but their standard firmware includes a
+   flash-resident bootloader that offers a different way of interrupting the
+   boot path and loading code over the serial line; fc-loadtool will be set up
+   to speak the latter protocol when run with the corresponding options from
+   the table above.
+
+You will see messages showing fc-loadtool's progress with feeding first
+compalstage (if needed), then loadagent (always needed) to the target device,
+followed by some target-specific initialization done via loadagent commands.
+If all of the above succeeds, you will land at a loadtool> prompt.  Type
+'help', and it will guide you from there.  Alternatively, you can familiarize
+yourself with loadtool commands and operations without actually running it by
+reading the loadtool.help text file.
+
+Command line options
+====================
+
+The fc-loadtool command lines shown above will usually be sufficient.  However,
+here is the complete command line description for all 3 tools:
+
+fc-iram [options] ttyport iramimage.srec
+fc-xram [options] ttyport xramimage.srec [2ndprog]
+fc-loadtool [options] ttyport
+
+The available options are common for all 3 utilities, with a few noted
+exceptions:
+
+-a /path/to/loadagent
+
+	This option applies only to fc-loadtool and fc-xram.  It specifies the
+	pathname at which the required loadagent.srec image should be sought,
+	overriding the compiled-in default.
+
+-b baud
+
+	This option is common for all 3 utilities.  It selects the baud rate
+	to be used when pushing the IRAM image to the Calypso boot ROM.  In the
+	case of fc-iram, the selected baud rate will be in effect when the
+	loaded IRAM image is jumped to and fc-iram drops into the serial tty
+	pass-thru mode; in the case of fc-loadtool, it will be the initial baud
+	rate for communicating with loadagent, which can be switched later with
+	the baud command.  The default is 115200 baud.
+
+-B baud
+
+	This option is specific to fc-xram.  It selects the baud rate to be
+	used when pushing the XRAM image to loadagent.  If no -B option is
+	specified, fc-xram will communicate with loadagent at the same baud
+	rate that was used to load loadagent itself via the Calypso boot ROM
+	download protocol, i.e., the rate selected with -b, defaulting to
+	115200 baud if no -b option was given either.  Neither -b nor -B
+	affects the baud rate that will be in effect when the loaded XRAM image
+	is jumped to and fc-xram drops into the serial tty pass-thru mode: that
+	baud rate independently defaults to 115200 baud and can only be changed
+	with the -r option.
+
+-c <compalstage flavor>
+
+	This option is common for all 3 utilities.  It directs the tools to
+	perform the Compal loading stage before proceeding with the Calypso
+	boot ROM serial protocol, and selects the "flavor" of compalstage to
+	use.  As you can see in the source, compalstage is built in 3 different
+	versions, for different C1xx models which exhibit different quirks.
+
+	This option overrides the compal-stage setting given in the hardware
+	parameter file selected with -h or -H; the -c or -C option must be given
+	after -h or -H in order to take effect.  -c none disables the Compal
+	stage and causes the tools to proceed directly to the Calypso boot ROM
+	phase, even on targets for which the hardware parameter file specifies
+	compal-stage.
+
+-C /path/to/compalstage-binary
+
+	This option is just like -c, except that the given argument is used
+	directly as the compalstage binary file pathname (absolute or relative)
+	without checking or alteration.
+
+-h hwtype
+
+	This option is common for all 3 utilities.  It selects the specific
+	target device configuration to be used.  More precisely, it constructs
+	a pathname of the form /usr/local/share/freecalypso/%s.config, where %s
+	is the argument given to this option, and uses that file as the hardware
+	parameter file.
+
+	The hardware configurations known to the present release of FreeCalypso
+	loadtools are listed in the "Basic usage" section above.
+
+-H /path/to/hwparam-file
+
+	This option is just like -h, except that the given argument is used
+	directly as the hardware parameter file pathname (absolute or relative)
+	without alteration.
+
+-i num
+
+	This option is common for all 3 utilities.  It specifies the interval
+	in milliseconds at which the tool will send "please interrupt the boot
+	process" beacons out the serial port, hoping to catch the Calypso
+	internal boot ROM.  The default is 13 ms.
+
+-n
+
+	This option does anything only when loadtools have been compiled to run
+	on GTA0x AP (see the corresponding section below).  If you've compiled
+	loadtools with the -DGTA0x_AP_BUILD option, it has an effect of making
+	each tool automatically toggle the modem power control upon startup,
+	removing the need for manual sequencing of the Calypso boot process.
+	This -n option suppresses that action, making the AP build behave like
+	the standard build in this regard.
+
+-r baud	(fc-loadtool)
+
+	This option is specific to fc-loadtool.  It causes the tool to skip its
+	normal steps of feeding loadagent and possibly compalstage to the target
+	via special serial protocols, and instead assume that the target is
+	already running loadagent, communicating at the specified baud rate.
+	In other words, reattach to an already running loadagent.  Use this
+	option if your fc-loadtool session has been terminated ungracefully and
+	you would like to reattach and resume, rather than forcibly reset the
+	target by yanking and reinserting the battery and restart from the
+	beginning.
+
+-r baud	(fc-xram)
+
+	This option is specific to fc-xram.  It selects the serial line baud
+	rate which should be set just before the loaded XRAM image is jumped
+	to; the default is 115200 baud.
+
+fc-xram 2nd program invokation
+==============================
+
+The fc-xram utility can take two possible actions after it has loaded the
+specified S-record image into XRAM:
+
+* The default action, in the absence of additional command line arguments, is
+  to drop into a serial tty pass-thru mode, just like fc-iram.
+
+* The alternative action is to invoke a 2nd program and pass the serial
+  communication channel to it.  This 2nd program invokation facility is intended
+  primarily for passing the serial communication channel to rvinterf or rvtdump
+  from the FreeCalypso software suite, not for launching any arbitrary 3rd-party
+  programs from fc-xram.
+
+The intended usage scenario is that one builds a version of the FreeCalypso GSM
+firmware (or some subset thereof, such as an "in vivo" FFS editing agent) in the
+ramImage configuration, fc-xram is used to load that ramImage into the target
+device, and then the serial communication channel (RVTMUX) is immediately taken
+over by rvinterf or rvtdump.
+
+Openmoko GTA0x
+==============
+
+All of the above instructions assume that you are running these loadtools on a
+general-purpose host system such as a GNU/Linux PC or laptop, and will
+potentially use them to operate on multiple Calypso targets of different kinds.
+If instead you are building loadtools to run on the application processor of a
+smartphone such as Openmoko GTA0x, then it makes no sense for that special build
+of loadtools to support any target other than the specific modem in that
+smartphone.  Loadtools can be built with compalstage support excluded and with
+GTA0x-specific modem power control included instead.  This build will still
+include a bunch of functions of no relevance to GTA0x, but oh well..
+
+To build loadtools for the GTA0x AP, you'll need to make the following
+modifications to the Makefile:
+
+* Change the CC= line to point to the appropriate cross-compiler (which you'll
+  need to provide yourself);
+
+* Change the CFLAGS= line: add the right options to target the ARM920T core in
+  the GTA0x AP (e.g., -march=armv4t -mtune=arm920t), and add -DGTA0x_AP_BUILD
+  to enable some code that makes sense only when running on the GTA0x AP.
+
+* Change EXTRA_OBJ= from listing compalload.o to listing compaldummy.o and
+  gtapower.o instead.
+
+See gta-ap-build.sed for an example.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/README.old	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,271 @@
+You are looking at the source for the FreeCalypso loadtools package.  You may
+have downloaded it either as a separate package or as part of the larger
+freecalypso-sw suite.
+
+The tools in this package are written to run on some Unix/Linux machine
+(normally a PC/Linux desktop or laptop) that acts as a host for operating on
+Calypso target devices.  All of these tools communicate with the Calypso target
+through a serial port; each tool begins its operation by sending special byte
+sequences to this serial port which are designed to interrupt the Calypso
+device boot process in the ROM bootloader.
+
+Three utilities are currently built as part of FreeCalypso loadtools:
+
+fc-iram & fc-xram	These utilities are intended for FreeCalypso developers
+			only.  They load an S-record code image into IRAM or
+			XRAM, respectively, induce a transfer of control to the
+			loaded code, and then drop into a serial line pass-thru
+			mode for the operator to interact with the thus loaded
+			target code.
+
+fc-loadtool		This utility is intended for both developers and end
+			users.  After establishing communication with the
+			target, fc-loadtool drops into interactive operation.
+			Once at the loadtool> prompt, you can peek and poke
+			registers, and most importantly, dump (read) and load
+			(program) the flash memory of the target device.
+
+Loadagent
+=========
+
+Both fc-loadtool and fc-xram work by first feeding a FreeCalypso-developed
+program called loadagent to the Calypso ROM bootloader; all further operations
+(loading code into XRAM or flash) are done via this loadagent.  An S-record
+image of the loadagent program is required for fc-loadtool and fc-xram to work.
+That program is in turn built with the ARM7 toolchain.
+
+If you are working with the full freecalypso-sw suite, you presumably already
+have the proper ARM7 toolchain built and installed.  To build loadagent, simply
+run 'make' in the ../target-utils tree.
+
+If you have downloaded a separately-packaged version of FreeCalypso loadtools,
+the package should have a prebuilt loadagent.srec image included, sparing
+non-developer users the nontrivial hurdle of having to build and install a
+special cross-compilation toolchain.  The same loadagent binary is designed to
+work on all supported Calypso targets.
+
+Building and installing loadtools
+=================================
+
+Normally the machine on which you build and install fc-loadtools would be your
+PC/Linux desktop or laptop, the system you would use to program or otherwise
+interact with Calypso phones by way of appropriate USB-to-phone cables.  Just
+like loadagent, the host utilities you are going to build and install aren't
+specific to a particular target device; instead you will select the target
+device at run time via a command line option.  Hence you can build and install
+the host utilities (usual 'make' and 'make install') without limiting your
+setup to just one target phone type.
+
+However, if your intended target device is an Openmoko GTA02 (or GTA01)
+smartphone, there is one additional complication: one cannot directly access
+the Calypso part of these phones from the outside without going through the
+phone's application processor first.  If you would like to use fc-loadtool to
+read or write the GSM flash memory of your GTA0x (load a different firmware
+image, dump the flash file system for backup or examination, restore a previous
+backup etc), there are two ways to do it:
+
+1. The recommended way for FreeCalypso developers is to get a special serial
+   cable (low voltage, as in 3.3V or lower - *NOT* RS-232 levels - please don't
+   fry your precious phone!) that would plug into the 2.5mm jack on the left
+   side of the phone that is normally intended for a wired headset.  This way
+   you can use your regular build of fc-loadtool (and fc-iram & fc-xram) on
+   your PC/Linux (or other) development host, no need to build anything for
+   GTA0x AP, and all communication happens directly between your development
+   host and the Calypso part of your target phone - not going through the AP
+   at all.  You still need working software on the GTA0x AP to do battery
+   management, to power the Calypso block on and off, and to enable the headset
+   jack "download" path, but it is much less burdensome than having to do the
+   actual FreeCalypso work from the AP.
+
+Having the headset jack do double duty as a programming port is actually a
+standard practice in the world of basic (non-smart) cellular phones, and
+furthermore, the pinout used by FIC on the GTA0x phones just happens to be
+exactly the same as that used by Compal/Motorola - hence the same headset jack
+serial cables that are used by OsmocomBB with the latter phones (the famous
+"T191 unlock cable") will also work for connecting from an external host
+directly to the Calypso part of GTA0x phones.
+
+2. If you are an end user who simply wishes to reflash a different GSM firmware
+   image, it can be done from inside the phone (from the AP) without having to
+   acquire special hardware (as in the cable described above).  However, the
+   trade-off is that in return for saving on the special hardware, you have to
+   do more work on the software.  You will have to use a cross-compiler
+   targeting the ARM/Linux AP environment (*not* the ARM7 cross-compiler used
+   for the GSM firmware itself!) to build fc-loadtools to run on the GTA0x AP.
+
+Building loadtools for GTA0x AP
+===============================
+
+If you've decided to build loadtools for the GTA0x AP, you'll need to make the
+following modifications to the Makefile:
+
+* Change the CC= line to point to the appropriate cross-compiler (which you'll
+  need to provide yourself);
+
+* Change the CFLAGS= line: add the right options to target the ARM920T core in
+  the GTA0x AP (e.g., -march=armv4t -mtune=arm920t), and add -DGTA0x_AP_BUILD
+  to enable some code that makes sense only when running on the GTA0x AP.
+
+* Change EXTRA_OBJ= to EXTRA_OBJ=gtapower.o, i.e., add gtapower.c (compiling
+  into gtapower.o) to the build.
+
+See gta-ap-build.sed for an example.
+
+Running fc-loadtool
+===================
+
+Once you've got loadtools built and installed, you can run fc-loadtool
+as follows:
+
+To operate on a Pirelli DP-L10 that appears as /dev/ttyUSB0:
+
+fc-loadtool -h pirelli /dev/ttyUSB0
+
+The usb2serial chip inside the phone is bus-powered and will be visible as
+/dev/ttyUSBx whether the phone battery is present or not.  There are two ways
+to break into the bootloader:
+
+1. Run the fc-loadtool command given above with the USB cable connected, but no
+   battery present.  Once loadtool says "Sending beacons to <port>", insert the
+   battery.
+
+2. Connect the USB cable to a powered-on phone running its original factory
+   firmware.  (If the phone was off, it will power up and boot in the "charging
+   only" mode - it is not possible for a Calypso/Iota phone to be completely
+   off when both the battery and the charging voltage are present.)  Run
+   fc-loadtool as above - it will start sending its beacons, which will be
+   ignored by the running fw.  Then execute the "power off" operation from the
+   UI (unlock the keypad, then press and hold the red button).  The presence of
+   USB VBUS (used as the charging power source on this phone) will turn the
+   power-off into a reboot, and you'll break into the bootloader.
+
+To operate on the Calypso block of a GTA02, accessing it from an external
+PC/Linux host via a USB-to-headset-jack serial cable that appears as
+/dev/ttyUSB0:
+
+fc-loadtool -h gta02 /dev/ttyUSB0
+
+Run the above command first, then power on the GSM modem from the AP - or power
+it off, then on if it was on already.  The "download" path needs to be enabled
+(controlled from the AP) and fc-loadtool needs to be running on the external
+host when the modem is powered on.
+
+To operate on the Calypso block of a GTA02, running fc-loadtool from inside the
+phone, i.e., from the AP of the same GTA02:
+
+fc-loadtool -h gta02 /dev/ttySAC0
+
+In this last scenario the specially built version of fc-loadtool running on the
+AP takes care of manipulating the modem power to induce entry into the
+bootloader, thus no extra manual steps are needed.
+
+See loadtool.help for a detailed description of the functionality and commands
+that are available once loadtool is running and communicating with loadagent on
+the target device.
+
+Command line options
+====================
+
+The fc-loadtool command lines shown above will usually be sufficient.  However,
+here is the complete command line description for all 3 tools:
+
+fc-iram [options] ttyport iramimage.srec
+fc-xram [options] ttyport xramimage.srec [2ndprog]
+fc-loadtool [options] ttyport
+
+The available options are common for all 3 utilities, with a few noted
+exceptions:
+
+-a /path/to/loadagent
+
+	This option applies only to fc-loadtool and fc-xram.  It specifies the
+	pathname at which the required loadagent.srec image should be sought,
+	overriding the compiled-in default.
+
+-b baud
+
+	This option is common for all 3 utilities.  It selects the baud rate
+	to be used when pushing the IRAM image to the Calypso boot ROM.  In the
+	case of fc-iram, the selected baud rate will be in effect when the
+	loaded IRAM image is jumped to and fc-iram drops into the serial tty
+	pass-thru mode; in the case of fc-loadtool, it will be the initial baud
+	rate for communicating with loadagent, which can be switched later with
+	the baud command.  The default is 115200 baud.
+
+-B baud
+
+	This option is specific to fc-xram.  It selects the baud rate to be
+	used when pushing the XRAM image to loadagent.  If no -B option is
+	specified, fc-xram will communicate with loadagent at the same baud
+	rate that was used to load loadagent itself via the Calypso boot ROM
+	download protocol, i.e., the rate selected with -b, defaulting to
+	115200 baud if no -b option was given either.  Neither -b nor -B
+	affects the baud rate that will be in effect when the loaded XRAM image
+	is jumped to and fc-xram drops into the serial tty pass-thru mode: that
+	baud rate independently defaults to 115200 baud and can only be changed
+	with the -r option.
+
+-h hwtype
+
+	This option is common for all 3 utilities.  It selects the specific
+	target device configuration to be used.  More precisely, it constructs
+	a pathname of the form /usr/local/share/freecalypso/%s.config, where %s
+	is the argument given to this option, and uses that file as the hardware
+	parameters file.
+
+	The hardware configurations known to the present release of FreeCalypso
+	loadtools are gta02 and pirelli.
+
+-H /path/to/hwparam-file
+
+	This option is just like -h, except that the given argument is used
+	directly as the hardware parameter file pathname (absolute or relative)
+	without alteration.
+
+-i num
+
+	This option is common for all 3 utilities.  It specifies the interval
+	in milliseconds at which the tool will send "please interrupt the boot
+	process" beacons out the serial port, hoping to catch the Calypso
+	internal boot ROM.  The default is 13 ms.
+
+-n
+
+	This option does anything only when loadtools have been compiled to run
+	on GTA0x AP.  If you've compiled loadtools with the -DGTA0x_AP_BUILD
+	option, it has an effect of making each tool automatically toggle the
+	modem power control upon startup, removing the need for manual
+	sequencing of the Calypso boot process.  This -n option suppresses that
+	action, making the AP build behave like the standard build in this
+	regard.
+
+-r baud
+
+	This option is specific to fc-xram.  It selects the serial line baud
+	rate which should be set just before the loaded XRAM image is jumped
+	to; the default is 115200 baud.
+
+fc-xram 2nd program invokation
+==============================
+
+The fc-xram utility can take two possible actions after it has loaded the
+specified S-record image into XRAM:
+
+* The default action, in the absence of additional command line arguments, is
+  to drop into a serial tty pass-thru mode, just like fc-iram.
+
+* The alternative action is to invoke a 2nd program and pass the serial
+  communication channel to it.  This 2nd program invokation facility is intended
+  primarily for passing the serial communication channel to rvinterf or rvtdump
+  from the FreeCalypso software suite, not for launching any arbitrary 3rd-party
+  programs from fc-xram.
+
+The intended usage scenario is that one builds a version of the FreeCalypso GSM
+firmware (or some subset thereof, such as an "in vivo" FFS editing agent) in the
+ramImage configuration, fc-xram is used to load that ramImage into the target
+device, and then the serial communication channel (RVTMUX) is immediately taken
+over by rvinterf or rvtdump.
+
+More detailed usage instructions will be written when the rvinterf tools reach
+a point of being usable by more than just the original developer; until then,
+read the source code.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/baudrate.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,7 @@
+/* this header file defines the data structure for baud rate machinations */
+
+struct baudrate {
+	char	*name;
+	speed_t	termios_code;
+	int	bootrom_code;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/chainload.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,114 @@
+/*
+ * This module implements the chain-loading of XRAM images via loadagent.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "srecreader.h"
+
+struct srecreader xramimage;
+
+extern struct baudrate *current_baud_rate;
+extern struct baudrate *xram_run_baudrate;
+
+static void
+make_ml_arg(rec, buf)
+	u_char *rec;
+	char *buf;
+{
+	register int i, len;
+	register char *s;
+
+	len = rec[0] + 1;
+	s = buf;
+	for (i = 0; i < len; i++) {
+		sprintf(s, "%02X", rec[i]);
+		s += 2;
+	}
+	*s = '\0';
+}
+
+perform_chain_load()
+{
+	int resp;
+	unsigned long rec_count;
+	char *argv[3], srecarg[516];
+
+	if (open_srec_file(&xramimage) < 0)
+		exit(1);
+	argv[0] = "ML";
+	argv[1] = srecarg;
+	argv[2] = 0;
+	for (rec_count = 0; ; ) {
+		if (read_s_record(&xramimage) < 0)
+			exit(1);
+		switch (xramimage.record_type) {
+		case '0':
+			if (xramimage.lineno == 1)
+				continue;
+			fprintf(stderr,
+		"%s: S0 record found in line %d (expected in line 1 only)\n",
+				xramimage.filename, xramimage.lineno);
+			exit(1);
+		case '3':
+		case '7':
+			if (s3s7_get_addr_data(&xramimage) < 0)
+				exit(1);
+			break;
+		default:
+			fprintf(stderr,
+				"%s line %d: S%c record type not supported\n",
+				xramimage.filename, xramimage.lineno,
+				xramimage.record_type);
+			exit(1);
+		}
+		if (xramimage.record_type == '7')
+			break;
+		/* must be S3 */
+		if (xramimage.datalen < 1) {
+			fprintf(stderr,
+				"%s line %d: S3 record has zero data length\n",
+				xramimage.filename, xramimage.lineno);
+			exit(1);
+		}
+		if (!rec_count)
+			printf("Each \'.\' is 100 S-records\n");
+		make_ml_arg(xramimage.record, srecarg);
+		tpinterf_make_cmd(argv);
+		if (tpinterf_send_cmd())
+			exit(1);
+		if (tpinterf_pass_output(1))
+			exit(1);
+		rec_count++;
+		if (rec_count % 100 == 0) {
+			putchar('.');
+			fflush(stdout);
+		}
+	}
+	/* got S7 */
+	fclose(xramimage.openfile);
+	if (!rec_count) {
+		fprintf(stderr,
+		"%s line %d: S7 without any preceding S3 data records\n",
+			xramimage.filename, xramimage.lineno);
+		exit(1);
+	}
+	if (xram_run_baudrate != current_baud_rate) {
+		resp = loadagent_switch_baud(xram_run_baudrate);
+		if (resp)
+			exit(1);
+	}
+	printf("Sending jump command\n");
+	sprintf(srecarg, "%lX", (u_long) xramimage.addr);
+	argv[0] = "jump";
+	tpinterf_make_cmd(argv);
+	if (tpinterf_send_cmd())
+		exit(1);
+	printf("Sent \"%s %s\": XRAM image should now be running!\n",
+		argv[0], argv[1]);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/clmain.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,143 @@
+/*
+ * This module contains the main() function for the XRAM chain-loading
+ * utility fc-xram.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include "baudrate.h"
+#include "srecreader.h"
+
+extern char *target_ttydev;
+extern struct srecreader iramimage;
+extern char default_loadagent_image[];
+extern struct srecreader xramimage;
+extern char hw_init_script[];
+extern struct baudrate baud_rate_table[];
+extern struct baudrate *current_baud_rate;
+extern int gta_modem_poweron;
+
+extern struct baudrate *find_baudrate_by_name();
+
+struct baudrate *xram_load_baudrate;
+struct baudrate *xram_run_baudrate = baud_rate_table;	/* 1st entry default */
+
+char **passon_argv;
+int passon_argc;
+
+main(argc, argv)
+	char **argv;
+{
+	extern char *optarg;
+	extern int optind;
+	int c;
+	struct baudrate *br;
+
+	while ((c = getopt(argc, argv, "+a:b:B:c:C:h:H:i:nr:")) != EOF)
+		switch (c) {
+		case 'a':
+			iramimage.filename = optarg;
+			continue;
+		case 'b':
+			set_romload_baudrate(optarg);
+			continue;
+		case 'B':
+			br = find_baudrate_by_name(optarg);
+			if (!br)
+				exit(1);	/* error msg already printed */
+			xram_load_baudrate = br;
+			continue;
+		case 'c':
+			set_compalstage_short(optarg);
+			continue;
+		case 'C':
+			set_compalstage_fullpath(optarg);
+			continue;
+		case 'h':
+			read_hwparam_file_shortname(optarg);
+			continue;
+		case 'H':
+			read_hwparam_file_fullpath(optarg);
+			continue;
+		case 'i':
+			set_beacon_interval(optarg);
+			continue;
+		case 'n':
+			gta_modem_poweron = 0;
+			continue;
+		case 'r':
+			br = find_baudrate_by_name(optarg);
+			if (!br)
+				exit(1);	/* error msg already printed */
+			xram_run_baudrate = br;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+		"usage: fc-xram [options] ttyport xramimage.srec [2ndprog]\n");
+			exit(1);
+		}
+	if (argc - optind < 2)
+		goto usage;
+	target_ttydev = argv[optind];
+	xramimage.filename = argv[optind+1];
+	if (!iramimage.filename)
+		iramimage.filename = default_loadagent_image;
+	if (argc - optind >= 3) {
+		passon_argv = argv + optind + 2;
+		passon_argc = argc - optind - 2;
+	}
+
+	open_target_serial();
+	perform_compal_stage(1);
+	perform_romload();
+	/* loadagent should be running now */
+	if (tpinterf_pass_output(1) < 0)
+		exit(1);
+	if (hw_init_script[0]) {
+		printf("Executing init script %s\n", hw_init_script);
+		c = exec_init_script(hw_init_script);
+		if (c)
+			exit(1);
+	}
+	if (xram_load_baudrate && xram_load_baudrate != current_baud_rate) {
+		c = loadagent_switch_baud(xram_load_baudrate);
+		if (c)
+			exit(1);
+	}
+	printf("Sending XRAM image to loadagent\n");
+	perform_chain_load();
+	if (passon_argv)
+		exec_2nd_prog();
+	tty_passthru();
+	exit(0);
+}
+
+exec_2nd_prog()
+{
+	char **execp_argv;
+	char **sp, **dp;
+	extern int target_fd;
+	char desc_arg[16];
+
+	sprintf(desc_arg, "-d%d", target_fd);
+	execp_argv = (char **) malloc(sizeof(char *) * (passon_argc + 2));
+	if (!execp_argv) {
+		perror("malloc argv for execvp");
+		exit(1);
+	}
+	sp = passon_argv;
+	dp = execp_argv;
+	*dp++ = *sp++;
+	*dp++ = desc_arg;
+	while (*sp)
+		*dp++ = *sp++;
+	*dp = NULL;
+	execvp(execp_argv[0], execp_argv);
+	fprintf(stderr, "Unable to execvp %s\n", passon_argv[0]);
+	exit(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/compaldummy.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+/*
+ * If you need to build a version of loadtools without compalstage support,
+ * e.g., because you are building for GTA01/02 AP, substitute this dummy
+ * module in the place of compalload.c to get a passing build.
+ */
+
+void
+set_compalstage_short()
+{
+}
+
+void
+set_compalstage_fullpath()
+{
+}
+
+perform_compal_stage()
+{
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/compalload.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,222 @@
+/*
+ * This module implements Compal's serial code loading protocol to load
+ * what we call compalstage, a piece of code that re-enables the Calypso
+ * boot ROM and allows us to use the same loadagent which we use on
+ * freedom-enabled target devices.
+ */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <termios.h>
+#include <unistd.h>
+
+extern char default_helpers_dir[];
+extern char *target_ttydev;
+extern int target_fd;
+extern struct termios target_termios;
+
+static char compalstage_pathname[MAXPATHLEN];
+static u_char *compalstage_imgbuf;
+static size_t compalstage_len, compalstage_totlen;
+static int rx_state;
+static u_char rx_msg[3];
+
+void
+set_compalstage_short(arg)
+	char *arg;
+{
+	if (strcmp(arg, "none"))
+		sprintf(compalstage_pathname, "%s/compalstage-%s.bin",
+			default_helpers_dir, arg);
+	else
+		compalstage_pathname[0] = 0;
+}
+
+void
+set_compalstage_fullpath(arg)
+	char *arg;
+{
+	strcpy(compalstage_pathname, arg);
+}
+
+static void
+compute_checksum()
+{
+	size_t i, l;
+	u_char ck;
+
+	ck = 0x02;
+	l = compalstage_len + 3;
+	for (i = 1; i < l; i++)
+		ck ^= compalstage_imgbuf[i];
+	compalstage_imgbuf[l] = ck;
+}
+
+static
+handle_rx_msg()
+{
+	static u_char download_cmd[7] = {0x1B, 0xF6, 0x02, 0x00,
+					 0x52, 0x01, 0x53};
+
+	if (rx_msg[0] == 0x41 && rx_msg[1] == 0x01 && rx_msg[2] == 0x40) {
+		printf("Received PROMPT1, sending download command\n");
+		write(target_fd, download_cmd, 7);
+		return(0);
+	} else if (rx_msg[0] == 0x41 && rx_msg[1] == 0x02 && rx_msg[2] == 0x43){
+		printf("Received PROMPT2, sending download image\n");
+		write(target_fd, compalstage_imgbuf, compalstage_totlen);
+		return(0);
+	} else if (rx_msg[0] == 0x41 && rx_msg[1] == 0x03 && rx_msg[2] == 0x42){
+	    printf("Received ACK; downloaded image should now be running!\n");
+		return(1);
+	} else if (rx_msg[0] == 0x45 && rx_msg[1] == 0x53 && rx_msg[2] == 0x16){
+		printf("Bootloader indicates bad checksum :-(\n");
+		return(0);
+	} else if (rx_msg[0] == 0x41 && rx_msg[1] == 0x03 && rx_msg[2] == 0x57){
+		printf("Bootloader indicates bad magic :-(\n");
+		return(0);
+	} else {
+	    printf("Unknown msg from bootloader: 1B F6 02 00 %02X %02X %02X\n",
+			rx_msg[0], rx_msg[1], rx_msg[2]);
+		return(0);
+	}
+}
+
+static
+handle_rx_byte(rxb)
+{
+	switch (rx_state) {
+	case 0:
+		if (rxb == 0x1B)
+			rx_state = 1;
+		return(0);
+	case 1:
+		if (rxb == 0xF6)
+			rx_state = 2;
+		else if (rxb == 0x1B)
+			rx_state = 1;
+		else
+			rx_state = 0;
+		return(0);
+	case 2:
+		if (rxb == 0x02)
+			rx_state = 3;
+		else if (rxb == 0x1B)
+			rx_state = 1;
+		else
+			rx_state = 0;
+		return(0);
+	case 3:
+		if (rxb == 0x00)
+			rx_state = 4;
+		else if (rxb == 0x1B)
+			rx_state = 1;
+		else
+			rx_state = 0;
+		return(0);
+	case 4:
+		rx_msg[0] = rxb;
+		rx_state = 5;
+		return(0);
+	case 5:
+		rx_msg[1] = rxb;
+		rx_state = 6;
+		return(0);
+	case 6:
+		rx_msg[2] = rxb;
+		rx_state = 0;
+		return handle_rx_msg();
+	}
+}
+
+static void
+read_loop()
+{
+	u_char rdbuf[16];
+	int cc, i;
+
+	for (;;) {
+		cc = read(target_fd, rdbuf, sizeof rdbuf);
+		if (cc <= 0) {
+			fprintf(stderr, "EOF/error on target tty\n");
+			exit(1);
+		}
+		for (i = 0; i < cc; i++)
+			if (handle_rx_byte(rdbuf[i]))
+				return;
+	}
+}
+
+perform_compal_stage(for_boot_rom)
+{
+	int fd;
+	struct stat st;
+	static int zero = 0;
+
+	if (!compalstage_pathname[0])
+		return(0);
+	fd = open(compalstage_pathname, O_RDONLY);
+	if (fd < 0) {
+		perror(compalstage_pathname);
+		exit(1);
+	}
+	fstat(fd, &st);
+	if (!S_ISREG(st.st_mode)) {
+		fprintf(stderr, "error: %s is not a regular file\n",
+			compalstage_pathname);
+		exit(1);
+	}
+	if (st.st_size > 65535) {
+		fprintf(stderr,
+		"error: %s exceed Compal download limit of 65535 bytes\n",
+			compalstage_pathname);
+		exit(1);
+	}
+	compalstage_len = st.st_size;
+	compalstage_totlen = compalstage_len + 4;
+	compalstage_imgbuf = malloc(compalstage_totlen);
+	if (!compalstage_imgbuf) {
+		perror("malloc");
+		exit(1);
+	}
+	compalstage_imgbuf[0] = 0x02;
+	compalstage_imgbuf[1] = compalstage_len >> 8;
+	compalstage_imgbuf[2] = compalstage_len;
+	if (read(fd, compalstage_imgbuf+3, compalstage_len) != compalstage_len){
+		fprintf(stderr, "%s: read error or short read\n",
+			compalstage_pathname);
+		exit(1);
+	}
+	close(fd);
+	compute_checksum();
+
+	printf("Using Compal stage image %s\n", compalstage_pathname);
+	cfsetispeed(&target_termios, B115200);
+	cfsetospeed(&target_termios, B115200);
+	if (tcsetattr(target_fd, TCSAFLUSH, &target_termios) < 0) {
+		perror("tcsetattr to switch baud rate");
+		exit(1);
+	}
+	ioctl(target_fd, FIONBIO, &zero);
+	printf("Waiting for PROMPT1 from target (%s) at 115200 baud\n",
+		target_ttydev);
+	read_loop();
+	free(compalstage_imgbuf);
+
+	if (for_boot_rom) {
+		cfsetispeed(&target_termios, B19200);
+		cfsetospeed(&target_termios, B19200);
+		if (tcsetattr(target_fd, TCSAFLUSH, &target_termios) < 0) {
+			perror("tcsetattr to switch baud rate");
+			exit(1);
+		}
+	}
+
+	return(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/compalram.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,26 @@
+/*
+ * This module contains the main() function for fc-compalram, a trivial
+ * utility that feeds a binary download image to Compal's bootloader
+ * and then switches into serial tty pass-through.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern char *target_ttydev;
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: fc-compalram ttyport image.bin\n");
+		exit(1);
+	}
+	target_ttydev = argv[1];
+	set_compalstage_fullpath(argv[2]);
+
+	open_target_serial();
+	perform_compal_stage(0);
+	tty_passthru();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/crc32tab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,70 @@
+/* This CRC-32 table has been computed for the LSB-first direction */
+
+#include <stdint.h>
+
+uint32_t crc32_table[256] = {
+	0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
+	0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+	0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+	0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+	0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+	0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+	0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+	0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+	0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+	0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+	0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
+	0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+	0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
+	0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+	0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+	0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+	0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
+	0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+	0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
+	0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+	0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+	0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+	0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+	0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+	0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+	0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+	0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+	0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+	0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
+	0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+	0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
+	0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+	0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+	0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+	0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+	0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+	0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
+	0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+	0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+	0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+	0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
+	0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+	0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
+	0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+	0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+	0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+	0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
+	0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+	0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+	0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+	0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+	0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+	0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
+	0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+	0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+	0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+	0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+	0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+	0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
+	0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+	0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
+	0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+	0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+	0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/defpath.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,9 @@
+/*
+ * By default the loadagent.srec target utility and some hardware parameter
+ * files are sought in an installation directory, to make the more common
+ * command line operations more manageable.
+ */
+
+char default_helpers_dir[] = "/usr/local/share/freecalypso";
+char default_loadagent_image[] = "/usr/local/share/freecalypso/loadagent.srec";
+char loadtool_help_file[] = "/usr/local/share/freecalypso/loadtool.help";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flash.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,73 @@
+/* this header file contains definitions for fc-loadtool flash support */
+
+/*
+ * The following structures represent an "abstract"
+ * description of flash devices.
+ *
+ * A "region" is a consecutive group of erase units of the same size.
+ */
+
+struct flash_region_desc {
+	uint32_t	sector_size;
+	unsigned	nsectors;
+};
+
+#define	CFI_MAX_REGIONS		4
+
+/*
+ * The info in struct flash_geom can be either
+ * gathered from CFI or hard-coded.
+ */
+struct flash_geom {
+	uint32_t			total_size;
+	unsigned			nregions;
+	struct flash_region_desc	regions[CFI_MAX_REGIONS];
+	unsigned			total_sectors;
+};
+
+struct flash_idcheck {
+	uint16_t	offset;
+	uint16_t	expect_val;
+};
+
+struct flash_bank_desc {
+	uint32_t		align_size;
+	struct flash_geom	*geom;
+	struct flash_idcheck	*idcheck_table;
+	unsigned		idcheck_num;
+};
+
+struct flash_device_desc {
+	char			*name;
+	struct flash_bank_desc	*bank_desc;
+	unsigned		nbanks;
+	struct flash_cmdset	*cmdset;
+};
+
+/* the following structures describe flash banks as accessible to us */
+
+struct sector_info {
+	uint32_t	start;
+	uint32_t	size;
+};
+
+struct flash_cmdset {
+	char	*cmdset_name;
+	int	(*reset_cmd)();
+	int	(*status_cmd)();
+	int	(*unlock_sector)();
+	int	(*erase_sector)();
+	int	(*prep_for_program)();
+	char	*loadagent_setbase_cmd;
+	char	*loadagent_program_cmd;
+	int	needs_unlock;
+};
+
+struct flash_bank_info {
+	uint32_t		base_addr;
+	struct flash_bank_desc	*bank_desc;
+	struct flash_geom	*geom;
+	struct flash_cmdset	*ops;
+	struct sector_info	*sectors;
+	int			idcheck_done;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flashops.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,218 @@
+/*
+ * This module implements those flash operations which are dependent
+ * on the AMD vs. Intel command set style.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include "flash.h"
+
+/* common stub functions */
+
+static
+noop()
+{
+	return(0);
+}
+
+static
+invalid()
+{
+	fprintf(stderr,
+	      "This operation is not applicable to the selected flash type\n");
+	return(-1);
+}
+
+/* AMD flash functions */
+
+amd_reset_cmd(bi)
+	struct flash_bank_info *bi;
+{
+	if (do_w16(bi->base_addr + 0xAAA, 0xF0)) {
+		fprintf(stderr,
+	"unexpected response to w16 when resetting flash to read mode!\n");
+		return(-1);
+	}
+	return(0);
+}
+
+amd_sector_erase(bi, sp)
+	struct flash_bank_info *bi;
+	struct sector_info *sp;
+{
+	int stat;
+	uint16_t flstat;
+	time_t start_time, curtime;
+
+	stat = do_w16(bi->base_addr + sp->start + 0xAAA, 0xAA);
+	if (stat) {
+bad_w16:	fprintf(stderr,
+	"unexpected response to w16 in erase cmd sequence - aborting\n");
+		return(-1);
+	}
+	stat = do_w16(bi->base_addr + sp->start + 0x554, 0x55);
+	if (stat)
+		goto bad_w16;
+	stat = do_w16(bi->base_addr + sp->start + 0xAAA, 0x80);
+	if (stat)
+		goto bad_w16;
+	stat = do_w16(bi->base_addr + sp->start + 0xAAA, 0xAA);
+	if (stat)
+		goto bad_w16;
+	stat = do_w16(bi->base_addr + sp->start + 0x554, 0x55);
+	if (stat)
+		goto bad_w16;
+	stat = do_w16(bi->base_addr + sp->start + 0xAAA, 0x30);
+	if (stat)
+		goto bad_w16;
+	start_time = time(0);
+	for (;;) {
+		stat = do_r16(bi->base_addr + sp->start, &flstat);
+		if (stat)
+			return(stat);	/* error msg already printed */
+		if (flstat == 0xFFFF)
+			return(0);
+		curtime = time(0);
+		if (curtime >= start_time + 20) {
+			fprintf(stderr, "erase timeout, aborting\n");
+			return(-1);
+		}
+	}
+}
+
+struct flash_cmdset flash_cmdset_amd = {
+	.cmdset_name		= "AMD",
+	.reset_cmd		= amd_reset_cmd,
+	.status_cmd		= invalid,
+	.unlock_sector		= invalid,
+	.erase_sector		= amd_sector_erase,
+	.prep_for_program	= noop,
+	.loadagent_setbase_cmd	= "AMFB",
+	.loadagent_program_cmd	= "AMFW",
+	.needs_unlock		= 0,
+};
+
+/* Intel flash functions */
+
+intel_reset_cmd(bi)
+	struct flash_bank_info *bi;
+{
+	if (do_w16(bi->base_addr, 0xFF)) {
+		fprintf(stderr,
+	"unexpected response to w16 when resetting flash to read mode!\n");
+		return(-1);
+	}
+	return(0);
+}
+
+intel_status_cmd(bi)
+	struct flash_bank_info *bi;
+{
+	int stat;
+	uint16_t sr;
+
+	/* issue Read SR command */
+	stat = do_w16(bi->base_addr, 0x70);
+	if (stat) {
+		fprintf(stderr,
+			"unexpected response to w16 for Read SR command\n");
+		return(-1);
+	}
+	stat = do_r16(bi->base_addr, &sr);
+	if (stat)
+		return(stat);	/* error msg already printed */
+	sr &= 0xFF;
+	printf("Status Register: %02X\n", sr);
+	return(0);
+}
+
+intel_sector_unlock(bi, sp)
+	struct flash_bank_info *bi;
+	struct sector_info *sp;
+{
+	int stat;
+
+	stat = do_w16(bi->base_addr + sp->start, 0x60);
+	if (stat) {
+bad_w16:	fprintf(stderr,
+	"unexpected response to w16 in block unlock cmd sequence - aborting\n");
+		return(-1);
+	}
+	stat = do_w16(bi->base_addr + sp->start, 0xD0);
+	if (stat)
+		goto bad_w16;
+	return(0);
+}
+
+intel_sector_erase(bi, sp)
+	struct flash_bank_info *bi;
+	struct sector_info *sp;
+{
+	int stat;
+	uint16_t flstat;
+	time_t start_time, curtime;
+
+	stat = intel_sector_unlock(bi, sp);
+	if (stat)
+		return(stat);	/* error msg already printed */
+	/* clear SR */
+	stat = do_w16(bi->base_addr + sp->start, 0x50);
+	if (stat) {
+bad_w16:	fprintf(stderr,
+	"unexpected response to w16 in erase cmd sequence - aborting\n");
+		return(-1);
+	}
+	/* send the actual block erase command */
+	stat = do_w16(bi->base_addr + sp->start, 0x20);
+	if (stat)
+		goto bad_w16;
+	stat = do_w16(bi->base_addr + sp->start, 0xD0);
+	if (stat)
+		goto bad_w16;
+	/* wait for completion */
+	start_time = time(0);
+	for (;;) {
+		stat = do_r16(bi->base_addr + sp->start, &flstat);
+		if (stat)
+			return(stat);	/* error msg already printed */
+		if (flstat & 0x80)
+			break;
+		curtime = time(0);
+		if (curtime >= start_time + 20) {
+			fprintf(stderr, "erase timeout, aborting\n");
+			return(-1);
+		}
+	}
+	if (flstat & 0x20) {
+		fprintf(stderr, "block erase failed!\n");
+		return(-1);
+	} else
+		return(0);
+}
+
+intel_clear_sr(bi)
+	struct flash_bank_info *bi;
+{
+	printf("Clearing Intel flash SR\n");
+	if (do_w16(bi->base_addr, 0x50)) {
+		fprintf(stderr,
+			"unexpected response to w16 for Clear SR command\n");
+		return(-1);
+	}
+	return(0);
+}
+
+struct flash_cmdset flash_cmdset_intel = {
+	.cmdset_name		= "Intel",
+	.reset_cmd		= intel_reset_cmd,
+	.status_cmd		= intel_status_cmd,
+	.unlock_sector		= intel_sector_unlock,
+	.erase_sector		= intel_sector_erase,
+	.prep_for_program	= intel_clear_sr,
+	.loadagent_setbase_cmd	= "INFB",
+	.loadagent_program_cmd	= "INFW",
+	.needs_unlock		= 1,
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flcmplboot.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,256 @@
+/*
+ * This module contains the implementation of the flash erase-program-boot
+ * hack for brickable Compal phones.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "flash.h"
+
+extern struct flash_bank_info flash_bank_info[2];
+extern struct flash_cmdset flash_cmdset_intel;
+
+extern uint32_t crc32_table[];
+
+static int hack_enabled;
+static uint32_t boot_sector_size;
+static uint32_t ram_buffer_addr;
+
+/* called from hwparam.c config file parser */
+void
+set_boot_reflash_hack(arg, filename_for_errs, lineno_for_errs)
+	char *arg;
+	char *filename_for_errs;
+	int lineno_for_errs;
+{
+	char *cp, *np, *ep;
+
+	if (hack_enabled) {
+		fprintf(stderr,
+			"%s line %d: duplicate boot-reflash-hack setting\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	for (cp = arg; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#') {
+too_few_arg:	fprintf(stderr,
+		"%s line %d: boot-reflash-hack setting: too few arguments\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (!*cp)
+		goto too_few_arg;
+	*cp++ = '\0';
+	ram_buffer_addr = strtoul(np, &ep, 16);
+	if (*ep) {
+invhex:		fprintf(stderr,
+			"%s line %d: syntax error (hex arguments expected)\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	while (isspace(*cp))
+		cp++;
+	if (!*cp || *cp == '#')
+		goto too_few_arg;
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	boot_sector_size = strtoul(np, &ep, 16);
+	if (*ep)
+		goto invhex;
+	while (isspace(*cp))
+		cp++;
+	if (*cp && *cp != '#') {
+		fprintf(stderr,
+		"%s line %d: boot-reflash-hack setting: too many arguments\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	hack_enabled = 1;
+}
+
+static void
+make_s3_record(buf, dest_addr, datalen)
+	u_char *buf;
+	uint32_t dest_addr;
+{
+	int totlen, i;
+	u_char accum;
+
+	buf[0] = totlen = datalen + 5;
+	buf[1] = dest_addr >> 24;
+	buf[2] = dest_addr >> 16;
+	buf[3] = dest_addr >> 8;
+	buf[4] = dest_addr;
+	accum = 0;
+	for (i = 0; i < totlen; i++)
+		accum += buf[i];
+	buf[i] = ~accum;
+}
+
+static void
+make_ml_arg(rec, buf)
+	u_char *rec;
+	char *buf;
+{
+	register int i, len;
+	register char *s;
+
+	len = rec[0] + 1;
+	s = buf;
+	for (i = 0; i < len; i++) {
+		sprintf(s, "%02X", rec[i]);
+		s += 2;
+	}
+	*s = '\0';
+}
+
+flashcmd_erase_program_boot(argc, argv)
+	char **argv;
+{
+	FILE *binf;
+	struct stat filestat;
+	size_t len;
+	char *strtoul_endp;
+	char *targv[5], longarg[513];
+	char shortarg1[9], shortarg2[9], shortarg3[9];
+	u_char databuf[256];
+	int reclen, cc, i;
+	uint32_t ramaddr, remlen, crcaccum;
+	u_long crc_from_target;
+
+	if (!hack_enabled) {
+		fprintf(stderr,
+			"Operation not applicable to this target device\n");
+		return(-1);
+	}
+	if (argc < 3 || argc > 4) {
+inv:		fprintf(stderr, "usage: %s %s binfile [length]\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	if (flash_get_cfi(0) < 0)
+		return(-1);
+	if (flash_bank_info[0].geom->regions[0].sector_size
+			!= boot_sector_size) {
+		fprintf(stderr,
+		"error: detected flash boot sector size differs from config\n");
+		return(-1);
+	}
+	if (flash_bank_info[0].ops != &flash_cmdset_intel) {
+		fprintf(stderr,
+			"error: operation implemented for Intel flash only\n");
+		return(-1);
+	}
+
+	binf = fopen(argv[2], "r");
+	if (!binf) {
+		perror(argv[2]);
+		return(-1);
+	}
+	fstat(fileno(binf), &filestat);
+	if (!S_ISREG(filestat.st_mode)) {
+		fprintf(stderr, "%s is not a regular file\n", argv[2]);
+		fclose(binf);
+		return(-1);
+	}
+	if (argc > 3) {
+		len = strtoul(argv[3], &strtoul_endp, 16);
+		if (*strtoul_endp) {
+			fclose(binf);
+			goto inv;
+		}
+		if (len > filestat.st_size) {
+			fprintf(stderr,
+			    "error: specified length exceeds file length\n");
+			fclose(binf);
+			return(-1);
+		}
+	} else
+		len = filestat.st_size;
+	if (len > boot_sector_size) {
+		fprintf(stderr, "error: length exceeds boot sector size\n");
+		fclose(binf);
+		return(-1);
+	}
+	if (len & 1) {
+		fprintf(stderr, "error: length must be even\n");
+		fclose(binf);
+		return(-1);
+	}
+
+	printf("Loading new boot code into target RAM at %lx\n",
+		(u_long) ram_buffer_addr);
+	targv[0] = "ML";
+	targv[1] = longarg;
+	targv[2] = 0;
+	ramaddr = ram_buffer_addr;
+	crcaccum = 0xFFFFFFFF;
+	for (remlen = len; remlen; remlen -= reclen) {
+		if (remlen >= 250)
+			reclen = 250;
+		else
+			reclen = remlen;
+		cc = fread(databuf + 5, 1, reclen, binf);
+		if (cc != reclen) {
+			fclose(binf);
+			fprintf(stderr, "error reading from %s\n", argv[2]);
+			return(-1);
+		}
+		for (i = 0; i < reclen; i++)	/* update running CRC */
+			crcaccum = crc32_table[crcaccum & 0xFF ^ databuf[i+5]]
+				^ (crcaccum >> 8);
+		make_s3_record(databuf, ramaddr, reclen);
+		make_ml_arg(databuf, longarg);
+		tpinterf_make_cmd(targv);
+		if (tpinterf_send_cmd() < 0) {
+			fclose(binf);
+			return(-1);
+		}
+		cc = tpinterf_pass_output(1);
+		if (cc) {
+			fclose(binf);
+			return(cc);
+		}
+		ramaddr += reclen;
+		putchar('.');
+		fflush(stdout);
+	}
+	putchar('\n');
+	fclose(binf);
+
+	printf("Verifying CRC-32 in target RAM\n");
+	if (crc32_on_target((u_long) ram_buffer_addr, (u_long) len,
+				&crc_from_target) < 0)
+		return(-1);
+	if (crc_from_target == crcaccum)
+		printf("match (%08lX)\n", crc_from_target);
+	else {
+		fprintf(stderr, "error: CRC mismatch!\n");
+		return(-1);
+	}
+
+	printf("Commanding flash erase+program operation on the target\n");
+	sprintf(shortarg1, "%lx", (u_long) ram_buffer_addr);
+	sprintf(shortarg2, "%lx", (u_long) flash_bank_info[0].base_addr);
+	sprintf(shortarg3, "%lx", (u_long) len);
+	targv[0] = "intel-rewrite-sector";
+	targv[1] = shortarg1;
+	targv[2] = shortarg2;
+	targv[3] = shortarg3;
+	targv[4] = 0;
+	tpinterf_make_cmd(targv);
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	return tpinterf_pass_output(20);	/* 20 s timeout */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flmain.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,260 @@
+/*
+ * This module is the main entry point for fc-loadtool flash functions
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "flash.h"
+
+/* K5A32xx device description */
+
+static struct flash_geom k5a32xx_topboot_geom = {
+	.total_size	= 0x400000,
+	.nregions	= 2,
+	.regions	= {0x10000, 63, 0x2000, 8},
+	.total_sectors	= 71,
+};
+
+static struct flash_idcheck k5a32xx_topboot_idcheck[2] = {
+	{0x00, 0x00EC},
+	{0x02, 0x22A0}
+};
+
+static struct flash_bank_desc k5a32xx_topboot_bankdesc = {
+	0x400000, &k5a32xx_topboot_geom, k5a32xx_topboot_idcheck, 2
+};
+
+/* S{29,71}PL129N device description */
+
+static struct flash_geom pl129n_ce1_geom = {
+	.total_size	= 0x800000,
+	.nregions	= 2,
+	.regions	= {0x10000, 4, 0x40000, 31},
+	.total_sectors	= 35,
+};
+
+static struct flash_geom pl129n_ce2_geom = {
+	.total_size	= 0x800000,
+	.nregions	= 2,
+	.regions	= {0x40000, 31, 0x10000, 4},
+	.total_sectors	= 35,
+};
+
+static struct flash_idcheck pl129n_idcheck[4] = {
+	{0x00, 0x0001},
+	{0x02, 0x227E},
+	{0x1C, 0x2221},
+	{0x1E, 0x2200}
+};
+
+static struct flash_bank_desc pl129n_banks[2] = {
+	{0x800000, &pl129n_ce1_geom, pl129n_idcheck, 4},
+	{0x800000, &pl129n_ce2_geom, pl129n_idcheck, 4}
+};
+
+/* bank configurations for CFI */
+
+static struct flash_bank_desc cfi_4M_bankdesc = {
+	0x400000, 0, 0, 0
+};
+
+static struct flash_bank_desc cfi_8M_bankdesc = {
+	0x800000, 0, 0, 0
+};
+
+/* list of supported flash devices */
+
+extern struct flash_cmdset flash_cmdset_amd;
+
+struct flash_device_desc flash_device_list[] = {
+	{"cfi-4M", &cfi_4M_bankdesc, 1, 0},
+	{"cfi-8M", &cfi_8M_bankdesc, 1, 0},
+	{"k5a32xx_t", &k5a32xx_topboot_bankdesc, 1, &flash_cmdset_amd},
+	{"pl129n", pl129n_banks, 2, &flash_cmdset_amd},
+	{0, 0, 0, 0}	/* array terminator */
+};
+
+/* the following variables describe our selected flash device */
+
+struct flash_device_desc *selected_flash_device;
+struct flash_bank_info flash_bank_info[2];
+
+/* called from hwparam.c config file parser */
+void
+set_flash_device(arg, filename_for_errs, lineno_for_errs)
+	char *arg;
+	char *filename_for_errs;
+	int lineno_for_errs;
+{
+	char *cp, *np, *ep;
+	struct flash_device_desc *tp;
+	int bank;
+	struct flash_bank_info *bi;
+
+	if (selected_flash_device) {
+		fprintf(stderr, "%s line %d: duplicate flash setting\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	for (cp = arg; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#') {
+too_few_arg:	fprintf(stderr,
+			"%s line %d: flash setting: too few arguments\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = flash_device_list; tp->name; tp++)
+		if (!strcmp(tp->name, np))
+			break;
+	if (!tp->name) {
+		fprintf(stderr,
+			"%s line %d: unknown flash device \"%s\"\n",
+			filename_for_errs, lineno_for_errs, np);
+		exit(1);
+	}
+	selected_flash_device = tp;
+
+	/* now initialize flash_bank_info */
+	for (bank = 0; bank < selected_flash_device->nbanks; bank++) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp || *cp == '#')
+			goto too_few_arg;
+		for (np = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		bi = flash_bank_info + bank;
+		bi->base_addr = strtoul(np, &ep, 16);
+		if (*ep) {
+			fprintf(stderr,
+"%s line %d: syntax error (base addr expected after flash device type)\n",
+				filename_for_errs, lineno_for_errs);
+			exit(1);
+		}
+		/* the rest comes from the flash device type */
+		bi->bank_desc = selected_flash_device->bank_desc + bank;
+		if (bi->base_addr & (bi->bank_desc->align_size - 1)) {
+			fprintf(stderr,
+"%s line %d: flash bank %d base addr is not aligned to the bank size (0x%lx)\n",
+				filename_for_errs, lineno_for_errs, bank,
+				(u_long) bi->bank_desc->align_size);
+			exit(1);
+		}
+		bi->geom = bi->bank_desc->geom;
+		bi->ops = selected_flash_device->cmdset;
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp && *cp != '#') {
+		fprintf(stderr,
+			"%s line %d: flash setting: too many arguments\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+}
+
+flashcmd_help()
+{
+	return loadtool_help("flash");
+}
+
+flashcmd_info(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+
+	if (argc > 2) {
+		fprintf(stderr, "error: too many arguments\n");
+		return(-1);
+	}
+	bi = flash_bank_info + bank;
+	printf("Flash device type: %s\n", selected_flash_device->name);
+	printf("Bank %d base address: %08lX\n", bank, (u_long) bi->base_addr);
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	printf("Bank %d total size: %lx\n", bank,
+		(u_long) bi->geom->total_size);
+	printf("Sectors in bank %d: %u (%u regions)\n", bank,
+		bi->geom->total_sectors, bi->geom->nregions);
+	printf("Command set style: %s\n", bi->ops->cmdset_name);
+	flash_id_check(bank, 1);
+	if (selected_flash_device->nbanks == 2 && !bank)
+	    printf("\nFlash device has 2 banks; flash2 command available\n");
+	return(0);
+}
+
+extern int flashcmd_blankchk();
+extern int flashcmd_dump2file();
+extern int flashcmd_erase();
+extern int flashcmd_erase_program_boot();
+extern int flashcmd_progbin();
+extern int flashcmd_program_m0();
+extern int flashcmd_program_srec();
+extern int flashcmd_quickprog();
+extern int flashcmd_reset();
+extern int flashcmd_sectors();
+extern int flashcmd_status();
+extern int flashcmd_unlock();
+
+static struct cmdtab {
+	char *cmd;
+	int (*func)();
+} cmdtab[] = {
+	{"blankchk", flashcmd_blankchk},
+	{"dump2bin", flashcmd_dump2file},
+	{"dump2srec", flashcmd_dump2file},
+	{"erase", flashcmd_erase},
+	{"erase-program-boot", flashcmd_erase_program_boot},
+	{"help", flashcmd_help},
+	{"info", flashcmd_info},
+	{"program-bin", flashcmd_progbin},
+	{"program-m0", flashcmd_program_m0},
+	{"program-srec", flashcmd_program_srec},
+	{"quickprog", flashcmd_quickprog},
+	{"reset", flashcmd_reset},
+	{"sectors", flashcmd_sectors},
+	{"status", flashcmd_status},
+	{"unlock", flashcmd_unlock},
+	{0, 0}
+};
+
+cmd_flash(argc, argv)
+	char **argv;
+{
+	int bank;
+	struct cmdtab *tp;
+
+	if (!selected_flash_device) {
+		fprintf(stderr, "No flash configuration defined\n");
+		return(-1);
+	}
+	if (argv[0][5] == '2') {
+		if (selected_flash_device->nbanks < 2) {
+			fprintf(stderr, "Flash device %s has only one bank\n",
+				selected_flash_device->name);
+			return(-1);
+		}
+		bank = 1;
+	} else
+		bank = 0;
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[1]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr, "%s %s: unknown/unimplemented subcommand\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	return tp->func(argc, argv, bank);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flmisc.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,311 @@
+/*
+ * Miscellaneous flash commands (fc-loadtool) are implemented here
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "flash.h"
+
+extern struct flash_bank_info flash_bank_info[2];
+
+flashcmd_blankchk(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+	u_long offset, len;
+	char *strtoul_endp;
+	char *targv[4], targ_start[10], targ_len[10];
+
+	if (argc != 4) {
+inv:		fprintf(stderr, "usage: %s %s hex-start-offset hex-length\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	offset = strtoul(argv[2], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	if (offset >= bi->geom->total_size) {
+		fprintf(stderr,
+		"error: specified offset exceeds flash bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		return(-1);
+	}
+	len = strtoul(argv[3], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	if (len > bi->geom->total_size - offset) {
+		fprintf(stderr,
+	"error: specified offset+length exceed flash bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		return(-1);
+	}
+	/* reset flash to read mode */
+	if (bi->ops->reset_cmd(bi) < 0)
+		return(-1);
+	sprintf(targ_start, "%lx", (u_long) bi->base_addr + offset);
+	sprintf(targ_len, "%lx", len);
+	targv[0] = "blankchk";
+	targv[1] = targ_start;
+	targv[2] = targ_len;
+	targv[3] = 0;
+	tpinterf_make_cmd(targv);
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	return tpinterf_pass_output(10);	/* 10 s timeout */
+}
+
+flashcmd_dump2file(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+	u_long offset, dumplen, maxlen;
+	char *strtoul_endp;
+	int format;
+
+	if (argc < 3 || argc > 5) {
+inv:		fprintf(stderr, "usage: %s %s outfile [offset [length]]\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	if (argc >= 4) {
+		offset = strtoul(argv[3], &strtoul_endp, 16);
+		if (*strtoul_endp)
+			goto inv;
+		if (offset >= bi->geom->total_size) {
+			fprintf(stderr,
+		"error: specified offset exceeds flash bank size (0x%lx)\n",
+				(u_long) bi->geom->total_size);
+			return(-1);
+		}
+	} else
+		offset = 0;
+	maxlen = bi->geom->total_size - offset;
+	if (argc >= 5) {
+		dumplen = strtoul(argv[4], &strtoul_endp, 16);
+		if (*strtoul_endp)
+			goto inv;
+		if (dumplen > maxlen) {
+			fprintf(stderr,
+	"error: specified offset+length exceed flash bank size (0x%lx)\n",
+				(u_long) bi->geom->total_size);
+			return(-1);
+		}
+	} else
+		dumplen = maxlen;
+	switch (argv[1][5]) {
+	case 'b':
+		format = 0;
+		break;
+	case 's':
+		format = 1;
+		break;
+	default:
+		fprintf(stderr,
+			"internal bug: bad format in flashcmd_dump2file()\n");
+		return(-1);
+	}
+	/* reset flash to read mode */
+	if (bi->ops->reset_cmd(bi) < 0)
+		return(-1);
+	return loadtool_memdump(bi->base_addr + offset, dumplen, argv[2],
+				format);
+}
+
+flashcmd_erase(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+	u_long offset, len;
+	char *strtoul_endp;
+	struct sector_info *startsec, *endsec, *sp;
+	int stat;
+
+	if (argc != 4) {
+inv:		fprintf(stderr, "usage: %s %s hex-start-offset hex-length\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	offset = strtoul(argv[2], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	if (offset >= bi->geom->total_size) {
+		fprintf(stderr,
+		"error: specified offset exceeds flash bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		return(-1);
+	}
+	len = strtoul(argv[3], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	if (len > bi->geom->total_size - offset) {
+		fprintf(stderr,
+	"error: specified offset+length exceed flash bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		return(-1);
+	}
+	if (!len) {
+		printf("Zero length specified - nothing to do!\n");
+		return(0);
+	}
+	/* now enforce sector alignment for both offset and length */
+	if (get_flash_sector_table(bank) < 0)
+		return(-1);
+	if (get_flash_sector_range(bi, offset, len, &startsec, &endsec) < 0)
+		return(-1);
+	stat = flash_id_check(bank, 0);
+	if (stat)
+		return(stat);
+	printf("Erasing %d sector(s)\n", endsec - startsec);
+	for (sp = startsec; sp < endsec; sp++) {
+		stat = bi->ops->erase_sector(bi, sp);
+		if (stat)
+			return(stat);
+		putchar('.');
+		fflush(stdout);
+	}
+	putchar('\n');
+	return(0);
+}
+
+flashcmd_quickprog(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+	char *targv[4], targ_base[10];
+	int stat;
+
+	if (argc != 4) {
+		fprintf(stderr, "usage: %s %s hex-offset hex-data-string\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	sprintf(targ_base, "%lx", (u_long) bi->base_addr);
+	targv[0] = bi->ops->loadagent_setbase_cmd;
+	targv[1] = targ_base;
+	targv[2] = 0;
+	tpinterf_make_cmd(targv);
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	stat = tpinterf_pass_output(1);
+	if (stat)
+		return(stat);
+	targv[0] = bi->ops->loadagent_program_cmd;
+	targv[1] = argv[2];
+	targv[2] = argv[3];
+	targv[3] = 0;
+	if (tpinterf_make_cmd(targv) < 0) {
+		fprintf(stderr,
+			"error: unable to form AMFW/INFW target command\n");
+		return(-1);
+	}
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	return tpinterf_pass_output(1);
+}
+
+flashcmd_reset(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+
+	if (argc > 2) {
+		fprintf(stderr, "error: too many arguments\n");
+		return(-1);
+	}
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	return bi->ops->reset_cmd(bi);
+}
+
+flashcmd_status(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+
+	if (argc > 2) {
+		fprintf(stderr, "error: too many arguments\n");
+		return(-1);
+	}
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	return bi->ops->status_cmd(bi);
+}
+
+flashcmd_unlock(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+	u_long offset, len;
+	char *strtoul_endp;
+	struct sector_info *startsec, *endsec, *sp;
+	int stat;
+
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	if (!bi->ops->needs_unlock) {
+		fprintf(stderr,
+	    "This operation is not applicable to the selected flash type\n");
+		return(-1);
+	}
+	if (argc != 4) {
+inv:		fprintf(stderr, "usage: %s %s hex-start-offset hex-length\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	offset = strtoul(argv[2], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	if (offset >= bi->geom->total_size) {
+		fprintf(stderr,
+		"error: specified offset exceeds flash bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		return(-1);
+	}
+	len = strtoul(argv[3], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	if (len > bi->geom->total_size - offset) {
+		fprintf(stderr,
+	"error: specified offset+length exceed flash bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		return(-1);
+	}
+	if (!len) {
+		printf("Zero length specified - nothing to do!\n");
+		return(0);
+	}
+	/* now enforce sector alignment for both offset and length */
+	if (get_flash_sector_table(bank) < 0)
+		return(-1);
+	if (get_flash_sector_range(bi, offset, len, &startsec, &endsec) < 0)
+		return(-1);
+	printf("Unlocking %d sector(s)\n", endsec - startsec);
+	for (sp = startsec; sp < endsec; sp++) {
+		stat = bi->ops->unlock_sector(bi, sp);
+		if (stat)
+			return(stat);
+		putchar('.');
+		fflush(stdout);
+	}
+	putchar('\n');
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flprogbin.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,200 @@
+/*
+ * This module implements the flash program-bin command:
+ * programming flash using a binary file as the data source.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <time.h>
+#include "flash.h"
+
+extern struct flash_bank_info flash_bank_info[2];
+extern uint32_t crc32_table[];
+
+flashcmd_progbin(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+	u_long flashoff, fileoff, len, origlen, bytesdone;
+	u_long crc_base_addr, crc_from_target;
+	uint32_t crcaccum;
+	char *strtoul_endp;
+	FILE *binf;
+	struct stat filestat;
+	char *targv[4], shortarg[10], longarg[513];
+	u_char databuf[256];
+	int reclen, cc, i;
+	time_t curtime, last_time;
+
+	if (argc < 4 || argc > 6) {
+inv:		fprintf(stderr,
+		"usage: %s %s flash-offset binfile [file-offset [length]]\n",
+			argv[0], argv[1]);
+		return(-1);
+	}
+	flashoff = strtoul(argv[2], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	if (flashoff >= bi->geom->total_size) {
+		fprintf(stderr,
+		"error: specified flash offset exceeds bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		return(-1);
+	}
+	if (flashoff & 1) {
+		fprintf(stderr, "error: flash offset must be even\n");
+		return(-1);
+	}
+	binf = fopen(argv[3], "r");
+	if (!binf) {
+		perror(argv[3]);
+		return(-1);
+	}
+	fstat(fileno(binf), &filestat);
+	if (!S_ISREG(filestat.st_mode)) {
+		fprintf(stderr, "%s is not a regular file\n", argv[3]);
+		fclose(binf);
+		return(-1);
+	}
+	if (argc > 4) {
+		fileoff = strtoul(argv[4], &strtoul_endp, 16);
+		if (*strtoul_endp) {
+			fclose(binf);
+			goto inv;
+		}
+		if (fileoff > filestat.st_size) {
+			fprintf(stderr,
+			"error: specified file offset exceeds file length\n");
+			fclose(binf);
+			return(-1);
+		}
+	} else
+		fileoff = 0;
+	if (argc > 5) {
+		len = strtoul(argv[5], &strtoul_endp, 16);
+		if (*strtoul_endp) {
+			fclose(binf);
+			goto inv;
+		}
+		if (len > filestat.st_size - fileoff) {
+			fprintf(stderr,
+		"error: specified file offset+length exceed file length\n");
+			fclose(binf);
+			return(-1);
+		}
+	} else
+		len = filestat.st_size - fileoff;
+	if (!len) {
+		printf("Length is zero - nothing to do!\n");
+		fclose(binf);
+		return(0);
+	}
+	if (len > bi->geom->total_size - flashoff) {
+		fprintf(stderr,
+	"error: specified flash offset+length exceed bank size (0x%lx)\n",
+			(u_long) bi->geom->total_size);
+		fclose(binf);
+		return(-1);
+	}
+	if (len & 1) {
+		fprintf(stderr, "error: program length must be even\n");
+		fclose(binf);
+		return(-1);
+	}
+
+	/* finally done with the arg parsing etc, can get to work now */
+	if (flash_id_check(bank, 0) < 0) {
+		fclose(binf);
+		return(-1);
+	}
+	crc_base_addr = bi->base_addr + flashoff;
+	sprintf(shortarg, "%lx", (u_long) bi->base_addr);
+	targv[0] = bi->ops->loadagent_setbase_cmd;
+	targv[1] = shortarg;
+	targv[2] = 0;
+	printf("Setting flash base address: %s %s\n", targv[0], targv[1]);
+	tpinterf_make_cmd(targv);
+	if (tpinterf_send_cmd() < 0) {
+		fclose(binf);
+		return(-1);
+	}
+	cc = tpinterf_pass_output(1);
+	if (cc) {
+		fclose(binf);
+		return(cc);
+	}
+	if (bi->ops->prep_for_program(bi) < 0) {
+		fclose(binf);
+		return(-1);
+	}
+	fseek(binf, fileoff, SEEK_SET);
+	targv[0] = bi->ops->loadagent_program_cmd;
+	targv[1] = shortarg;
+	targv[2] = longarg;
+	targv[3] = 0;
+	printf("Programming flash: %lu (0x%lx) bytes\n", len, len);
+	origlen = len;
+	bytesdone = 0;
+	last_time = 0;
+	crcaccum = 0xFFFFFFFF;
+	while (len) {
+		if (len >= 256)
+			reclen = 256;
+		else
+			reclen = len;
+		cc = fread(databuf, 1, reclen, binf);
+		if (cc != reclen) {
+			fclose(binf);
+			fprintf(stderr, "error reading from %s\n", argv[3]);
+			return(-1);
+		}
+		for (i = 0; i < reclen; i++)	/* update running CRC */
+			crcaccum = crc32_table[crcaccum & 0xFF ^ databuf[i]]
+				^ (crcaccum >> 8);
+		sprintf(shortarg, "%lx", flashoff);
+		build_flashw_hex_string(databuf, longarg, reclen >> 1, 0);
+		tpinterf_make_cmd(targv);
+		if (tpinterf_send_cmd() < 0) {
+			fclose(binf);
+			return(-1);
+		}
+		i = tpinterf_pass_output(8);	/* 8 s timeout */
+		if (i) {
+			fclose(binf);
+			return(i);
+		}
+		flashoff += reclen;
+		len -= reclen;
+		bytesdone += reclen;
+		cc = bytesdone * 100 / origlen;
+		time(&curtime);
+		if (curtime != last_time || cc == 100) {
+			printf("\r0x%lx bytes programmed (%i%%)",
+				bytesdone, cc);
+			fflush(stdout);
+		}
+		last_time = curtime;
+	}
+	putchar('\n');
+	fclose(binf);
+
+	/* reset flash to read mode */
+	if (bi->ops->reset_cmd(bi) < 0)
+		return(-1);
+	printf("Verifying CRC-32 of programmed flash area\n");
+	if (crc32_on_target(crc_base_addr, origlen, &crc_from_target) < 0)
+		return(-1);
+	if (crc_from_target == crcaccum) {
+		printf("match (%08lX)\n", crc_from_target);
+		return(0);
+	} else {
+		fprintf(stderr, "error: CRC mismatch!\n");
+		return(-1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flprogsrec.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,158 @@
+/*
+ * This module implements the flash program-srec and flash program-m0 commands:
+ * programming flash using S-record files as the data source.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "flash.h"
+#include "srecreader.h"
+
+extern struct flash_bank_info flash_bank_info[2];
+
+flashcmd_progsrec_gen(bank, imgfile, is_m0)
+	char *imgfile;
+{
+	struct flash_bank_info *bi;
+	struct srecreader srr;
+	char *targv[4], shortarg[10], longarg[513];
+	int resp;
+	unsigned long rec_count;
+
+	if (flash_get_cfi(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	srr.filename = imgfile;
+	resp = open_srec_file(&srr);
+	if (resp < 0)
+		return(resp);
+	resp = flash_id_check(bank, 0);
+	if (resp) {
+		fclose(srr.openfile);
+		return(resp);
+	}
+	sprintf(shortarg, "%lx", (u_long) bi->base_addr);
+	targv[0] = bi->ops->loadagent_setbase_cmd;
+	targv[1] = shortarg;
+	targv[2] = 0;
+	printf("Setting flash base address: %s %s\n", targv[0], targv[1]);
+	tpinterf_make_cmd(targv);
+	if (tpinterf_send_cmd() < 0) {
+		fclose(srr.openfile);
+		return(-1);
+	}
+	resp = tpinterf_pass_output(1);
+	if (resp) {
+		fclose(srr.openfile);
+		return(resp);
+	}
+	if (bi->ops->prep_for_program(bi) < 0) {
+		fclose(srr.openfile);
+		return(-1);
+	}
+	targv[0] = bi->ops->loadagent_program_cmd;
+	targv[1] = shortarg;
+	targv[2] = longarg;
+	targv[3] = 0;
+	for (rec_count = 0; ; ) {
+		if (read_s_record(&srr) < 0) {
+			/* error msg already printed */
+			fclose(srr.openfile);
+			return(-1);
+		}
+		if (srr.record_type == '0') {
+			if (srr.lineno == 1)
+				continue;
+			fprintf(stderr,
+	"Warning: S0 record found in line %d of %s (expected in line 1 only)\n",
+				srr.lineno, srr.filename);
+			continue;
+		} else if (srr.record_type == '7')
+			break;
+		else if (srr.record_type != '3') {
+			fprintf(stderr,
+		"Warning: unsupported S%c record type in line %d of %s\n",
+				srr.record_type, srr.lineno, srr.filename);
+			continue;
+		}
+		/* must be S3 */
+		if (s3s7_get_addr_data(&srr) < 0) {
+			/* error msg already printed */
+			fclose(srr.openfile);
+			return(-1);
+		}
+		if (srr.datalen < 1) {
+			fprintf(stderr,
+			"%s line %d: S3 record of zero data length ignored\n",
+				srr.filename, srr.lineno);
+			continue;
+		}
+		if (srr.addr & 1 || srr.datalen & 1) {
+			fprintf(stderr,
+			"%s line %d: violates word alignment requirement\n",
+				srr.filename, srr.lineno);
+			fclose(srr.openfile);
+			return(-1);
+		}
+		srr.addr &= bi->geom->total_size - 1;
+		if (srr.addr + srr.datalen > bi->geom->total_size) {
+			fprintf(stderr,
+			"%s line %d: goes past the end of the flash bank\n",
+				srr.filename, srr.lineno);
+			fclose(srr.openfile);
+			return(-1);
+		}
+		if (!rec_count)
+		    printf("Programming flash, each \'.\' is 100 S-records\n");
+		sprintf(shortarg, "%lx", (u_long) srr.addr);
+		build_flashw_hex_string(srr.record + 5, longarg,
+					srr.datalen >> 1, is_m0);
+		tpinterf_make_cmd(targv);
+		if (tpinterf_send_cmd() < 0) {
+			fclose(srr.openfile);
+			return(-1);
+		}
+		resp = tpinterf_pass_output(8);	/* 8 s timeout */
+		if (resp) {
+			fclose(srr.openfile);
+			return(resp);
+		}
+		rec_count++;
+		if (rec_count % 100 == 0) {
+			putchar('.');
+			fflush(stdout);
+		}
+	}
+	/* got S7 */
+	fclose(srr.openfile);
+	if (!rec_count) {
+		fprintf(stderr,
+		"%s line %d: S7 without any preceding S3 data records\n",
+			srr.filename, srr.lineno);
+		return(-1);
+	}
+	printf("\nProgramming complete\n");
+	return(0);
+}
+
+flashcmd_program_srec(argc, argv, bank)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s %s image.srec\n", argv[0], argv[1]);
+		return(-1);
+	}
+	return flashcmd_progsrec_gen(bank, argv[2], 0);
+}
+
+flashcmd_program_m0(argc, argv, bank)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s %s image.m0\n", argv[0], argv[1]);
+		return(-1);
+	}
+	return flashcmd_progsrec_gen(bank, argv[2], 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/flutil.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,350 @@
+/*
+ * Miscellaneous utility functions for flash support
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include "flash.h"
+
+extern struct flash_bank_info flash_bank_info[2];
+extern struct flash_cmdset flash_cmdset_amd;
+extern struct flash_cmdset flash_cmdset_intel;
+
+static int
+cfi_read_byte(bi, off, ret16p)
+	struct flash_bank_info *bi;
+	int off;
+	uint16_t *ret16p;
+{
+	return do_r16(bi->base_addr + (off << 1), ret16p);
+}
+
+static int
+cfi_read_twobyte(bi, off, retptr)
+	struct flash_bank_info *bi;
+	int off;
+	uint16_t *retptr;
+{
+	uint16_t lo, hi;
+
+	if (cfi_read_byte(bi, off, &lo) < 0)
+		return(-1);
+	lo &= 0xFF;
+	if (cfi_read_byte(bi, off + 1, &hi) < 0)
+		return(-1);
+	hi &= 0xFF;
+	*retptr = (hi << 8) | lo;
+	return(0);
+}
+
+flash_get_cfi(bank)
+{
+	struct flash_bank_info *bi;
+	struct flash_geom *geom;
+	struct flash_region_desc *reg;
+	int nr;
+	uint16_t rdval, cmdset_id;
+	uint32_t size_check;
+
+	bi = flash_bank_info + bank;
+	if (bi->geom)
+		return(0);
+	printf("Performing CFI query\n");
+	if (do_w16(bi->base_addr + 0xAA, 0x98)) {
+		fprintf(stderr, "unexpected response to w16 - aborting\n");
+		return(-1);
+	}
+	/* if do_r16() returns -1, error msg has already been printed */
+	if (cfi_read_byte(bi, 0x10, &rdval) < 0)
+		return(-1);
+	if (rdval != 'Q') {
+noqry:		fprintf(stderr, "error: no QRY response from flash\n");
+		amd_reset_cmd(bi);
+		return(-1);
+	}
+	if (cfi_read_byte(bi, 0x11, &rdval) < 0)
+		return(-1);
+	if (rdval != 'R')
+		goto noqry;
+	if (cfi_read_byte(bi, 0x12, &rdval) < 0)
+		return(-1);
+	if (rdval != 'Y')
+		goto noqry;
+	if (cfi_read_twobyte(bi, 0x13, &cmdset_id) < 0)
+		return(-1);
+	if (!bi->ops) {
+		switch (cmdset_id) {
+		case 2:
+			bi->ops = &flash_cmdset_amd;
+			break;
+		case 3:
+			bi->ops = &flash_cmdset_intel;
+			break;
+		default:
+			fprintf(stderr, "error: command set %04X unsupported\n",
+				cmdset_id);
+			amd_reset_cmd(bi);
+			return(-1);
+		}
+	}
+	geom = malloc(sizeof(struct flash_geom));
+	if (!geom) {
+		fprintf(stderr,
+	"unable to malloc buffer for flash bank %d CFI geometry structure\n",
+			bank);
+		bi->ops->reset_cmd(bi);
+		return(-1);
+	}
+	/* total device size */
+	if (cfi_read_byte(bi, 0x27, &rdval) < 0) {
+free_and_immed_out:
+		free(geom);
+		return(-1);
+	}
+	if (rdval < 20 || rdval > 24) {
+		fprintf(stderr,
+			"error: CFI reports unreasonable device size\n");
+free_and_clean_out:
+		free(geom);
+		bi->ops->reset_cmd(bi);
+		return(-1);
+	}
+	geom->total_size = 1 << rdval;
+	if (geom->total_size > bi->bank_desc->align_size) {
+		fprintf(stderr,
+	"error: CFI device size 0x%lx exceeds configured maximum 0x%lx\n",
+			(u_long) geom->total_size, bi->bank_desc->align_size);
+		goto free_and_clean_out;
+	}
+	if (cfi_read_byte(bi, 0x2C, &rdval) < 0)
+		goto free_and_immed_out;
+	if (rdval < 1 || rdval > CFI_MAX_REGIONS) {
+		fprintf(stderr,
+			"error: CFI reports unreasonable # of erase regions\n");
+		goto free_and_clean_out;
+	}
+	geom->nregions = rdval;
+	geom->total_sectors = 0;
+	size_check = 0;
+	for (nr = 0; nr < geom->nregions; nr++) {
+		reg = geom->regions + nr;
+		if (cfi_read_twobyte(bi, 0x2D + nr*4, &rdval) < 0)
+			goto free_and_immed_out;
+		if (rdval > 255) {
+			fprintf(stderr,
+		"error: CFI reports unreasonable # of sectors in region %d\n",
+				nr);
+			goto free_and_clean_out;
+		}
+		reg->nsectors = rdval + 1;
+		geom->total_sectors += reg->nsectors;
+		if (cfi_read_twobyte(bi, 0x2F + nr*4, &rdval) < 0)
+			goto free_and_immed_out;
+		if (rdval < 0x20 || rdval > 0x400) {
+			fprintf(stderr,
+		"error: CFI reports unreasonable sector size in region %d\n",
+				nr);
+			goto free_and_clean_out;
+		}
+		reg->sector_size = rdval << 8;
+		size_check += reg->sector_size * reg->nsectors;
+	}
+	if (bi->ops->reset_cmd(bi) < 0) {
+		/* error msg already printed */
+		free(geom);
+		return(-1);
+	}
+	if (size_check != geom->total_size) {
+		fprintf(stderr,
+"CFI error: added size of erase regions (%lx) != reported devive size (%lx)\n",
+			(u_long) size_check, (u_long) geom->total_size);
+		free(geom);
+		return(-1);
+	}
+	/* all checks passed */
+	bi->geom = geom;
+	printf(
+"CFI query successful: total size %lx, %u sectors, command set style %04X\n",
+		(u_long) geom->total_size, geom->total_sectors, cmdset_id);
+	return(1);
+}
+
+get_flash_sector_table(bank)
+{
+	struct flash_bank_info *bi;
+	struct flash_geom *geom;
+	struct flash_region_desc *reg;
+	struct sector_info *sp;
+	uint32_t offset;
+	int nr, i;
+
+	bi = flash_bank_info + bank;
+	if (bi->sectors)
+		return(0);
+	i = flash_get_cfi(bank);
+	if (i < 0)
+		return(i);
+	geom = bi->geom;
+	sp = (struct sector_info *) malloc(sizeof(struct sector_info)
+						* (geom->total_sectors + 1));
+	if (!sp) {
+		fprintf(stderr,
+		"unable to malloc buffer for flash bank %d sector table\n",
+			bank);
+		return(-1);
+	}
+	bi->sectors = sp;
+	/* now fill it */
+	offset = 0;
+	for (nr = 0; nr < geom->nregions; nr++) {
+		reg = geom->regions + nr;
+		for (i = 0; i < reg->nsectors; i++) {
+			sp->start = offset;
+			sp->size = reg->sector_size;
+			sp++;
+			offset += reg->sector_size;
+		}
+	}
+	/* sanity checks */
+	if (sp - bi->sectors != geom->total_sectors) {
+		fprintf(stderr,
+	"BUG in get_flash_sector_table(): wrong # of sectors at the end\n");
+		abort();
+	}
+	if (offset != geom->total_size) {
+		fprintf(stderr,
+		"BUG in get_flash_sector_table(): wrong offset at the end\n");
+		abort();
+	}
+	/* finish */
+	sp->start = 0;
+	sp->size = 0;
+	return(0);
+}
+
+flashcmd_sectors(argc, argv, bank)
+	char **argv;
+{
+	struct flash_bank_info *bi;
+	struct sector_info *sp;
+
+	if (argc > 2) {
+		fprintf(stderr, "error: too many arguments\n");
+		return(-1);
+	}
+	if (get_flash_sector_table(bank) < 0)
+		return(-1);
+	bi = flash_bank_info + bank;
+	printf("%u sectors in flash bank %d:\n", bi->geom->total_sectors, bank);
+	printf("Offset    Size\n");
+	for (sp = bi->sectors; sp->size; sp++)
+		printf("%08lX  %lx\n", (u_long) sp->start, (u_long) sp->size);
+	return(0);
+}
+
+get_flash_sector_range(bi, useroff, userlen, startp, endp)
+	struct flash_bank_info *bi;
+	u_long useroff, userlen;
+	struct sector_info **startp, **endp;
+{
+	struct sector_info *sp;
+	uint32_t remlen;
+
+	for (sp = bi->sectors; sp->size; sp++)
+		if (sp->start == useroff)
+			break;
+	if (!sp->size) {
+		fprintf(stderr,
+	"error: specified offset not aligned to a flash sector boundary\n");
+		return(-1);
+	}
+	*startp = sp;
+	for (remlen = userlen; remlen; ) {
+		if (remlen < sp->size) {
+			fprintf(stderr,
+	"error: specified length not aligned to a flash sector boundary\n");
+			return(-1);
+		}
+		remlen -= sp->size;
+		sp++;
+	}
+	*endp = sp;
+	return(0);
+}
+
+build_flashw_hex_string(bin, strbuf, nwords, m0src)
+	u_char *bin;
+	char *strbuf;
+	int nwords, m0src;
+{
+	int i;
+	u_char *dp;
+	char *s;
+
+	for (dp = bin, s = strbuf, i = 0; i < nwords; dp += 2, s += 4, i++) {
+		if (m0src)
+			sprintf(s, "%02X%02X", dp[0], dp[1]);
+		else
+			sprintf(s, "%02X%02X", dp[1], dp[0]);
+	}
+	*s = '\0';
+}
+
+flash_id_check(bank, repeat)
+{
+	struct flash_bank_info *bi;
+	struct flash_bank_desc *bd;
+	struct flash_idcheck *id;
+	int stat, fail;
+	uint16_t rdval;
+	unsigned cnt;
+
+	bi = flash_bank_info + bank;
+	if (bi->idcheck_done && !repeat)
+		return(0);
+	bd = bi->bank_desc;
+	if (!bd->idcheck_table || !bd->idcheck_num)
+		return(0);
+	printf("Performing flash ID check\n");
+	stat = do_w16(bi->base_addr + 0xAAA, 0xAA);
+	if (stat) {
+bad_w16:	fprintf(stderr,
+	"unexpected response to w16 in read ID cmd sequence - aborting\n");
+		return(-1);
+	}
+	stat = do_w16(bi->base_addr + 0x554, 0x55);
+	if (stat)
+		goto bad_w16;
+	stat = do_w16(bi->base_addr + 0xAAA, 0x90);
+	if (stat)
+		goto bad_w16;
+	id = bd->idcheck_table;
+	fail = 0;
+	for (cnt = 0; cnt < bd->idcheck_num; cnt++) {
+		stat = do_r16(bi->base_addr + id->offset, &rdval);
+		if (stat)
+			return(stat);	/* error msg already printed */
+		printf("offset %02X: %04X -- ", (int)id->offset, (int)rdval);
+		if (rdval == id->expect_val)
+			printf("PASS\n");
+		else {
+			printf("FAIL: expected %04X\n", (int)id->expect_val);
+			fail = 1;
+			break;
+		}
+		id++;
+	}
+	/* reset flash to read mode */
+	stat = do_w16(bi->base_addr + 0xAAA, 0xF0);
+	if (stat) {
+		fprintf(stderr,
+	"unexpected response to w16 when resetting flash to read mode!\n");
+		return(-1);
+	}
+	if (fail)
+		return(-1);
+	bi->idcheck_done = 1;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/gta-ap-build.sed	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,3 @@
+s,^CC=.*$,CC=	/opt/arm-2012.03/bin/arm-none-linux-gnueabi-gcc,
+s,^CFLAGS=.*$,CFLAGS=	-O2 -march=armv4t -mtune=arm920t -DGTA0x_AP_BUILD,
+s,^EXTRA_OBJ=.*$,EXTRA_OBJ=	compaldummy.o gtapower.o,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/gtapower.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,54 @@
+/*
+ * This module is included only when loadtools are being built to run on the
+ * GTA0x application processor (AP).  It provides automated modem power
+ * control, i.e., coordinates modem power control with loadtools operations
+ * for convenience.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * Check this pathname: it is correct for the kernel version I'm using
+ * on my test GTA02, but it differs for some other kernel versions.
+ */
+static char modem_powerctl_pathname[] =
+	"/sys/bus/platform/devices/gta02-pm-gsm.0/power_on";
+
+void
+set_gta_modem_power_ctrl(boolval)
+{
+	char strbuf[16];
+	int len, fd;
+
+	len = sprintf(strbuf, "%d\n", boolval);
+	fd = open(modem_powerctl_pathname, O_WRONLY);
+	if (fd < 0) {
+		perror(modem_powerctl_pathname);
+		exit(1);
+	}
+	write(fd, strbuf, len);
+	close(fd);
+}
+
+void
+fork_gta_modem_poweron()
+{
+	int i;
+
+	i = fork();
+	if (i < 0) {
+		perror("fork");
+		exit(1);
+	}
+	if (i)
+		return;
+	printf("Toggling %s\n", modem_powerctl_pathname);
+	set_gta_modem_power_ctrl(0);
+	usleep(350000);
+	set_gta_modem_power_ctrl(1);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/hexdecode.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,30 @@
+/*
+ * This module contains the decode_hex_byte() function,
+ * which is used by the SREC file reader and will likely be used
+ * by other code as well, such as the dump-to-file function
+ * of loadtool.
+ */
+
+#include <ctype.h>
+
+decode_hex_byte(s)
+	char *s;
+{
+	register int u, l;
+
+	if (!isxdigit(s[0]) || !isxdigit(s[1]))
+		return(-1);
+	if (isdigit(s[0]))
+		u = s[0] - '0';
+	else if (isupper(s[0]))
+		u = s[0] - 'A' + 10;
+	else
+		u = s[0] - 'a' + 10;
+	if (isdigit(s[1]))
+		l = s[1] - '0';
+	else if (isupper(s[1]))
+		l = s[1] - 'A' + 10;
+	else
+		l = s[1] - 'a' + 10;
+	return((u << 4) | l);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/hwparam.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,190 @@
+/*
+ * This module contains the code that reads the hardware parameter files
+ * specified with -h or -H, and sets variables for later use by other code.
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char default_helpers_dir[];
+
+extern void set_boot_reflash_hack();
+extern void set_default_exit_mode();
+extern void set_flash_device();
+
+char hw_init_script[128];
+
+static void
+handle_compal_stage(arg, filename_for_errs, lineno_for_errs)
+	char *arg;
+	char *filename_for_errs;
+	int lineno_for_errs;
+{
+	char *cp;
+
+	while (isspace(*arg))
+		arg++;
+	if (!*arg) {
+		fprintf(stderr,
+		"%s line %d: compal-stage setting requires an argument\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	for (cp = arg; *cp && !isspace(*cp); cp++)
+		;
+	*cp = '\0';
+	set_compalstage_short(arg);
+}
+
+static void
+handle_init_script(arg, filename_for_errs, lineno_for_errs)
+	char *arg;
+	char *filename_for_errs;
+	int lineno_for_errs;
+{
+	char *cp;
+
+	while (isspace(*arg))
+		arg++;
+	if (!*arg) {
+		fprintf(stderr,
+		"%s line %d: init-script setting requires an argument\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	for (cp = arg; *cp && !isspace(*cp); cp++)
+		;
+	*cp = '\0';
+	if (cp - arg > sizeof(hw_init_script) - 1) {
+		fprintf(stderr,
+	"%s line %d: init-script argument is too long (buffer overflow)\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	strcpy(hw_init_script, arg);
+}
+
+static void
+handle_pll_config(arg, filename_for_errs, lineno_for_errs)
+	char *arg;
+	char *filename_for_errs;
+	int lineno_for_errs;
+{
+	int mult, div;
+
+	while (isspace(*arg))
+		arg++;
+	if (!isdigit(*arg)) {
+inv:		fprintf(stderr, "%s line %d: pll-config argument must be M/N\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	mult = atoi(arg);
+	arg++;
+	if (isdigit(*arg))
+		arg++;
+	if (*arg++ != '/')
+		goto inv;
+	if (!isdigit(*arg))
+		goto inv;
+	div = atoi(arg);
+	arg++;
+	if (*arg && !isspace(*arg))
+		goto inv;
+	if (mult < 0 || mult > 31 || div < 1 || div > 4) {
+		fprintf(stderr,
+			"%s line %d: pll-config argument is out of range\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	set_romload_pll_conf((mult << 2) | (div - 1));
+}
+
+static void
+handle_rhea_cntl(arg, filename_for_errs, lineno_for_errs)
+	char *arg;
+	char *filename_for_errs;
+	int lineno_for_errs;
+{
+	int byte;
+
+	while (isspace(*arg))
+		arg++;
+	if (arg[0] == '0' && (arg[1] == 'x' || arg[1] == 'X'))
+		arg += 2;
+	byte = decode_hex_byte(arg);
+	if (byte < 0 || arg[2] && !isspace(arg[2])) {
+		fprintf(stderr,
+		"%s line %d: rhea-cntl argument must be a hex byte value\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	set_romload_rhea_cntl(byte);
+}
+
+static struct cmdtab {
+	char *name;
+	void (*func)();
+} cmdtab[] = {
+	{"boot-reflash-hack", set_boot_reflash_hack},
+	{"compal-stage", handle_compal_stage},
+	{"exit-mode", set_default_exit_mode},
+	{"flash", set_flash_device},
+	{"init-script", handle_init_script},
+	{"pll-config", handle_pll_config},
+	{"rhea-cntl", handle_rhea_cntl},
+	{0, 0}
+};
+
+void
+read_hwparam_file_fullpath(filename)
+	char *filename;
+{
+	FILE *f;
+	char linebuf[512];
+	int lineno;
+	char *cp, *np;
+	struct cmdtab *tp;
+
+	f = fopen(filename, "r");
+	if (!f) {
+		perror(filename);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) {
+		for (cp = linebuf; isspace(*cp); cp++)
+			;
+		if (!*cp || *cp == '#')
+			continue;
+		for (np = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		for (tp = cmdtab; tp->name; tp++)
+			if (!strcmp(tp->name, np))
+				break;
+		if (tp->func)
+			tp->func(cp, filename, lineno);
+		else {
+			fprintf(stderr,
+				"%s line %d: setting \"%s\" not understood\n",
+				filename, lineno, np);
+			exit(1);
+		}
+	}
+	fclose(f);
+}
+
+void
+read_hwparam_file_shortname(confname)
+	char *confname;
+{
+	char pathname[MAXPATHLEN];
+
+	sprintf(pathname, "%s/%s.config", default_helpers_dir, confname);
+	read_hwparam_file_fullpath(pathname);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/hwparamstubs.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+/*
+ * The exit-mode and flash settings in the hardware parameter files
+ * specified with -h or -H are meaningful only for fc-loadtool, but
+ * the same hwparam.c code is included in fc-iram and fc-xram as well.
+ * This module provides the stubs, allowing fc-iram and fc-xram to link.
+ */
+
+void
+set_default_exit_mode()
+{
+}
+
+void
+set_flash_device()
+{
+}
+
+void
+set_boot_reflash_hack()
+{
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/initscript.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,124 @@
+/*
+ * This module has been copied from ltscript.c, ltdispatch.c and ltpassthru.c:
+ * here we implement the init-script functionality for fc-xram.
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char default_helpers_dir[];
+
+extern int cmd_baud();
+
+static
+loadagent_cmd(argc, argv)
+	char **argv;
+{
+	if (tpinterf_make_cmd(argv) < 0) {
+		fprintf(stderr, "error: unable to form target command\n");
+		return(-1);
+	}
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	return tpinterf_pass_output(1);
+}
+
+static struct cmdtab {
+	char *cmd;
+	int minargs;
+	int maxargs;
+	int (*func)();
+} cmdtab[] = {
+	{"baud", 1, 1, cmd_baud},
+	{"w8", 2, 2, loadagent_cmd},
+	{"w16", 2, 2, loadagent_cmd},
+	{"w32", 2, 2, loadagent_cmd},
+	{0, 0, 0, 0}
+};
+
+static
+dispatch_cmd(cmd)
+	char *cmd;
+{
+	char *argv[10];
+	char *cp, **ap;
+	struct cmdtab *tp;
+
+	for (cp = cmd; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#')
+		return(0);
+	printf("init-script command: %s\n", cp);
+	argv[0] = cp;
+	while (*cp && !isspace(*cp))
+		cp++;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr, "error: no such command\n");
+		return(-1);
+	}
+	for (ap = argv + 1; ; ) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp || *cp == '#')
+			break;
+		if (ap - argv - 1 >= tp->maxargs) {
+			fprintf(stderr, "error: too many arguments\n");
+			return(-1);
+		}
+		*ap++ = cp;
+		while (*cp && !isspace(*cp))
+			cp++;
+		if (*cp)
+			*cp++ = '\0';
+	}
+	if (ap - argv - 1 < tp->minargs) {
+		fprintf(stderr, "error: too few arguments\n");
+		return(-1);
+	}
+	*ap = 0;
+	return tp->func(ap - argv, argv);
+}
+
+exec_init_script(script_name)
+	char *script_name;
+{
+	char pathbuf[MAXPATHLEN], *openfname;
+	FILE *f;
+	char linebuf[512], *cp;
+	int lineno, retval = 0;
+
+	if (index(script_name, '/'))
+		openfname = script_name;
+	else {
+		sprintf(pathbuf, "%s/%s", default_helpers_dir, script_name);
+		openfname = pathbuf;
+	}
+	f = fopen(openfname, "r");
+	if (!f) {
+		perror(openfname);
+		return(-1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) {
+		cp = index(linebuf, '\n');
+		if (!cp) {
+			fprintf(stderr, "%s line %d: missing newline\n",
+				openfname, lineno);
+			fclose(f);
+			return(-1);
+		}
+		*cp = '\0';
+		retval = dispatch_cmd(linebuf);
+		if (retval)
+			break;
+	}
+	fclose(f);
+	return(retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/install-helpers.sh	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,25 @@
+#!/bin/sh
+# A functional installation of FreeCalypso loadtools consists of not only
+# the fc-* host binaries, but also the loadagent and compalstage target
+# binaries and some script and help files.  This shell script is
+# responsible for installing the latter.
+
+instdir=/usr/local/share/freecalypso
+set -ex
+mkdir -p $instdir
+
+# The target-binaries directory may or may not be present.  Loadagent and
+# compalstage are built in the target-utils tree with the ARM7 toolchain,
+# hence having prebuilt binaries would be an important convenience for
+# end user releases.  But if one is working with just a source tree, with
+# nothing prebuilt, there will be no target-binaries directory here;
+# one needs to have the ARM7 toolchain installed, then build target-utils,
+# then do a 'make install' there.
+
+if [ -d target-binaries ]
+then
+	install -c target-binaries/* $instdir
+fi
+
+# scripts and loadtool.help should always be present
+install -c scripts/* loadtool.help $instdir
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/labaud.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,48 @@
+/*
+ * This module handles the switching of serial baud rates
+ * in coordination with loadagent.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.h>
+#include "baudrate.h"
+
+extern int target_fd;
+extern struct baudrate *current_baud_rate;
+extern struct baudrate *find_baudrate_by_name();
+
+loadagent_switch_baud(newbr)
+	struct baudrate *newbr;
+{
+	char *argv[3];
+	static char U = 'U';
+
+	printf("Switching loadagent communication to %s baud\n", newbr->name);
+	argv[0] = "baud";
+	argv[1] = newbr->name;
+	argv[2] = 0;
+	tpinterf_make_cmd(argv);
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	switch_baud_rate(newbr);
+	usleep(150000);
+	write(target_fd, &U, 1);
+	return tpinterf_pass_output(1);
+}
+
+cmd_baud(argc, argv)
+	char **argv;
+{
+	struct baudrate *br;
+
+	if (argc < 2) {
+		printf("Current baud rate is %s\n", current_baud_rate->name);
+		return(0);
+	}
+	br = find_baudrate_by_name(argv[1]);
+	if (!br)
+		return(-1);	/* error msg already printed */
+	return loadagent_switch_baud(br);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/loadtool.help	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,348 @@
+This is the help file for fc-loadtool.  See lthelp.c for the code
+that parses and displays it.  The parsing code is the only
+documentation for the format of this help file - if you are going to
+edit this help text, read the parsing code first.
+
+=== main
+This utility allows you to perform the following operations
+on your Calypso GSM device:
+
+* Peek and poke registers
+* Dump any part of memory
+* Read and program flash
+
+See the following help topics for more information:
+
+help peekpoke		Register peek and poke commands
+help flash		Flash operation commands
+help all		List of all implemented commands
+help exit		Controlling the cleanup on exit
+
+=== all
+abbr		Read an ABB register
+abbw		Write an ABB register
+baud		Switch the serial comm with loadagent to a different baud rate
+crc32		Get CRC-32 of a memory area on the target
+dieid		Read the Calypso die ID
+dump		Dump a target memory region in hex and ASCII
+dump2bin	Dump a target memory region to a file in binary format
+dump2srec	Dump a target memory region to a file in S-record format
+exec		Execute a command script
+exit		Exit from loadtool and clean up the target device state
+flash		Flash operations
+flash2		Operations on the 2nd flash bank (Pirelli phone only)
+quit		Alias for exit
+r8		Read an 8-bit register or memory location
+r16		Read a 16-bit register or memory location
+r32		Read a 32-bit register or memory location
+w8		Write an 8-bit register or memory location
+w16		Write a 16-bit register or memory location
+w32		Write a 32-bit register or memory location
+
+To get help on any command, type help and the command keyword.
+
+=== abbr
+=== abbw
+abbr pg reg		Read ABB register <reg> on page <pg>
+abbw pg reg val		Write <val> into register <reg> on page <pg>
+
+The <pg> and <reg> arguments default to decimal unless prefixed with 0x;
+the <val> argument to abbw is always hexadecimal.
+
+=== baud
+baud		Display the current baud rate
+baud <rate>	Switch the baud rate to <rate> (number in bps)
+
+The supported baud rates are:
+
+Standard: 19200, 38400, 57600, 115200
+Calypso special: 203125, 406250, 812500
+
+The baud command coordinates the necessary simultaneous switching of the
+baud rate on both the host and the target (loadagent).  Loadagent always
+supports all of the listed rates and will switch happily, but in the case
+of the higher non-standard baud rates fc-loadtool has no way of knowing
+ahead of time whether or not the requested baud rate is supported by your
+usb2serial adapter or whatever other serial port hardware and driver
+you are using.  If the baud rate switch fails, the communication link with
+the target will be broken and fc-loadtool will exit ungracefully.
+
+=== crc32
+crc32 hex-start hex-len
+
+The first argument is the starting target memory address in hex; the second
+argument is the length of the area to CRC, also in hex.  The command will be
+sent to loadagent; CRC-32 of the requested range will be computed on the target
+and displayed in hex.
+
+=== dieid
+dieid			Display the Calypso die ID
+dieid <filename>	Display the Calypso die ID, and also save it in the
+			named file
+
+The Calypso die ID resides in 4 16-bit registers at target addresses
+0xFFFEF010 through 0xFFFEF016.  It is displayed on the terminal and optionally
+saved to a file in this simple form:
+
+FFFEF010: xxxx
+FFFEF012: xxxx
+FFFEF014: xxxx
+FFFEF016: xxxx
+
+=== dump
+dump hex-start hex-len
+
+The first argument is the starting target memory address in hex; the second
+argument is the length of the area to dump, also in hex.  The dump will be
+displayed on the terminal in hex and ASCII.
+
+=== dump2bin
+=== dump2srec
+dump2bin hex-start hex-len outfile
+dump2srec hex-start hex-len outfile
+
+The first argument is the starting target memory address in hex; the second
+argument is the length of the area to dump, also in hex; the third argument
+is the name of the output file to be created/written.  The dump will be saved
+in binary or S-records as per the chosen command, always in the native byte
+order of the Calypso ARM7 target (little-endian).
+
+=== exec
+exec <script-file>
+
+Read and execute commands from the named file.  If the given file name contains
+no slashes, the script file is sought in the default directory (normally
+/usr/local/share/freecalypso unless changed in the code); otherwise the given
+name is used directly as the pathname.
+
+=== exit
+=== quit
+exit		Clean up the target in the default manner and exit
+exit bare	Exit loadtool without doing anything to the target
+exit iota-off	Exit loadtool and command an ABB power-off on the target
+exit jump0	Exit loadtool and command the target to reboot via jump to 0
+
+The default method of cleaning up the target device state upon exit is set
+in the hardware parameters file selected with the -h or -H command line
+option; it is the exit-mode setting.  On the Pirelli phone the default exit
+mode is jump0: it causes the phone to reboot and enter the "charging boot"
+mode, as the USB cable is connected and VBUS is present.  On Compal phones the
+default exit mode is iota-off.
+
+If your device is a GTA02 and you are running fc-loadtool from inside the phone
+(from the AP), the exit command will power off the modem from the AP.  If you
+are talking to GTA02 Calypso from an external host via the headset jack, there
+is nothing that fc-loadtool can currently do to power the modem off (exit is
+the same as exit bare in this case) - power it off yourself from the AP.
+
+If you do an exit bare without powering the target device off in some other
+way, the device will still be running loadagent, which is probably not what you
+want.  However, it is the proper exit mode if you have powered off or reset the
+target device by brute force (yanking the battery).
+
+=== flash
+=== flash2
+The primary end use of fc-loadtool is for reading and writing the NOR flash
+memories of the supported GSM devices.  Compal phones and the GTA0x GSM modem
+have only one flash bank (as in chip select), and are manipulated with the
+flash command.  The Pirelli phone has two flash banks (as in chip selects) of
+8 MiB each; they are manipulated with the flash and flash2 commands.
+
+The following flash operations are available on all target devices:
+
+flash blankchk		Blank-check a region of flash
+flash dump2bin		Dump flash content to a file in binary format
+flash dump2srec		Dump flash content to a file in S-record format
+flash erase		Erase a region of flash
+flash info		Display flash configuration info
+flash program-bin	Program flash with a binary file
+flash program-m0	Program flash with an image in TI's *.m0 format
+flash program-srec	Program flash with an image in standard S-record format
+flash quickprog		Program a few flash words from the command line
+flash reset		Reset flash chip to read array mode
+flash sectors		Display the list of flash sector addresses and sizes
+
+Substitute flash2 instead of flash when operating on the 2nd flash chip select
+of Pirelli-style flash memory configurations.  Prepend help before a command to
+get usage information, e.g., help flash program-bin.  See help compal for some
+additional info specific to Compal targets.
+
+=== compal
+=== compalflash
+=== flash:compal
+Compal phones have Intel or Intel-style flash chips; other Calypso targets for
+which loadtool was originally designed have AMD-style flash chips.  The author
+of the present software has a personal bias toward AMD-style flash, hence the
+support for Intel-style flash is not as clean.  Compal phones also have the
+Calypso boot ROM disabled, and depend on flash-resident boot code instead.
+This property makes them brickable.
+
+The following additional loadtool commands apply only to Compal targets with
+Intel-style flash:
+
+flash erase-program-boot	Erase and reprogram the boot sector
+flash status			Read Intel flash Status Register
+flash unlock			Unlock flash sectors
+
+=== flash:blankchk
+flash[2] blankchk hex-start-offset hex-length
+
+Blank-checks an area of flash starting at the specified hex offset (from the
+base address of the flash bank) and extending for the specified hex length.
+Reports whether the checked region is blank or not.  Flash must be blank (FF in
+all bytes) before it can be programmed.
+
+=== flash:dump2bin
+flash[2] dump2bin outfile [offset [length]]
+
+Read device flash content and save it to a file.  If only a filename is
+specified, the full flash bank is dumped; one can optionally specify a starting
+offset and an explicit length, both in hex.
+
+This command is merely a user-friendly front-end to the plain dump2bin command;
+see help dump2bin.
+
+=== flash:dump2srec
+flash[2] dump2srec outfile [offset [length]]
+
+Read device flash content and save it to a file.  If only a filename is
+specified, the full flash bank is dumped; one can optionally specify a starting
+offset and an explicit length, both in hex.
+
+This command is merely a user-friendly front-end to the plain dump2srec command;
+see help dump2srec.
+
+=== flash:erase
+flash[2] erase hex-start-offset hex-length
+
+Erases an area of flash starting at the specified hex offset (from the base
+address of the flash bank) and extending for the specified hex length.
+
+Flash memory can only be erased (turning 0 bits back to 1s) in units of
+sectors, as set in stone by the design of the flash chip in use.  Loadtool
+knows the sector layout of the flash chip in your device from CFI or from the
+hardware parameters file (you can display it with the flash[2] sectors
+command), and enforces that both arguments to the flash[2] erase command lie
+on sector boundaries.
+
+=== flash:erase-program-boot
+flash erase-program-boot binfile [length]
+
+This operation is applicable to Compal targets only.  This command erases and
+reprograms flash sector 0 (the boot sector) with minimized vulnerability to
+bricking by loading the new boot code into a scratchpad RAM area on the target,
+then commanding loadagent (running on the target) to erase and reprogram the
+dangerous flash sector without requiring further interaction with loadtool.
+(In contrast, loadtool's "regular" flash erase and program operations are
+driven primarily by loadtool, with loadagent providing only low-level
+functions.)
+
+The new bits to be programmed are taken from the specified binary file.  Byte 0
+of the file goes into byte 0 of the flash and so on, for the specified length.
+If no length argument is given, it defaults to the length of the file, which
+must not exceed the length of flash sector 0: 64 KiB on the "basic" Compal
+phones or 8 KiB on C155/156.
+
+=== flash:info
+This command displays summary information about the flash memory configuration
+of the Calypso target device loadtool thinks it's talking to.
+
+=== flash:program-bin
+flash[2] program-bin flash-offset binfile [file-offset [length]]
+
+This command programs flash, using a binary file as the data source.  One must
+always specify the starting flash offset (from the base address of the flash
+bank), but the starting file offset and length are optional, defaulting to the
+whole file.
+
+The binary file must be in the native byte order of the Calypso ARM7 processor,
+which is little-endian.  Images produced by flash[2] dump2bin are suitable.
+
+=== flash:program-m0
+flash[2] program-m0 image.m0
+
+*.m0 is the format that has been used by companies like TI and Closedmoko for
+their proprietary firmware images.  It is emitted by TI's hex470 tool, and in
+the proprietary environment it is fed as an input to FLUID.  (The latter is
+TI's Flash Loader Utility Independent of Device, and fc-loadtool can be seen as
+an independent reimplementation thereof - although it doesn't have the same
+level of device-independence.)  The *.m0 format is actually a variant of
+Motorola's SREC, but with a different byte order: each 16-bit word is
+byte-reversed relative to the native byte order of the ARM7 processor.
+(This strange byte order actually makes some sense if one views the image as a
+long array of 16-bit hex values; 16 bits is the width of the flash memory on
+Calypso GSM devices and thus the natural unit size for flash programming.)
+
+Because each S-record contains an address, no addresses or offsets need to be
+specified in the flash[2] program-m0 command, only the image file.
+
+=== flash:program-srec
+flash[2] program-srec image.srec
+
+This command programs the flash with an image in the standard S-record format,
+in the native little-endian byte order of the Calypso ARM7 processor.  It is
+the opposite byte order from that used by TI's *.m0 files -
+see help flash program-m0.  Images produced by flash[2] dump2srec are suitable
+for flash[2] program-srec.
+
+Because each S-record contains an address, no addresses or offsets need to be
+specified in the flash[2] program-srec command, only the image file.
+
+=== flash:quickprog
+flash[2] quickprog hex-offset hex-data-string
+
+This command is intended only for developers; it provides raw access to
+loadagent's basic flash write primitive.  Read the source code for more
+information.
+
+=== flash:reset
+Intel-style flash memory chips (found in Compal phones) have two "stable" or
+"quiescent" states: reading array data and reading the status register (SR).
+After an erase or program operation the flash chip is "parked" in the Read SR
+state; the flash reset command switches it back to reading array data.
+
+This command works for AMD-style flash as well (found in Openmoko and Pirelli
+phones), but it is normally not needed, as AMD-style flash chips automatically
+return to the read-array-data state after every erase or program operation.
+
+=== flash:sectors
+This command displays the list of sector offsets and sizes for the flash chip
+in the Calypso target device loadtool thinks it's talking to.
+
+=== flash:status
+This command is only applicable to Intel-style flash as found in Compal phones.
+It reads the flash chip's Status Register, which can be used to diagnose errors
+incurred by previous erase or program operations.
+
+=== flash:unlock
+flash unlock hex-start-offset hex-length
+
+This command is only applicable to Intel-style flash as found in Compal phones.
+These flash chips power up with each sector (erase block) in the "locked" state;
+each sector needs to be unlocked before it can be erased or programmed.
+
+This command is normally not needed, as the flash erase command unlocks each
+sector before erasing it.  However, if you are going to perform program
+operations in some sectors without erasing them, you will need to unlock them
+explicitly first.
+
+This command operates only on sector boundaries just like flash erase.
+
+=== r8
+=== r16
+=== r32
+=== w8
+=== w16
+=== w32
+=== peekpoke
+The register peek and poke commands are:
+
+r8 addr		Read an 8-bit register or memory location
+r16 addr	Read a 16-bit register or memory location
+r32 addr	Read a 32-bit register or memory location
+w8 addr data	Write an 8-bit register or memory location
+w16 addr data	Write a 16-bit register or memory location
+w32 addr data	Write a 32-bit register or memory location
+
+All addresses and data values are in hex.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ltdispatch.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,97 @@
+/*
+ * This module implements the command dispatch for fc-loadtool
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern int cmd_baud();
+extern int cmd_crc32();
+extern int cmd_dieid();
+extern int cmd_dump2bin();
+extern int cmd_dump2srec();
+extern int cmd_exec();
+extern int cmd_exit();
+extern int cmd_flash();
+extern int cmd_help();
+extern int loadtool_cmd_passthru();
+
+static struct cmdtab {
+	char *cmd;
+	int minargs;
+	int maxargs;
+	int (*func)();
+} cmdtab[] = {
+	{"abbr", 2, 2, loadtool_cmd_passthru},
+	{"abbw", 3, 3, loadtool_cmd_passthru},
+	{"baud", 0, 1, cmd_baud},
+	{"crc32", 2, 2, cmd_crc32},
+	{"dieid", 0, 1, cmd_dieid},
+	{"dump", 2, 2, loadtool_cmd_passthru},
+	{"dump2bin", 3, 3, cmd_dump2bin},
+	{"dump2srec", 3, 3, cmd_dump2srec},
+	{"exec", 1, 1, cmd_exec},
+	{"exit", 0, 1, cmd_exit},
+	{"flash", 1, 5, cmd_flash},
+	{"flash2", 1, 5, cmd_flash},
+	{"help", 0, 2, cmd_help},
+	{"quit", 0, 1, cmd_exit},
+	{"r8", 1, 1, loadtool_cmd_passthru},
+	{"r16", 1, 1, loadtool_cmd_passthru},
+	{"r32", 1, 1, loadtool_cmd_passthru},
+	{"w8", 2, 2, loadtool_cmd_passthru},
+	{"w16", 2, 2, loadtool_cmd_passthru},
+	{"w32", 2, 2, loadtool_cmd_passthru},
+	{0, 0, 0, 0}
+};
+
+loadtool_dispatch_cmd(cmd, is_script)
+	char *cmd;
+{
+	char *argv[10];
+	char *cp, **ap;
+	struct cmdtab *tp;
+
+	for (cp = cmd; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#')
+		return(0);
+	if (is_script)
+		printf("Script command: %s\n", cp);
+	argv[0] = cp;
+	while (*cp && !isspace(*cp))
+		cp++;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr, "error: no such command\n");
+		return(-1);
+	}
+	for (ap = argv + 1; ; ) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp || *cp == '#')
+			break;
+		if (ap - argv - 1 >= tp->maxargs) {
+			fprintf(stderr, "error: too many arguments\n");
+			return(-1);
+		}
+		*ap++ = cp;
+		while (*cp && !isspace(*cp))
+			cp++;
+		if (*cp)
+			*cp++ = '\0';
+	}
+	if (ap - argv - 1 < tp->minargs) {
+		fprintf(stderr, "error: too few arguments\n");
+		return(-1);
+	}
+	*ap = 0;
+	return tp->func(ap - argv, argv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ltdump.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,257 @@
+/*
+ * This module implements the dump2bin and dump2srec functionality
+ * of fc-loadtool.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <time.h>
+
+extern uint32_t crc32_table[];
+extern char target_response_line[];
+
+crc32_on_target(area_base, area_len, retptr)
+	u_long area_base, area_len, *retptr;
+{
+	char arg1[10], arg2[10], *argv[4];
+	int stat;
+	char *strtoul_endp;
+
+	sprintf(arg1, "%lx", area_base);
+	sprintf(arg2, "%lx", area_len);
+	argv[0] = "crc32";
+	argv[1] = arg1;
+	argv[2] = arg2;
+	argv[3] = 0;
+	tpinterf_make_cmd(argv);
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	stat = tpinterf_capture_output_oneline(10);	/* 10 s timeout */
+	if (stat != 1) {
+errout:		fprintf(stderr, "error: malformed response to crc32 command\n");
+		return(-1);
+	}
+	if (strlen(target_response_line) != 8)
+		goto errout;
+	*retptr = strtoul(target_response_line, &strtoul_endp, 16);
+	if (strtoul_endp != target_response_line + 8)
+		goto errout;
+	return(0);
+}
+
+cmd_crc32(argc, argv)
+	char **argv;
+{
+	u_long area_base, area_len;
+	char *strtoul_endp;
+	u_long crc_result;
+	int stat;
+
+	area_base = strtoul(argv[1], &strtoul_endp, 16);
+	if (*strtoul_endp) {
+inv:		fprintf(stderr, "usage: crc32 hex-start hex-len\n");
+		return(-1);
+	}
+	area_len = strtoul(argv[2], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	stat = crc32_on_target(area_base, area_len, &crc_result);
+	if (stat == 0)
+		printf("%08lX\n", crc_result);
+	return(stat);
+}
+
+/* the actual dump facility */
+
+static FILE *dump_outfile;
+static int dump_save_srec;
+static uint32_t dump_nextaddr, dump_crcaccum;
+static uint32_t dump_total_len, dump_progress_len;
+static u_char dump_binrec[0x86];
+static time_t dump_last_time;
+
+static char dumpsrec_s0_line[] = "S007000044554D50C2\n";
+static char dumpsrec_s7_line[] = "S70500000000FA\n";
+
+static
+dump_receiver(line)
+	char *line;
+{
+	int i, b;
+	u_char sr_cksum;
+	uint32_t addr_from_srec;
+	time_t curtime;
+
+	if (strncmp(line, "S385", 4)) {
+		fprintf(stderr,
+			"error: target response is not the expected S385...\n");
+		return(-1);
+	}
+	for (i = 0; i < 0x86; i++) {
+		b = decode_hex_byte(line + i*2 + 2);
+		if (b < 0) {
+			fprintf(stderr,
+			"data from target: S-record hex decode error\n");
+			return(-1);
+		}
+		dump_binrec[i] = b;
+	}
+	sr_cksum = 0;
+	for (i = 0; i < 0x86; i++)
+		sr_cksum += dump_binrec[i];
+	if (sr_cksum != 0xFF) {
+		fprintf(stderr, "data from target: bad S-record checksum\n");
+		return(-1);
+	}
+	/* basic S-record format OK; now verify the address */
+	addr_from_srec = ((uint32_t) dump_binrec[1] << 24) |
+			 ((uint32_t) dump_binrec[2] << 16) |
+			 ((uint32_t) dump_binrec[3] << 8) |
+			  (uint32_t) dump_binrec[4];
+	if (addr_from_srec != dump_nextaddr) {
+		fprintf(stderr,
+			"error: S3 record from target has the wrong address\n");
+		return(-1);
+	}
+	/* all checks passed - save it */
+	if (dump_save_srec) {
+		if (!dump_progress_len)
+			fputs(dumpsrec_s0_line, dump_outfile);
+		fprintf(dump_outfile, "%s\n", line);
+	} else
+		fwrite(dump_binrec + 5, 1, 0x80, dump_outfile);
+	/* update running CRC */
+	for (i = 0; i < 0x80; i++)
+		dump_crcaccum = crc32_table[dump_crcaccum & 0xFF ^
+						dump_binrec[i+5]] ^
+				(dump_crcaccum >> 8);
+	/* progress indication */
+	dump_progress_len += 0x80;
+	i = dump_progress_len * 100 / dump_total_len;
+	time(&curtime);
+	if (curtime != dump_last_time || i == 100) {
+		printf("\rRx %lu out of %lu bytes (%i%%)",
+			(u_long) dump_progress_len, (u_long) dump_total_len, i);
+		fflush(stdout);
+	}
+	dump_nextaddr += 0x80;
+	dump_last_time = curtime;
+	return(1);
+}
+
+loadtool_memdump(start_addr, area_len, filename, fmt_srec)
+	u_long start_addr, area_len;
+	char *filename;
+{
+	u_long target_crc_init, target_crc_fin;
+	char *target_argv[4], target_arg1[10], target_arg2[10];
+	int stat;
+
+	if (start_addr & 0x7F || area_len & 0x7F) {
+		fprintf(stderr,
+		"error: implementation limit: 128-byte alignment required\n");
+		return(-1);
+	}
+	printf("Requesting initial CRC-32 of the area from target...\n");
+	stat = crc32_on_target(start_addr, area_len, &target_crc_init);
+	if (stat)
+		return(stat);
+	printf("got %08lX\n", target_crc_init);
+	dump_outfile = fopen(filename, "w");
+	if (!dump_outfile) {
+		perror(filename);
+		return(-1);
+	}
+	dump_save_srec = fmt_srec;
+	dump_nextaddr = start_addr;
+	dump_crcaccum = 0xFFFFFFFF;
+	dump_total_len = area_len;
+	dump_progress_len = 0;
+
+	printf("Requesting memory dump...\n");
+	sprintf(target_arg1, "%lx", start_addr);
+	sprintf(target_arg2, "%lx", area_len);
+	target_argv[0] = "DUMP";
+	target_argv[1] = target_arg1;
+	target_argv[2] = target_arg2;
+	target_argv[3] = 0;
+	tpinterf_make_cmd(target_argv);
+	stat = tpinterf_send_cmd();
+	if (stat < 0) {
+		fclose(dump_outfile);
+		return(stat);
+	}
+	stat = tpinterf_capture_output(2, dump_receiver);
+	if (stat < 0) {
+		fclose(dump_outfile);
+		return(stat);
+	}
+	putchar('\n');	/* after last progress line */
+
+	/* sanity checks */
+	if (dump_nextaddr != start_addr + area_len) {
+		fclose(dump_outfile);
+		fprintf(stderr,
+		"error: received dump length does not match expected\n");
+		return(-1);
+	}
+	if (dump_crcaccum != (uint32_t) target_crc_init) {
+		fclose(dump_outfile);
+		fprintf(stderr, "error: CRC mismatch (computed %lX)\n",
+			(u_long) dump_crcaccum);
+		return(-1);
+	}
+	if (fmt_srec)
+		fputs(dumpsrec_s7_line, dump_outfile);
+	fclose(dump_outfile);
+	printf("Requesting another CRC-32 of the area from target...\n");
+	stat = crc32_on_target(start_addr, area_len, &target_crc_fin);
+	if (stat)
+		return(stat);
+	if (target_crc_fin == target_crc_init) {
+		printf("match, dump successful\n");
+		return(0);
+	} else {
+		fprintf(stderr, "mismatch: got %lX this time\n",
+			target_crc_fin);
+		return(-1);
+	}
+}
+
+cmd_dump2bin(argc, argv)
+	char **argv;
+{
+	u_long area_base, area_len;
+	char *strtoul_endp;
+
+	area_base = strtoul(argv[1], &strtoul_endp, 16);
+	if (*strtoul_endp) {
+inv:		fprintf(stderr, "usage: dump2bin hex-start hex-len outfile\n");
+		return(-1);
+	}
+	area_len = strtoul(argv[2], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	return loadtool_memdump(area_base, area_len, argv[3], 0);
+}
+
+cmd_dump2srec(argc, argv)
+	char **argv;
+{
+	u_long area_base, area_len;
+	char *strtoul_endp;
+
+	area_base = strtoul(argv[1], &strtoul_endp, 16);
+	if (*strtoul_endp) {
+inv:		fprintf(stderr, "usage: dump2srec hex-start hex-len outfile\n");
+		return(-1);
+	}
+	area_len = strtoul(argv[2], &strtoul_endp, 16);
+	if (*strtoul_endp)
+		goto inv;
+	return loadtool_memdump(area_base, area_len, argv[3], 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ltexit.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,109 @@
+/*
+ * This module implements the loadtool exit command, along with its
+ * options for jump-reboot and Iota power-off.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+static void
+exit_bare()
+{
+	exit(0);
+}
+
+static void
+exit_gta02_cutpwr()
+{
+#ifdef GTA0x_AP_BUILD
+	set_gta_modem_power_ctrl(0);
+#endif
+	exit(0);
+}
+
+static void
+exit_iotaoff()
+{
+	static char *poweroff_argv[2] = {"poweroff", 0};
+
+	tpinterf_make_cmd(poweroff_argv);
+	tpinterf_send_cmd();
+	exit(0);
+}
+
+static void
+exit_jump0()
+{
+	static char *jump0_argv[3] = {"jump", "0", 0};
+
+	tpinterf_make_cmd(jump0_argv);
+	tpinterf_send_cmd();
+	exit(0);
+}
+
+void (*default_exit)() = exit_bare;
+
+static struct kwtab {
+	char *kw;
+	void (*func)();
+} exit_modes[] = {
+	{"bare", exit_bare},
+	{"gta02-cutpwr", exit_gta02_cutpwr},
+	{"iota-off", exit_iotaoff},
+	{"jump0", exit_jump0},
+	{0, 0}
+};
+
+cmd_exit(argc, argv)
+	char **argv;
+{
+	struct kwtab *tp;
+
+	if (argc < 2)
+		default_exit();
+	for (tp = exit_modes; tp->kw; tp++)
+		if (!strcmp(tp->kw, argv[1]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr,
+			"error: \"%s\" is not an understood exit mode\n",
+			argv[1]);
+		return(-1);
+	}
+	tp->func();
+}
+
+/* called from hwparam.c config file parser */
+void
+set_default_exit_mode(arg, filename_for_errs, lineno_for_errs)
+	char *arg;
+	char *filename_for_errs;
+	int lineno_for_errs;
+{
+	char *cp;
+	struct kwtab *tp;
+
+	while (isspace(*arg))
+		arg++;
+	if (!*arg) {
+		fprintf(stderr,
+		"%s line %d: exit-mode setting requires an argument\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	for (cp = arg; *cp && !isspace(*cp); cp++)
+		;
+	*cp = '\0';
+	for (tp = exit_modes; tp->kw; tp++)
+		if (!strcmp(tp->kw, arg))
+			break;
+	if (!tp->func) {
+		fprintf(stderr,
+			"%s line %d: \"%s\" is not an understood exit mode\n",
+			filename_for_errs, lineno_for_errs, arg);
+		exit(1);
+	}
+	default_exit = tp->func;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/lthelp.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,80 @@
+/*
+ * This module implements the loadtool help facility.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char loadtool_help_file[];
+
+loadtool_help(topic)
+	char *topic;
+{
+	FILE *f;
+	char linebuf[256];
+	char *cp, *np;
+	int flag;
+
+	f = fopen(loadtool_help_file, "r");
+	if (!f) {
+		perror(loadtool_help_file);
+		return(-1);
+	}
+	for (;;) {
+		if (!fgets(linebuf, sizeof linebuf, f)) {
+			fprintf(stderr, "Help topic \"%s\" not found\n", topic);
+			fclose(f);
+			return(-1);
+		}
+		if (linebuf[0] != '=' || linebuf[1] != '=' || linebuf[2] != '=')
+			continue;
+		for (cp = linebuf + 3; isspace(*cp); cp++)
+			;
+		for (np = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		if (!strcmp(np, topic))
+			break;
+	}
+	for (flag = 0; fgets(linebuf, sizeof linebuf, f); ) {
+		if (linebuf[0] == '=' && linebuf[1] == '=' &&
+		    linebuf[2] == '=') {
+			if (flag)
+				break;
+			else
+				continue;
+		}
+		fputs(linebuf, stdout);
+		flag = 1;
+	}
+	fclose(f);
+	return(0);
+}
+
+cmd_help(argc, argv)
+	char **argv;
+{
+	char flashtopic[32];
+
+	switch (argc) {
+	case 1:
+		return loadtool_help("main");
+	case 2:
+		return loadtool_help(argv[1]);
+	case 3:
+		if ((!strcmp(argv[1], "flash") || !strcmp(argv[1], "flash2"))
+		    && strlen(argv[2]) <= 20) {
+			sprintf(flashtopic, "flash:%s", argv[2]);
+			return loadtool_help(flashtopic);
+		}
+		fprintf(stderr, "No such help topic\n");
+		return(-1);
+	default:
+		fprintf(stderr, "internal error in cmd_help(): bad argc\n");
+		abort();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ltmain.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,98 @@
+/*
+ * This module contains the main() function for fc-loadtool
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "srecreader.h"
+
+extern char *target_ttydev;
+extern struct srecreader iramimage;
+extern char default_loadagent_image[];
+extern char hw_init_script[];
+extern void (*default_exit)();
+extern int gta_modem_poweron;
+
+extern struct baudrate *find_baudrate_by_name();
+
+static struct baudrate *reattach;
+
+main(argc, argv)
+	char **argv;
+{
+	extern char *optarg;
+	extern int optind;
+	int c;
+	char command[512];
+
+	while ((c = getopt(argc, argv, "a:b:c:C:h:H:i:nr:")) != EOF)
+		switch (c) {
+		case 'a':
+			iramimage.filename = optarg;
+			continue;
+		case 'b':
+			set_romload_baudrate(optarg);
+			continue;
+		case 'c':
+			set_compalstage_short(optarg);
+			continue;
+		case 'C':
+			set_compalstage_fullpath(optarg);
+			continue;
+		case 'h':
+			read_hwparam_file_shortname(optarg);
+			continue;
+		case 'H':
+			read_hwparam_file_fullpath(optarg);
+			continue;
+		case 'i':
+			set_beacon_interval(optarg);
+			continue;
+		case 'n':
+			gta_modem_poweron = 0;
+			continue;
+		case 'r':
+			reattach = find_baudrate_by_name(optarg);
+			if (!reattach)
+				exit(1);	/* error msg already printed */
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: fc-loadtool [options] ttyport\n");
+			exit(1);
+		}
+	if (argc - optind != 1)
+		goto usage;
+	target_ttydev = argv[optind];
+	if (!iramimage.filename)
+		iramimage.filename = default_loadagent_image;
+
+	open_target_serial();
+	if (reattach)
+		switch_baud_rate(reattach);
+	else {
+		perform_compal_stage(1);
+		perform_romload();
+		putchar('\n');
+		if (tpinterf_pass_output(1) < 0)
+			exit(1);
+		putchar('\n');
+		if (hw_init_script[0]) {
+			printf("Executing init script %s\n", hw_init_script);
+			loadtool_exec_script(hw_init_script);
+		}
+	}
+	for (;;) {
+		if (isatty(0)) {
+			fputs("loadtool> ", stdout);
+			fflush(stdout);
+		}
+		if (!fgets(command, sizeof command, stdin))
+			default_exit();
+		loadtool_dispatch_cmd(command, 0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ltmisc.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,37 @@
+/*
+ * This module is a place to implement little miscellaneous fc-loadtool
+ * commands which don't belong anywhere else.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+
+cmd_dieid(argc, argv)
+	char **argv;
+{
+	static uint32_t addrs[4] = {0xFFFEF010, 0xFFFEF012, 0xFFFEF014,
+				    0xFFFEF016};
+	uint16_t data[4];
+	int i, stat;
+	FILE *of;
+
+	for (i = 0; i < 4; i++) {
+		stat = do_r16(addrs[i], data + i);
+		if (stat)
+			return(stat);
+		printf("%08lX: %04X\n", (u_long)addrs[i], (int)data[i]);
+	}
+	if (argc < 2)
+		return(0);
+	of = fopen(argv[1], "w");
+	if (!of) {
+		perror(argv[1]);
+		return(-1);
+	}
+	for (i = 0; i < 4; i++)
+		fprintf(of, "%08lX: %04X\n", (u_long)addrs[i], (int)data[i]);
+	fclose(of);
+	printf("Saved to %s\n", argv[1]);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ltpassthru.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,19 @@
+/*
+ * This module contains the loadtool_cmd_passthru() function,
+ * which implements the simplest commands that pass directly
+ * through to loadagent.
+ */
+
+#include <stdio.h>
+
+loadtool_cmd_passthru(argc, argv)
+	char **argv;
+{
+	if (tpinterf_make_cmd(argv) < 0) {
+		fprintf(stderr, "error: unable to form target command\n");
+		return(-1);
+	}
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	return tpinterf_pass_output(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ltscript.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,54 @@
+/*
+ * This module contains the code that implements the loadtool scripting
+ * functionality: init-script setting and the exec command.
+ */
+
+#include <sys/param.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char default_helpers_dir[];
+
+loadtool_exec_script(script_name)
+	char *script_name;
+{
+	char pathbuf[MAXPATHLEN], *openfname;
+	FILE *f;
+	char linebuf[512], *cp;
+	int lineno, retval = 0;
+
+	if (index(script_name, '/'))
+		openfname = script_name;
+	else {
+		sprintf(pathbuf, "%s/%s", default_helpers_dir, script_name);
+		openfname = pathbuf;
+	}
+	f = fopen(openfname, "r");
+	if (!f) {
+		perror(openfname);
+		return(-1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) {
+		cp = index(linebuf, '\n');
+		if (!cp) {
+			fprintf(stderr, "%s line %d: missing newline\n",
+				openfname, lineno);
+			fclose(f);
+			return(-1);
+		}
+		*cp = '\0';
+		retval = loadtool_dispatch_cmd(linebuf, 1);
+		if (retval)
+			break;
+	}
+	fclose(f);
+	return(retval);
+}
+
+cmd_exec(argc, argv)
+	char **argv;
+{
+	return loadtool_exec_script(argv[1]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/romload.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,319 @@
+/*
+ * This module implements the communication protocol for pushing our
+ * IRAM-loadable code to the Calypso ROM bootloader.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <termios.h>
+#include <unistd.h>
+#include "baudrate.h"
+#include "srecreader.h"
+
+extern int errno;
+
+extern char *target_ttydev;
+extern int target_fd;
+extern struct baudrate baud_rate_table[];
+extern struct baudrate *find_baudrate_by_name();
+
+struct srecreader iramimage;
+struct baudrate *romload_baud_rate = baud_rate_table;	/* 1st entry default */
+
+/* global var always defined, but does anything only for GTA0x_AP_BUILD */
+int gta_modem_poweron = 1;
+
+static int beacon_interval = 13;	/* in milliseconds */
+
+static u_char beacon_cmd[2] = {'<', 'i'};
+
+static u_char param_cmd[11] = {'<', 'p',
+			0x00,	/* baud rate select code (115200) */
+			0x00,	/* DPLL setup: leave it off like on power-up, */
+				/* OsmocomBB does the same thing */
+			0x00, 0x04,	/* chip select timing (WS) settings */
+					/* our setting matches both OsmocomBB */
+					/* and what the ROM runs with */
+					/* before receiving this command */
+			0x22,	/* FFFF:F900 register config, low byte */
+				/* OsmocomBB sends 0x00 here, but I've chosen */
+				/* 0x22 to match the setting of this register */
+				/* used by the boot ROM before this command. */
+			0x00, 0x01, 0xD4, 0xC0	/* UART timeout */
+				/* I've chosen the same value as what the */
+				/* boot ROM runs with before getting this cmd */
+};
+
+static u_char write_cmd[10] = {'<', 'w', 0x01, 0x01, 0x00};
+static u_char cksum_cmd[3]  = {'<', 'c'};
+static u_char branch_cmd[6] = {'<', 'b'};
+
+#define	INTERMEDIATE_TIMEOUT	500	/* ms to wait for responses */
+#define	SERIAL_FLUSH_DELAY	200	/* also in ms */
+
+/*
+ * The following function should be called by command line option
+ * parsers upon encountering the -i option.
+ */
+set_beacon_interval(arg)
+	char *arg;
+{
+	int i;
+
+	i = atoi(arg);
+	if (i < 2 || i > 500) {
+		fprintf(stderr, "invalid -i argument specified\n");
+		exit(1);
+	}
+	beacon_interval = i;
+}
+
+/*
+ * The following function should be called by command line option
+ * parsers upon encountering the -b option.
+ */
+set_romload_baudrate(arg)
+	char *arg;
+{
+	struct baudrate *br;
+
+	br = find_baudrate_by_name(arg);
+	if (!br)
+		exit(1);	/* error msg already printed */
+	if (br->bootrom_code < 0) {
+		fprintf(stderr,
+		"baud rate of %s is not supported by the Calypso boot ROM\n",
+			br->name);
+		exit(1);
+	}
+	romload_baud_rate = br;
+}
+
+/*
+ * The following functions alter some of the parameters sent to the
+ * boot ROM in the <p command.
+ */
+set_romload_pll_conf(byte)
+{
+	param_cmd[3] = byte;
+}
+
+set_romload_rhea_cntl(byte)
+{
+	param_cmd[6] = byte;
+}
+
+static int
+expect_response(timeout)
+{
+	char buf[2];
+	fd_set fds;
+	struct timeval tv;
+	int pass, cc;
+
+	for (pass = 0; pass < 2; ) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		tv.tv_sec = 0;
+		tv.tv_usec = timeout * 1000;
+		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
+		if (cc < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		if (cc < 1)
+			return(-1);
+		cc = read(target_fd, buf + pass, 2 - pass);
+		if (cc <= 0) {
+			perror("read after successful select");
+			exit(1);
+		}
+		if (pass == 0 && buf[0] != '>')
+			continue;
+		pass += cc;
+	}
+	return(buf[1]);
+}
+
+static
+send_beacons()
+{
+	printf("Sending beacons to %s\n", target_ttydev);
+#ifdef GTA0x_AP_BUILD
+	if (gta_modem_poweron)
+		fork_gta_modem_poweron();
+#endif
+	do
+		write(target_fd, beacon_cmd, sizeof beacon_cmd);
+	while (expect_response(beacon_interval) != 'i');
+	return 0;
+}
+
+static uint32_t
+compute_block_cksum()
+{
+	uint32_t sum;
+	int i, llen;
+
+	sum = iramimage.datalen + 5;
+	llen = iramimage.datalen + 4;
+	for (i = 0; i < llen; i++)
+		sum += iramimage.record[i+1];
+	return sum;
+}
+
+perform_romload()
+{
+	int resp;
+	uint16_t image_cksum;
+	unsigned long rec_count;
+	static int zero = 0;
+
+	if (open_srec_file(&iramimage) < 0)
+		exit(1);
+	ioctl(target_fd, FIONBIO, &zero);
+	send_beacons();
+	printf("Got beacon response, attempting download\n");
+
+	usleep(SERIAL_FLUSH_DELAY * 1000);
+	tcflush(target_fd, TCIFLUSH);
+	param_cmd[2] = romload_baud_rate->bootrom_code;
+	write(target_fd, param_cmd, sizeof param_cmd);
+	resp = expect_response(INTERMEDIATE_TIMEOUT);
+	if (resp != 'p') {
+		if (resp < 0)
+			fprintf(stderr, "No response to <p command\n");
+		else if (isprint(resp))
+			fprintf(stderr,
+			"Got >%c in response to <p command; expected >p\n",
+				resp);
+		else
+			fprintf(stderr,
+			"Got > %02X in response to <p command; expected >p\n",
+				resp);
+		exit(1);
+	}
+	printf("<p command successful, switching to %s baud\n",
+		romload_baud_rate->name);
+	switch_baud_rate(romload_baud_rate);
+	usleep(SERIAL_FLUSH_DELAY * 1000);
+	tcflush(target_fd, TCIFLUSH);
+
+	image_cksum = 0;
+	for (rec_count = 0; ; ) {
+		if (read_s_record(&iramimage) < 0)
+			exit(1);
+		switch (iramimage.record_type) {
+		case '0':
+			if (iramimage.lineno == 1)
+				continue;
+			fprintf(stderr,
+		"%s: S0 record found in line %d (expected in line 1 only)\n",
+				iramimage.filename, iramimage.lineno);
+			exit(1);
+		case '3':
+		case '7':
+			if (s3s7_get_addr_data(&iramimage) < 0)
+				exit(1);
+			break;
+		default:
+			fprintf(stderr,
+				"%s line %d: S%c record type not supported\n",
+				iramimage.filename, iramimage.lineno,
+				iramimage.record_type);
+			exit(1);
+		}
+		if (iramimage.record_type == '7')
+			break;
+		/* must be S3 */
+		if (iramimage.datalen < 1) {
+			fprintf(stderr,
+				"%s line %d: S3 record has zero data length\n",
+				iramimage.filename, iramimage.lineno);
+			exit(1);
+		}
+		/* form <w command */
+		if (!rec_count)
+			printf("Sending image payload\n");
+		write_cmd[5] = iramimage.datalen;
+		bcopy(iramimage.record + 1, write_cmd + 6, 4);
+		write(target_fd, write_cmd, sizeof write_cmd);
+		write(target_fd, iramimage.record + 5, iramimage.datalen);
+		/* update our checksum accumulator */
+		image_cksum += ~compute_block_cksum() & 0xFF;
+		/* collect response */
+		resp = expect_response(INTERMEDIATE_TIMEOUT);
+		if (resp != 'w') {
+			fprintf(stderr, "Block #%lu: ", rec_count);
+			if (resp < 0)
+				fprintf(stderr, "No response to <w command\n");
+			else if (isprint(resp))
+				fprintf(stderr,
+			"Got >%c in response to <w command; expected >w\n",
+					resp);
+			else
+				fprintf(stderr,
+			"Got > %02X in response to <w command; expected >w\n",
+					resp);
+			exit(1);
+		}
+		putchar('.');
+		fflush(stdout);
+		rec_count++;
+	}
+	/* got S7 */
+	fclose(iramimage.openfile);
+	if (!rec_count) {
+		fprintf(stderr,
+		"%s line %d: S7 without any preceding S3 data records\n",
+			iramimage.filename, iramimage.lineno);
+		exit(1);
+	}
+
+	/* send <c */
+	printf("Sending checksum\n");
+	cksum_cmd[2] = ~image_cksum & 0xFF;
+	write(target_fd, cksum_cmd, sizeof cksum_cmd);
+	resp = expect_response(INTERMEDIATE_TIMEOUT);
+	if (resp != 'c') {
+		if (resp < 0)
+			fprintf(stderr, "No response to <c command\n");
+		else if (isprint(resp))
+			fprintf(stderr,
+			"Got >%c in response to <c command; expected >c\n",
+				resp);
+		else
+			fprintf(stderr,
+			"Got > %02X in response to <c command; expected >c\n",
+				resp);
+		exit(1);
+	}
+	printf("<c command successful, sending <b\n");
+
+	bcopy(iramimage.record + 1, branch_cmd + 2, 4);
+	write(target_fd, branch_cmd, sizeof branch_cmd);
+	resp = expect_response(INTERMEDIATE_TIMEOUT);
+	if (resp != 'b') {
+		if (resp < 0)
+			fprintf(stderr, "No response to <b command\n");
+		else if (isprint(resp))
+			fprintf(stderr,
+			"Got >%c in response to <b command; expected >b\n",
+				resp);
+		else
+			fprintf(stderr,
+			"Got > %02X in response to <b command; expected >b\n",
+				resp);
+		exit(1);
+	}
+	printf("<b command successful: downloaded image should now be running!\n");
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/c155.config	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,23 @@
+# This configuration is for Motorola C155/156 phones.  Use the "generic"
+# compal configuration for C11x/123 and C139/140.
+
+compal-stage thumb
+
+# Parameters for the Calypso boot ROM;
+# will result in the ARM core being clocked at 52 MHz as we want.
+
+pll-config 4/1
+rhea-cntl 0x00
+
+# The remaining settings are carried out via loadagent commands
+init-script c155.init
+
+# Flash: use CFI autodetection, 8 MiB max
+# Unlike C139, C155 and C156 phones do have working flash mapping at 0x03000000
+flash cfi-8M 0x03000000
+
+# bottom boot flash, sector 0 is only 8 KiB
+boot-reflash-hack 0x820000 0x2000
+
+# Perform a Iota poweroff when we are done
+exit-mode iota-off
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/c155.init	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,15 @@
+# Set WS=3 for both nCS0 and nCS1, same as the more basic Compal phones
+
+w16 fffffb00 00A3
+w16 fffffb02 00A3
+
+# We need to switch the CS4/ADD22 pin from its default function of CS4
+# to the needed ADD22, to access the 8 MiB of flash.  Compal's C155/156
+# in-flash boot code does this setting, but let's do it ourselves too.
+
+w16 fffef006 0008
+
+# We don't mess with the FFFF:FB10 register on C155/156, i.e., we
+# leave the "enable boot ROM" setting established by compalstage.
+# Unlike C139, C155 and C156 phones do have working flash mapping
+# at 0x03000000, so we use that.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/compal.config	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,36 @@
+# This configuration is intended to be applicable to all of C11x, C123,
+# C139 and C140.  The "plain" version of compalstage selected below
+# should work for all C11x/123; it will also work on C139/140 phones
+# that had the simpler boot code flashed into them, as will be used for
+# FreeCalypso.  When running loadtools with this config on C139/140
+# phones that still have the "official" fw in them, one will need to
+# specify -h compal -c 1003 to use the inefficient ~15 KiB version of
+# compalstage.
+
+compal-stage plain
+
+# Whether we are breaking in through compalstage (as above) or through
+# tfc139, the re-enabled Calypso boot ROM is used to load our loadagent
+# into IRAM.  The boot ROM will autodetect the Calypso input clock as
+# 26 MHz (physical reality) when entered through compalstage, or as
+# 13 MHz when entered through tfc139 - the latter results from the
+# original fw setting bit 7 in the FFFF:FD02 register (VTCXO_DIV2),
+# which the boot ROM does not clear.
+#
+# However, the following configuration will result in the ARM core
+# being clocked at 52 MHz in both cases.
+
+pll-config 4/1
+rhea-cntl 0x00
+
+# The remaining settings are carried out via loadagent commands
+init-script compal.init
+
+# Flash: use CFI autodetection, 4 MiB max
+# mapped at 0, see compal.init for the explanation
+flash cfi-4M 0
+
+boot-reflash-hack 0x820000 0x10000
+
+# Perform a Iota poweroff when we are done
+exit-mode iota-off
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/compal.init	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,16 @@
+# Set WS=3 for both nCS0 and nCS1.  This configuration is used by OsmocomBB
+# for all 3 Compal models (E86/88/99), and is also seen in the IDA disassembly
+# listing of c115-1.0.46.E firmware contributed by Christophe Devine.
+
+w16 fffffb00 00A3
+w16 fffffb02 00A3
+
+# We need to set the FFFF:FB10 register to map the flash (not the boot ROM)
+# to address 0.  We need this mapping in order to be able to dump and program
+# the entire flash, as for some reason the alternate nCS0 mapping at 0x03000000
+# does not work on Compal phones.  (That alternate mapping works fine on
+# Openmoko and Pirelli phones, though.  Perhaps the different Calypso chip
+# version is the culprit, or perhaps this alternate mapping works only if the
+# physical nIBOOT pin is low.)
+
+w16 fffffb10 0300
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/cs2-4ws-8mb.init	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+# This loadtool init script provides memory interface register setup
+# for targets which fit the following criteria:
+#
+# 3 chip selects are used: nCS0, nCS1 and nCS2
+# 4 wait states are to be used (register setting 00A4)
+# 8 MiB memory banks are in use, such that ADD22 needs to be enabled
+
+w16 fffffb00 00A4
+w16 fffffb02 00A4
+w16 fffffb04 00A4
+w16 fffef006 0008
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/dsample.config	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,22 @@
+# The following parameters go into the <p command sent to the boot ROM
+# The values to be used have been gleaned from the 20020917 fw image
+
+# CLKTCXO input is 13 MHz on the D-Sample, and with Calypso C05
+# the max allowed PLL'ed clock is 78 MHz for the DSP and 39 MHz for the ARM.
+# TI's firmware sets the PLL up to multiply by 6 (giving 78 MHz) with
+# divide by 2 for the ARM, but the boot ROM doesn't do the latter when
+# the input clock is 13 MHz.  Hence we'll program the PLL to multiply
+# by 3, putting everything at 39 MHz.
+
+pll-config 3/1
+rhea-cntl 0x00		# set by 20020917 fw, hence presumed correct
+
+# The remaining settings are carried out via loadagent commands
+init-script cs2-4ws-8mb.init
+
+# 8 MiB flash, accessible at 0x03000000 without Compal-like problems,
+# let's use CFI.
+flash cfi-8M 0x03000000
+
+# Perform a Iota poweroff when we are done
+exit-mode iota-off
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/fcfam.config	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,22 @@
+# This loadtool target configuration is intended to be applicable to all
+# original hardware designs created within the FreeCalypso project,
+# starting with FCDEV3B.  Specifically, it should fit all targets that
+# satisfy the following criteria:
+#
+# The DBB chip is Calypso C035
+# The RF block is Rita (26 MHz VCXO)
+# Spansion S71PL129NC0 used for flash and XRAM, copied from Pirelli DP-L10
+# The 2nd flash chip select is wired to nCS2
+
+# The following parameters go into the <p command sent to the boot ROM
+pll-config 4/1		# 26 MHz in, PLL&DSP @ 104 MHz, ARM @ 52 MHz
+rhea-cntl 0x00		# good for all Calypso platforms
+
+# The remaining settings are carried out via loadagent commands
+init-script cs2-4ws-8mb.init
+
+# Flash type and chip select base addresses
+flash pl129n 0x03000000 0x01800000
+
+# Perform a Iota poweroff when we are done
+exit-mode iota-off
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/gta02.config	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+# If one is running fc-loadtool from inside the GTA02 (i.e., from the AP,
+# as opposed to an external host connected via the headset jack), the
+# following setting will cause the fc-loadtool utility to power the modem off
+# fully upon exit.
+
+exit-mode gta02-cutpwr
+
+# The following parameters go into the <p command sent to the boot ROM
+# same values as in pirelli.config, apparently correct for this modem too
+
+pll-config 4/1		# 26 MHz in, PLL&DSP @ 104 MHz, ARM @ 52 MHz
+rhea-cntl 0x00		# fastest setting, used by OsmocomBB, presumably correct
+
+# Configure memory timings with loadagent commands
+init-script k5a3281.init
+
+# Flash type and chip select base address (full access mapping)
+flash k5a32xx_t 0x03000000
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/k5a3281.init	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,12 @@
+# The RAM+flash MCP in the GTA02 GSM modem block is SEC K5A3281CTM.
+# The closest datasheet that could be found is for K5A3280;
+# all current understanding of this IC is based on the latter datasheet.
+
+# OsmocomBB sets WS=3 for both nCS0 and nCS1.  At first I was concerned that
+# the setting was wrong for nCS0, but I have now located the responsible
+# code in the moko11 binary (see the moko11 notes file in the
+# freecalypso-reveng Hg tree) and confirmed that the official firmware
+# runs with the same settings.  So let's do likewise.
+
+w16 fffffb00 00A3
+w16 fffffb02 00A3
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/pirelli.config	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+# The following parameters go into the <p command sent to the boot ROM
+
+pll-config 4/1		# 26 MHz in, PLL&DSP @ 104 MHz, ARM @ 52 MHz
+rhea-cntl 0x00		# fastest setting, used by OsmocomBB, presumably correct
+
+# The remaining settings are carried out via loadagent commands
+init-script pirelli.init
+
+# Flash type and chip select base addresses
+flash pl129n 0x03000000 0x02000000
+
+# On this phone the current best exit strategy is jump0.
+# A Iota power-off, even if we implement one, won't be any different:
+# having the serial connection implies a USB connection, that in turn
+# implies having VBUS, and thus a Iota power-off will immediately
+# result in another power-on for charger-insert.
+
+exit-mode jump0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/scripts/pirelli.init	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,27 @@
+# This phone has 3 memory chip selects:
+#
+# nCS0: flash chip select 1
+# nCS1: RAM chip select
+# nCS3: flash chip select 2
+#
+# All 3 chip select lines go to the same physical IC, a RAM/flash MCP.
+# We set WS=4 for all 3 here, copying what OsmocomBB does.  The access
+# time listed in the datasheet is 70 ns for both RAM and flash, and per
+# my math setting WS=3 *might* work, but it could be marginal, so let's
+# play it safe for now.
+
+w16 fffffb00 00A4
+w16 fffffb02 00A4
+w16 fffffb06 00A4
+
+# We also need to switch the CS4/ADD22 pin from its default function
+# of CS4 to the needed ADD22.
+
+w16 fffef006 0008
+
+# With this phone all Calypso serial access always goes through the
+# CP2102 usb2serial IC inside the phone itself, which is programmed
+# to support the high non-standard baud rates.  So we can safely
+# switch to 812500 baud unconditionally.
+
+baud 812500
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/sercomm.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,87 @@
+/*
+ * This module handles the establishment of serial communication
+ * with the target, i.e., the host-side termios stuff.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "baudrate.h"
+
+char *target_ttydev;
+int target_fd;
+struct termios target_termios;
+
+struct baudrate baud_rate_table[] = {
+	/* the first listed rate will be our default */
+	{"115200",	B115200,	0},
+	{"57600",	B57600,		1},
+	{"38400",	B38400,		2},
+	{"19200",	B19200,		4},
+	/* non-standard high baud rates "remapped" by CP2102 usb2serial IC */
+	{"812500",	B921600,	-1},
+	{"406250",	B460800,	-1},
+	{"203125",	B230400,	-1},
+	/* table search terminator */
+	{NULL,		B0,		-1},
+};
+struct baudrate *current_baud_rate;
+
+open_target_serial()
+{
+	target_fd = open(target_ttydev, O_RDWR|O_NONBLOCK);
+	if (target_fd < 0) {
+		perror(target_ttydev);
+		exit(1);
+	}
+	target_termios.c_iflag = IGNBRK;
+	target_termios.c_oflag = 0;
+	target_termios.c_cflag = CLOCAL|HUPCL|CREAD|CS8;
+	target_termios.c_lflag = 0;
+	target_termios.c_cc[VMIN] = 1;
+	target_termios.c_cc[VTIME] = 0;
+	/* start at B19200, as that's what we'll need to use initially */
+	cfsetispeed(&target_termios, B19200);
+	cfsetospeed(&target_termios, B19200);
+	if (tcsetattr(target_fd, TCSAFLUSH, &target_termios) < 0) {
+		perror("initial tcsetattr on target");
+		exit(1);
+	}
+	return 0;
+}
+
+struct baudrate *
+find_baudrate_by_name(srch_name)
+	char *srch_name;
+{
+	struct baudrate *br;
+
+	for (br = baud_rate_table; br->name; br++)
+		if (!strcmp(br->name, srch_name))
+			break;
+	if (br->name)
+		return(br);
+	else {
+		fprintf(stderr, "error: baud rate \"%s\" not known\n",
+			srch_name);
+		return(NULL);
+	}
+}
+
+switch_baud_rate(br)
+	struct baudrate *br;
+{
+	cfsetispeed(&target_termios, br->termios_code);
+	cfsetospeed(&target_termios, br->termios_code);
+	if (tcsetattr(target_fd, TCSAFLUSH, &target_termios) < 0) {
+		perror("tcsetattr to switch baud rate");
+		exit(1);
+	}
+	current_baud_rate = br;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/sertool.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,65 @@
+/*
+ * This module contains the main() function for fc-iram, previously
+ * called fc-sertool: the simplest of the FreeCalypso loading tools,
+ * which sends the user-specified IRAM SREC image to the boot ROM
+ * and then switches into serial tty pass-through.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "srecreader.h"
+
+extern char *target_ttydev;
+extern struct srecreader iramimage;
+extern int gta_modem_poweron;
+
+main(argc, argv)
+	char **argv;
+{
+	extern char *optarg;
+	extern int optind;
+	int c;
+
+	while ((c = getopt(argc, argv, "b:c:C:h:H:i:n")) != EOF)
+		switch (c) {
+		case 'b':
+			set_romload_baudrate(optarg);
+			continue;
+		case 'c':
+			set_compalstage_short(optarg);
+			continue;
+		case 'C':
+			set_compalstage_fullpath(optarg);
+			continue;
+		case 'h':
+			read_hwparam_file_shortname(optarg);
+			continue;
+		case 'H':
+			read_hwparam_file_fullpath(optarg);
+			continue;
+		case 'i':
+			set_beacon_interval(optarg);
+			continue;
+		case 'n':
+			gta_modem_poweron = 0;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+			"usage: fc-iram [options] ttyport iramimage.srec\n");
+			exit(1);
+		}
+	if (argc - optind != 2)
+		goto usage;
+	target_ttydev = argv[optind];
+	iramimage.filename = argv[optind+1];
+
+	open_target_serial();
+	perform_compal_stage(1);
+	perform_romload();
+	tty_passthru();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/srecreader.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,106 @@
+/*
+ * This module contains the functions for reading S-record files.
+ */
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <strings.h>
+#include "srecreader.h"
+
+open_srec_file(sr)
+	struct srecreader *sr;
+{
+	sr->openfile = fopen(sr->filename, "r");
+	if (!sr->openfile) {
+		perror(sr->filename);
+		return(-1);
+	}
+	sr->lineno = 0;
+	return(0);
+}
+
+static
+srec2bin(sr, asciiline)
+	struct srecreader *sr;
+	char *asciiline;
+{
+	register int i, l, b;
+
+	l = decode_hex_byte(asciiline + 2);
+	if (l < 1) {
+		fprintf(stderr, "%s line %d: S-record length octet is bad\n",
+			sr->filename, sr->lineno);
+		return(-1);
+	}
+	sr->record[0] = l;
+	for (i = 1; i <= l; i++) {
+		b = decode_hex_byte(asciiline + i*2 + 2);
+		if (b < 0) {
+			fprintf(stderr,
+				"%s line %d: S-record hex decode error\n",
+				sr->filename, sr->lineno);
+			return(-1);
+		}
+		sr->record[i] = b;
+	}
+	return(0);
+}
+
+static
+srec_cksum(sr)
+	struct srecreader *sr;
+{
+	u_char accum;
+	register int i, len;
+
+	len = sr->record[0] + 1;
+	accum = 0;
+	for (i = 0; i < len; i++)
+		accum += sr->record[i];
+	if (accum != 0xFF) {
+		fprintf(stderr, "%s line %d: bad S-record checksum\n",
+			sr->filename, sr->lineno);
+		return(-1);
+	}
+	return(0);
+}
+
+read_s_record(sr)
+	struct srecreader *sr;
+{
+	char asciiline[1024];
+
+	if (!fgets(asciiline, sizeof asciiline, sr->openfile)) {
+		fprintf(stderr, "%s: premature EOF after %d S-records\n",
+			sr->filename, sr->lineno);
+		return(-1);
+	}
+	sr->lineno++;
+	if (asciiline[0] != 'S' || !isdigit(asciiline[1])) {
+		fprintf(stderr, "%s line %d: S-record expected\n",
+			sr->filename, sr->lineno);
+		return(-1);
+	}
+	sr->record_type = asciiline[1];
+	if (srec2bin(sr, asciiline) < 0)
+		return(-1);
+	return srec_cksum(sr);
+}
+
+s3s7_get_addr_data(sr)
+	struct srecreader *sr;
+{
+	if (sr->record[0] < 5) {
+		fprintf(stderr, "%s line %d: S%c record is too short\n",
+			sr->filename, sr->lineno, sr->record_type);
+		return(-1);
+	}
+	sr->datalen = sr->record[0] - 5;
+	sr->addr = ((uint32_t)sr->record[1] << 24) |
+		   ((uint32_t)sr->record[2] << 16) |
+		   ((uint32_t)sr->record[3] << 8) |
+		    (uint32_t)sr->record[4];
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/srecreader.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+/* this header file defines the data structures for the SREC reader module */
+
+struct srecreader {
+	char		*filename;
+	FILE		*openfile;
+	int		lineno;
+	u_char		record[256];	/* binary */
+	char		record_type;	/* ASCII char */
+	u_char		datalen;
+	uint32_t	addr;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/tpinterf.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,178 @@
+/*
+ * Target program interface - this module provides some primitives
+ * for communicating programmatically with loadagent and possibly
+ * other target-utils.  This module will be linked by both
+ * fc-loadtool and fc-chainload.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern int errno;
+
+extern int target_fd;
+
+/* definition matches ../target-utils/libcommon/cmdentry.c */
+#define	MAXCMD	527
+
+/*
+ * static buffer between tpinterf_make_cmd and tpinterf_send_cmd
+ *
+ * We store the command with an ending \r\n so we can use it for
+ * matching the received echo as well, hence the sizing of the
+ * buffer.
+ */
+static char cmdbuf[MAXCMD+2];
+static int cmdlen;
+
+static int
+arg_chars_valid(arg)
+	char *arg;
+{
+	char *cp;
+
+	for (cp = arg; *cp; cp++)
+		if (*cp < ' ' || *cp > '~')
+			return(0);
+	return(1);
+}
+
+/*
+ * This function takes a command for the target in argv form and
+ * converts it to a space-separated continuous string which can be
+ * passed as tty "keyboard" input to the target, enforcing length
+ * and character validity limits in the process.  The output is
+ * stored in an internal static buffer for subsequent
+ * tpinterf_send_cmd().
+ *
+ * Return value: 0 if everything OK, or -1 if some constraint is
+ * violated.
+ */
+tpinterf_make_cmd(argv)
+	char **argv;
+{
+	int arglen;
+	char **ap, *dp;
+
+	dp = cmdbuf;
+	cmdlen = 0;
+	for (ap = argv; *ap; ap++) {
+		arglen = strlen(*ap);
+		if (ap != argv)
+			arglen++;	/* separating space */
+		if (arglen > MAXCMD - cmdlen)
+			return(-1);
+		if (!arg_chars_valid(*ap))
+			return(-1);
+		if (ap != argv)
+			*dp++ = ' ';
+		strcpy(dp, *ap);
+		dp += strlen(*ap);
+		cmdlen += arglen;
+	}
+	*dp++ = '\r';
+	*dp = '\n';
+	return(0);
+}
+
+/*
+ * This function sends the previously-constructed command to the target,
+ * and collects the expected echo.
+ *
+ * Return value: 0 if successful, -1 on errors (timeout or wrong response)
+ */
+tpinterf_send_cmd()
+{
+	char echobuf[MAXCMD+2];
+	fd_set fds;
+	struct timeval tv;
+	int rcvd, cc;
+
+	write(target_fd, cmdbuf, cmdlen + 1);
+	for (rcvd = 0; rcvd < cmdlen + 2; ) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		tv.tv_sec = 1;
+		tv.tv_usec = 0;
+		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
+		if (cc < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			return(-1);
+		}
+		if (cc < 1) {
+			fprintf(stderr,
+				"error: timeout waiting for command echo\n");
+			return(-1);
+		}
+		cc = read(target_fd, echobuf + rcvd, cmdlen + 2 - rcvd);
+		if (cc <= 0) {
+			perror("read after successful select");
+			return(-1);
+		}
+		rcvd += cc;
+	}
+	if (bcmp(echobuf, cmdbuf, cmdlen + 2)) {
+		fprintf(stderr, "error: command echo mismatch\n");
+		return(-1);
+	} else
+		return(0);
+}
+
+/*
+ * This functions reads the serial output from the target until a
+ * '=' prompt is received.  All intermediate output is passed to
+ * stdout.
+ *
+ * Return value: 0 if '=' prompt received immediately,
+ * positive if some scribble came before the prompt, -1 on errors
+ * (timeout, read errors, etc).
+ */
+tpinterf_pass_output(timeout)
+{
+	char buf[512], *cp;
+	fd_set fds;
+	struct timeval tv;
+	int cc, newline = 1, totout = 0;
+
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		tv.tv_sec = timeout;
+		tv.tv_usec = 0;
+		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
+		if (cc < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			return(-1);
+		}
+		if (cc < 1) {
+			fprintf(stderr,
+		"error: timeout waiting for \'=\' prompt from target\n");
+			return(-1);
+		}
+		cc = read(target_fd, buf, sizeof buf);
+		if (cc <= 0) {
+			perror("read after successful select");
+			return(-1);
+		}
+		for (cp = buf; cc; cp++) {
+			cc--;
+			if (*cp == '=' && newline && !cc)
+				return(totout);
+			putchar(*cp);
+			totout++;
+			if (*cp == '\n')
+				newline = 1;
+			else
+				newline = 0;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/tpinterf2.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,113 @@
+/*
+ * This module provides a more advanced target interface function
+ * than tpinterf.c - programmatic capture of target responses,
+ * for dumps etc.  It will be linked by fc-loadtool, but not fc-chainload.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern int errno;
+
+extern int target_fd;
+
+/*
+ * This functions reads the serial output from the target until a
+ * '=' prompt is received.  All intermediate output is parsed into
+ * lines and passed to a callback function.
+ *
+ * The callback function is called with a pointer to each received
+ * line, stored in a buffer ending in NUL, with CRLF stripped.
+ * The callback function is expected to return an int.  If the
+ * callback return value is negative, this function returns immediately
+ * with that negative value.  If the callback return value is positive
+ * or zero, it is added to an accumulator.
+ *
+ * Termination: this function returns when it has received a '=' at
+ * the beginning of a line (return value is the callback return
+ * accumulator, or 0 if no lines came), if the callback returns a
+ * negative value (that value is returned), or if an error is detected
+ * within this function (return value -1, and an error message
+ * printed on stderr).
+ */
+tpinterf_capture_output(timeout, callback)
+	int timeout;	/* seconds */
+	int (*callback)();
+{
+	char buf[512], *cp;
+	char line[1024], *dp = line;
+	fd_set fds;
+	struct timeval tv;
+	int cc, linelen = 0;
+	int totout = 0, cbret;
+
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		tv.tv_sec = timeout;
+		tv.tv_usec = 0;
+		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
+		if (cc < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			return(-1);
+		}
+		if (cc < 1) {
+			fprintf(stderr,
+		"error: timeout waiting for \'=\' prompt from target\n");
+			return(-1);
+		}
+		cc = read(target_fd, buf, sizeof buf);
+		if (cc <= 0) {
+			perror("read after successful select");
+			return(-1);
+		}
+		for (cp = buf; cc; cp++) {
+			cc--;
+			if (*cp == '=' && !linelen && !cc)
+				return(totout);
+			if (*cp == '\r')
+				continue;
+			if (*cp == '\n') {
+				*dp = '\0';
+				cbret = callback(line);
+				if (cbret < 0)
+					return(cbret);
+				totout += cbret;
+				dp = line;
+				linelen = 0;
+				continue;
+			}
+			*dp++ = *cp;
+			linelen++;
+			if (linelen >= sizeof line) {
+				fprintf(stderr,
+			"error: target response line length exceeds buffer\n");
+				return(-1);
+			}
+		}
+	}
+}
+
+/* single line response capture mechanism */
+/* same line buffer size as in tpinterf_capture_output() */
+char target_response_line[1024];
+
+static
+oneline_catcher(linein)
+	char *linein;
+{
+	strcpy(target_response_line, linein);
+	return(1);
+}
+
+tpinterf_capture_output_oneline(timeout)
+{
+	return tpinterf_capture_output(timeout, oneline_catcher);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/tpinterf3.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,60 @@
+/*
+ * The do_r16() and do_w16() functions implemented in this module
+ * provide programmatic access to the r16 and w16 commands on the target.
+ * They will be used to implement some flash operations.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char target_response_line[];
+
+do_r16(addr, retptr)
+	uint32_t addr;
+	uint16_t *retptr;
+{
+	char addr_arg[10], *argv[3];
+	int stat;
+	char *strtoul_endp;
+
+	sprintf(addr_arg, "%lx", (u_long) addr);
+	argv[0] = "r16";
+	argv[1] = addr_arg;
+	argv[2] = 0;
+	tpinterf_make_cmd(argv);
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	stat = tpinterf_capture_output_oneline(1);
+	if (stat != 1) {
+errout:		fprintf(stderr, "error: malformed response to r16 command\n");
+		return(-1);
+	}
+	if (strlen(target_response_line) != 4)
+		goto errout;
+	*retptr = strtoul(target_response_line, &strtoul_endp, 16);
+	if (strtoul_endp != target_response_line + 4)
+		goto errout;
+	return(0);
+}
+
+do_w16(addr, data)
+	uint32_t addr;
+	uint16_t data;
+{
+	char addr_arg[10], data_arg[10], *argv[4];
+
+	sprintf(addr_arg, "%lx", (u_long) addr);
+	sprintf(data_arg, "%lx", (u_long) data);
+	argv[0] = "w16";
+	argv[1] = addr_arg;
+	argv[2] = data_arg;
+	argv[3] = 0;
+	tpinterf_make_cmd(argv);
+	if (tpinterf_send_cmd() < 0)
+		return(-1);
+	return tpinterf_pass_output(1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/loadtools/ttypassthru.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,109 @@
+/*
+ * This module implements the pass-thru operation mode, in which
+ * the Unix host tty is cross-connected directly to the target
+ * running some code we have just loaded.
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+
+extern int errno;
+
+extern int target_fd;
+
+static struct termios saved_termios, my_termios;
+
+static void
+safe_output(buf, cc)
+	u_char *buf;
+{
+	int i, c;
+
+	for (i = 0; i < cc; i++) {
+		c = buf[i];
+		if (c == '\r' || c == '\n' || c == '\t' || c == '\b') {
+			putchar(c);
+			continue;
+		}
+		if (c & 0x80) {
+			putchar('M');
+			putchar('-');
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			putchar('^');
+			putchar(c + '@');
+		} else if (c == 0x7F) {
+			putchar('^');
+			putchar('?');
+		} else
+			putchar(c);
+	}
+	fflush(stdout);
+}
+
+static void
+loop()
+{
+	char buf[BUFSIZ];
+	fd_set fds, fds1;
+	register int i, cc, max;
+
+	FD_ZERO(&fds);
+	FD_SET(0, &fds);
+	FD_SET(target_fd, &fds);
+	max = target_fd + 1;
+	for (;;) {
+		bcopy(&fds, &fds1, sizeof(fd_set));
+		i = select(max, &fds1, NULL, NULL, NULL);
+		if (i < 0) {
+			if (errno == EINTR)
+				continue;
+			tcsetattr(0, TCSAFLUSH, &saved_termios);
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(0, &fds1)) {
+			cc = read(0, buf, sizeof buf);
+			if (cc <= 0)
+				return;
+			if (cc == 1 && buf[0] == 0x1C)
+				return;
+			write(target_fd, buf, cc);
+		}
+		if (FD_ISSET(target_fd, &fds1)) {
+			cc = read(target_fd, buf, sizeof buf);
+			if (cc <= 0) {
+				tcsetattr(0, TCSAFLUSH, &saved_termios);
+				fprintf(stderr, "EOF/error on target tty\n");
+				exit(1);
+			}
+			safe_output(buf, cc);
+		}
+	}
+}
+
+tty_passthru()
+{
+	static int zero = 0;
+
+	ioctl(target_fd, FIONBIO, &zero);
+
+	tcgetattr(0, &saved_termios);
+	bcopy(&saved_termios, &my_termios, sizeof(struct termios));
+	cfmakeraw(&my_termios);
+	my_termios.c_cc[VMIN] = 1;
+	my_termios.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSAFLUSH, &my_termios);
+
+	printf("Entering tty pass-thru; type ^\\ to exit\r\n\n");
+	loop();
+	tcsetattr(0, TCSAFLUSH, &saved_termios);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/miscutil/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,27 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	fc-rgbconv fc-serterm imei-luhn
+INSTBIN=/usr/local/bin
+
+all:	${PROGS}
+
+SERTERM_OBJS=	fc-serterm.o openport.o ttypassthru.o
+
+fc-rgbconv:	fc-rgbconv.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+fc-serterm:	${SERTERM_OBJS}
+	${CC} ${CFLAGS} -o $@ ${SERTERM_OBJS}
+
+ttypassthru.o:	../loadtools/ttypassthru.c
+	${CC} ${CFLAGS} -c -o $@ $<
+
+imei-luhn:	imei-luhn.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+install:
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f ${PROGS} *.o *errs *.out
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/miscutil/fc-rgbconv.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,77 @@
+/*
+ * This utility is an aid for phone UI development.  The color LCDs in the
+ * phones targeted by FreeCalypso implement 16-bit color in RGB 5:6:5 format,
+ * but these 16-bit color words are not particularly intuitive in raw hex form.
+ * In contrast, a 24-bit RGB 8:8:8 color given in RRGGBB hex form is quite
+ * intuitive, at least to those who understand RGB.
+ *
+ * This utility performs the conversion.  It takes a single command line
+ * argument that must be either 4 or 6 hex digits.  If the argument is 4 hex
+ * digits, it is interpreted as RGB 5:6:5 and converted into RGB 8:8:8.
+ * If the argument is 6 hex digits, it is interpreted as RGB 8:8:8 and
+ * converted into RGB 5:6:5.  The output of the conversion is emitted on
+ * stdout as 4 or 6 hex digits.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+is_arg_all_hex(str)
+	char *str;
+{
+	char *cp;
+	int c;
+
+	for (cp = str; c = *cp++; )
+		if (!isxdigit(c))
+			return(0);
+	return(1);
+}
+
+convert_565_to_888(in)
+	unsigned in;
+{
+	unsigned r, g, b;
+
+	r = in >> 11;
+	g = (in >> 5) & 0x3F;
+	b = in & 0x1F;
+	printf("%02X%02X%02X\n", r << 3, g << 2, b << 3);
+}
+
+convert_888_to_565(in)
+	unsigned in;
+{
+	unsigned r, g, b;
+
+	r = (in >> 16) >> 3;
+	g = ((in >> 8) & 0xFF) >> 2;
+	b = (in & 0xFF) >> 3;
+	printf("%04X\n", (r << 11) | (g << 5) | b);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	unsigned in;
+
+	if (argc != 2) {
+usage:		fprintf(stderr,
+		    "please specify one 4-digit or 6-digit hex RGB value\n");
+		exit(1);
+	}
+	if (!is_arg_all_hex(argv[1]))
+		goto usage;
+	in = strtoul(argv[1], 0, 16);
+	switch (strlen(argv[1])) {
+	case 4:
+		convert_565_to_888(in);
+		exit(0);
+	case 6:
+		convert_888_to_565(in);
+		exit(0);
+	}
+	goto usage;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/miscutil/fc-serterm.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,24 @@
+/*
+ * This hack-utility opens a serial port at the user-specified baud rate
+ * and drops into a terminal pass-thru mode, except that any binary bytes
+ * received on this port are turned into cat -v form.  The intent is for
+ * sniffing on and/or talking to targets that emit some ASCII mixed in
+ * with binary.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+int target_fd;
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s ttyname baudrate\n", argv[0]);
+		exit(1);
+	}
+	open_target_serial(argv[1], argv[2]);
+	tty_passthru();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/miscutil/imei-luhn.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,76 @@
+/*
+ * This program computes the Luhn check digit for an IMEI number given
+ * the first 14 digits, or verifies the correctness of this check digit
+ * given a 15-digit number as input.
+ *
+ * The number given on the command line may optionally include punctuation,
+ * which is skipped and ignored.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+char digits[15];
+
+compute_cd()
+{
+	int i, dig, sum;
+
+	sum = 0;
+	for (i = 0; i < 14; i++) {
+		dig = digits[i];
+		if (i & 1) {
+			dig *= 2;
+			if (dig > 9)
+				dig -= 9;
+		}
+		sum += dig;
+	}
+	dig = sum % 10;
+	if (dig)
+		dig = 10 - dig;
+	return dig;
+}
+
+main(argc, argv)
+	char **argv;
+{
+	char *cp;
+	int i;
+
+	if (argc != 2) {
+usage:		fprintf(stderr, "usage: %s number\n", argv[0]);
+		exit(2);
+	}
+	cp = argv[1];
+	if (!isdigit(*cp))
+		goto usage;
+	for (i = 0; *cp; ) {
+		if (ispunct(*cp))
+			cp++;
+		if (!isdigit(*cp))
+			goto usage;
+		if (i >= 15) {
+wrong_len:		fprintf(stderr,
+				"error: argument must have 14 or 15 digits\n");
+			exit(2);
+		}
+		digits[i++] = *cp++ - '0';
+	}
+	switch (i) {
+	case 14:
+		printf("%d\n", compute_cd());
+		exit(0);
+	case 15:
+		if (digits[14] == compute_cd()) {
+			printf("IMEI OK\n");
+			exit(0);
+		} else {
+			printf("Check digit mismatch!\n");
+			exit(1);
+		}
+	default:
+		goto wrong_len;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/miscutil/openport.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,65 @@
+/*
+ * Serial port opening code for fc-serterm
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+extern int target_fd;
+
+static struct baudrate {
+	char	*name;
+	speed_t	termios_code;
+} baud_rate_table[] = {
+	{"19200",	B19200},
+	{"38400",	B38400},
+	{"57600",	B57600},
+	{"115200",	B115200},
+	/* non-standard high baud rates "remapped" by CP2102 usb2serial IC */
+	{"203125",	B230400},
+	{"406250",	B460800},
+	{"812500",	B921600},
+	/* table search terminator */
+	{NULL,		B0}
+};
+
+open_target_serial(ttydev, baudname)
+	char *ttydev, *baudname;
+{
+	struct termios target_termios;
+	struct baudrate *br;
+
+	for (br = baud_rate_table; br->name; br++)
+		if (!strcmp(br->name, baudname))
+			break;
+	if (!br->name) {
+		fprintf(stderr, "baud rate \"%s\" unknown/unsupported\n",
+			baudname);
+		exit(1);
+	}
+	target_fd = open(ttydev, O_RDWR|O_NONBLOCK);
+	if (target_fd < 0) {
+		perror(ttydev);
+		exit(1);
+	}
+	target_termios.c_iflag = IGNBRK;
+	target_termios.c_oflag = 0;
+	target_termios.c_cflag = CLOCAL|HUPCL|CREAD|CS8;
+	target_termios.c_lflag = 0;
+	target_termios.c_cc[VMIN] = 1;
+	target_termios.c_cc[VTIME] = 0;
+	cfsetispeed(&target_termios, br->termios_code);
+	cfsetospeed(&target_termios, br->termios_code);
+	if (tcsetattr(target_fd, TCSAFLUSH, &target_termios) < 0) {
+		perror("initial tcsetattr on target");
+		exit(1);
+	}
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+PROGDIR=asyncshell ctracedec etmsync lowlevel tmsh
+LIBDIR=	libasync libg23
+SUBDIR=	${PROGDIR} ${LIBDIR}
+
+all:	${SUBDIR}
+
+asyncshell:	libasync libg23
+lowlevel:	libg23
+tmsh:		libasync
+
+${SUBDIR}: FRC
+	cd $@; ${MAKE} ${MFLAGS}
+
+clean: FRC
+	rm -f a.out core errs
+	for i in ${SUBDIR}; do (cd $$i; ${MAKE} ${MFLAGS} clean); done
+
+install: FRC
+	for i in ${PROGDIR}; do (cd $$i; ${MAKE} ${MFLAGS} install); done
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,19 @@
+CC=	gcc
+CFLAGS=	-O2 -I../include
+PROG=	fc-shell
+OBJS=	at.o gsm0610.o init.o main.o oneshot.o parse.o pktsort.o poweroff.o \
+	rxctl.o sendarb.o sendsp.o tchcmd.o tchplay.o tchrec.o usercmd.o
+LIBS=	../libasync/libasync.a ../libg23/libg23.a
+INSTBIN=/usr/local/bin
+
+all:	${PROG}
+
+${PROG}: ${OBJS} ${LIBS}
+	${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS}
+
+install:	${PROG}
+	mkdir -p ${INSTBIN}
+	install -c ${PROG} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROG}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/at.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,45 @@
+/*
+ * Functions for the AT-over-RVTMUX interface
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+
+send_string_to_ati(str)
+	char *str;
+{
+	unsigned len;
+	u_char sendpkt[MAX_PKT_TO_TARGET+1];
+
+	len = strlen(str);
+	if (len + 1 > MAX_PKT_TO_TARGET) {
+		printf("error: max pkt to target limit exceeded\n");
+		return(1);
+	}
+	/* fill out the packet */
+	sendpkt[0] = RVT_AT_HEADER;
+	strcpy(sendpkt + 1, str);
+	/* send it! */
+	send_pkt_to_target(sendpkt, len + 1);
+	return(0);
+}
+
+void
+cmd_sendat(arg)
+	char *arg;
+{
+	while (isspace(*arg))
+		arg++;
+	if (!*arg) {
+		printf("error: missing string argument\n");
+		return;
+	}
+	ati_rx_control(1);
+	send_string_to_ati(arg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/gsm0610.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,336 @@
+/*
+ * TI's DSP has API words from which GSM 06.10 codec bits that have been
+ * demodulated from the downlink may be read, FreeCalypso firmware has
+ * an experimental feature by way of which these bits may be forwarded
+ * to the external host, and fc-shell provides a mechanism to record
+ * this downlink speech stream in a file.  We would like to record these
+ * files in the libgsm format which the FLOSS world has adopted as the
+ * standard, so that they can be played with the SoX play command,
+ * for example.  But the bit order is different between what TI's DSP
+ * gives us and libgsm, hence we need to reshuffle the bits here.
+ *
+ * Adapted from OsmocomBB.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+
+/* GSM FR - subjective importance bit ordering */
+	/* This array encodes GSM 05.03 Table 2.
+	 * It's also GSM 06.10 Table A.2.1a
+	 *
+	 * It converts between serial parameter output by the encoder and the
+	 * order needed before channel encoding.
+	 */
+const u_short gsm0610_bitorder[260] = {
+	0,	/* LARc0:5 */
+	47,	/* Xmaxc0:5 */
+	103,	/* Xmaxc1:5 */
+	159,	/* Xmaxc2:5 */
+	215,	/* Xmaxc3:5 */
+	1,	/* LARc0:4 */
+	6,	/* LARc1:5 */
+	12,	/* LARc2:4 */
+	2,	/* LARc0:3 */
+	7,	/* LARc1:4 */
+	13,	/* LARc2:3 */
+	17,	/* LARc3:4 */
+	36,	/* Nc0:6 */
+	92,	/* Nc1:6 */
+	148,	/* Nc2:6 */
+	204,	/* Nc3:6 */
+	48,	/* Xmaxc0:4 */
+	104,	/* Xmaxc1:4 */
+	160,	/* Xmaxc2:4 */
+	216,	/* Xmaxc3:4 */
+	8,	/* LARc1:3 */
+	22,	/* LARc4:3 */
+	26,	/* LARc5:3 */
+	37,	/* Nc0:5 */
+	93,	/* Nc1:5 */
+	149,	/* Nc2:5 */
+	205,	/* Nc3:5 */
+	38,	/* Nc0:4 */
+	94,	/* Nc1:4 */
+	150,	/* Nc2:4 */
+	206,	/* Nc3:4 */
+	39,	/* Nc0:3 */
+	95,	/* Nc1:3 */
+	151,	/* Nc2:3 */
+	207,	/* Nc3:3 */
+	40,	/* Nc0:2 */
+	96,	/* Nc1:2 */
+	152,	/* Nc2:2 */
+	208,	/* Nc3:2 */
+	49,	/* Xmaxc0:3 */
+	105,	/* Xmaxc1:3 */
+	161,	/* Xmaxc2:3 */
+	217,	/* Xmaxc3:3 */
+	3,	/* LARc0:2 */
+	18,	/* LARc3:3 */
+	30,	/* LARc6:2 */
+	41,	/* Nc0:1 */
+	97,	/* Nc1:1 */
+	153,	/* Nc2:1 */
+	209,	/* Nc3:1 */
+	23,	/* LARc4:2 */
+	27,	/* LARc5:2 */
+	43,	/* bc0:1 */
+	99,	/* bc1:1 */
+	155,	/* bc2:1 */
+	211,	/* bc3:1 */
+	42,	/* Nc0:0 */
+	98,	/* Nc1:0 */
+	154,	/* Nc2:0 */
+	210,	/* Nc3:0 */
+	45,	/* Mc0:1 */
+	101,	/* Mc1:1 */
+	157,	/* Mc2:1 */
+	213,	/* Mc3:1 */
+	4,	/* LARc0:1 */
+	9,	/* LARc1:2 */
+	14,	/* LARc2:2 */
+	33,	/* LARc7:2 */
+	19,	/* LARc3:2 */
+	24,	/* LARc4:1 */
+	31,	/* LARc6:1 */
+	44,	/* bc0:0 */
+	100,	/* bc1:0 */
+	156,	/* bc2:0 */
+	212,	/* bc3:0 */
+	50,	/* Xmaxc0:2 */
+	106,	/* Xmaxc1:2 */
+	162,	/* Xmaxc2:2 */
+	218,	/* Xmaxc3:2 */
+	53,	/* xmc0_0:2 */
+	56,	/* xmc0_1:2 */
+	59,	/* xmc0_2:2 */
+	62,	/* xmc0_3:2 */
+	65,	/* xmc0_4:2 */
+	68,	/* xmc0_5:2 */
+	71,	/* xmc0_6:2 */
+	74,	/* xmc0_7:2 */
+	77,	/* xmc0_8:2 */
+	80,	/* xmc0_9:2 */
+	83,	/* xmc0_10:2 */
+	86,	/* xmc0_11:2 */
+	89,	/* xmc0_12:2 */
+	109,	/* xmc1_0:2 */
+	112,	/* xmc1_1:2 */
+	115,	/* xmc1_2:2 */
+	118,	/* xmc1_3:2 */
+	121,	/* xmc1_4:2 */
+	124,	/* xmc1_5:2 */
+	127,	/* xmc1_6:2 */
+	130,	/* xmc1_7:2 */
+	133,	/* xmc1_8:2 */
+	136,	/* xmc1_9:2 */
+	139,	/* xmc1_10:2 */
+	142,	/* xmc1_11:2 */
+	145,	/* xmc1_12:2 */
+	165,	/* xmc2_0:2 */
+	168,	/* xmc2_1:2 */
+	171,	/* xmc2_2:2 */
+	174,	/* xmc2_3:2 */
+	177,	/* xmc2_4:2 */
+	180,	/* xmc2_5:2 */
+	183,	/* xmc2_6:2 */
+	186,	/* xmc2_7:2 */
+	189,	/* xmc2_8:2 */
+	192,	/* xmc2_9:2 */
+	195,	/* xmc2_10:2 */
+	198,	/* xmc2_11:2 */
+	201,	/* xmc2_12:2 */
+	221,	/* xmc3_0:2 */
+	224,	/* xmc3_1:2 */
+	227,	/* xmc3_2:2 */
+	230,	/* xmc3_3:2 */
+	233,	/* xmc3_4:2 */
+	236,	/* xmc3_5:2 */
+	239,	/* xmc3_6:2 */
+	242,	/* xmc3_7:2 */
+	245,	/* xmc3_8:2 */
+	248,	/* xmc3_9:2 */
+	251,	/* xmc3_10:2 */
+	254,	/* xmc3_11:2 */
+	257,	/* xmc3_12:2 */
+	46,	/* Mc0:0 */
+	102,	/* Mc1:0 */
+	158,	/* Mc2:0 */
+	214,	/* Mc3:0 */
+	51,	/* Xmaxc0:1 */
+	107,	/* Xmaxc1:1 */
+	163,	/* Xmaxc2:1 */
+	219,	/* Xmaxc3:1 */
+	54,	/* xmc0_0:1 */
+	57,	/* xmc0_1:1 */
+	60,	/* xmc0_2:1 */
+	63,	/* xmc0_3:1 */
+	66,	/* xmc0_4:1 */
+	69,	/* xmc0_5:1 */
+	72,	/* xmc0_6:1 */
+	75,	/* xmc0_7:1 */
+	78,	/* xmc0_8:1 */
+	81,	/* xmc0_9:1 */
+	84,	/* xmc0_10:1 */
+	87,	/* xmc0_11:1 */
+	90,	/* xmc0_12:1 */
+	110,	/* xmc1_0:1 */
+	113,	/* xmc1_1:1 */
+	116,	/* xmc1_2:1 */
+	119,	/* xmc1_3:1 */
+	122,	/* xmc1_4:1 */
+	125,	/* xmc1_5:1 */
+	128,	/* xmc1_6:1 */
+	131,	/* xmc1_7:1 */
+	134,	/* xmc1_8:1 */
+	137,	/* xmc1_9:1 */
+	140,	/* xmc1_10:1 */
+	143,	/* xmc1_11:1 */
+	146,	/* xmc1_12:1 */
+	166,	/* xmc2_0:1 */
+	169,	/* xmc2_1:1 */
+	172,	/* xmc2_2:1 */
+	175,	/* xmc2_3:1 */
+	178,	/* xmc2_4:1 */
+	181,	/* xmc2_5:1 */
+	184,	/* xmc2_6:1 */
+	187,	/* xmc2_7:1 */
+	190,	/* xmc2_8:1 */
+	193,	/* xmc2_9:1 */
+	196,	/* xmc2_10:1 */
+	199,	/* xmc2_11:1 */
+	202,	/* xmc2_12:1 */
+	222,	/* xmc3_0:1 */
+	225,	/* xmc3_1:1 */
+	228,	/* xmc3_2:1 */
+	231,	/* xmc3_3:1 */
+	234,	/* xmc3_4:1 */
+	237,	/* xmc3_5:1 */
+	240,	/* xmc3_6:1 */
+	243,	/* xmc3_7:1 */
+	246,	/* xmc3_8:1 */
+	249,	/* xmc3_9:1 */
+	252,	/* xmc3_10:1 */
+	255,	/* xmc3_11:1 */
+	258,	/* xmc3_12:1 */
+	5,	/* LARc0:0 */
+	10,	/* LARc1:1 */
+	15,	/* LARc2:1 */
+	28,	/* LARc5:1 */
+	32,	/* LARc6:0 */
+	34,	/* LARc7:1 */
+	35,	/* LARc7:0 */
+	16,	/* LARc2:0 */
+	20,	/* LARc3:1 */
+	21,	/* LARc3:0 */
+	25,	/* LARc4:0 */
+	52,	/* Xmaxc0:0 */
+	108,	/* Xmaxc1:0 */
+	164,	/* Xmaxc2:0 */
+	220,	/* Xmaxc3:0 */
+	55,	/* xmc0_0:0 */
+	58,	/* xmc0_1:0 */
+	61,	/* xmc0_2:0 */
+	64,	/* xmc0_3:0 */
+	67,	/* xmc0_4:0 */
+	70,	/* xmc0_5:0 */
+	73,	/* xmc0_6:0 */
+	76,	/* xmc0_7:0 */
+	79,	/* xmc0_8:0 */
+	82,	/* xmc0_9:0 */
+	85,	/* xmc0_10:0 */
+	88,	/* xmc0_11:0 */
+	91,	/* xmc0_12:0 */
+	111,	/* xmc1_0:0 */
+	114,	/* xmc1_1:0 */
+	117,	/* xmc1_2:0 */
+	120,	/* xmc1_3:0 */
+	123,	/* xmc1_4:0 */
+	126,	/* xmc1_5:0 */
+	129,	/* xmc1_6:0 */
+	132,	/* xmc1_7:0 */
+	135,	/* xmc1_8:0 */
+	138,	/* xmc1_9:0 */
+	141,	/* xmc1_10:0 */
+	144,	/* xmc1_11:0 */
+	147,	/* xmc1_12:0 */
+	167,	/* xmc2_0:0 */
+	170,	/* xmc2_1:0 */
+	173,	/* xmc2_2:0 */
+	176,	/* xmc2_3:0 */
+	179,	/* xmc2_4:0 */
+	182,	/* xmc2_5:0 */
+	185,	/* xmc2_6:0 */
+	188,	/* xmc2_7:0 */
+	191,	/* xmc2_8:0 */
+	194,	/* xmc2_9:0 */
+	197,	/* xmc2_10:0 */
+	200,	/* xmc2_11:0 */
+	203,	/* xmc2_12:0 */
+	223,	/* xmc3_0:0 */
+	226,	/* xmc3_1:0 */
+	229,	/* xmc3_2:0 */
+	232,	/* xmc3_3:0 */
+	235,	/* xmc3_4:0 */
+	238,	/* xmc3_5:0 */
+	241,	/* xmc3_6:0 */
+	244,	/* xmc3_7:0 */
+	247,	/* xmc3_8:0 */
+	250,	/* xmc3_9:0 */
+	253,	/* xmc3_10:0 */
+	256,	/* xmc3_11:0 */
+	259,	/* xmc3_12:0 */
+	11,	/* LARc1:0 */
+	29,	/* LARc5:0 */
+};
+
+static int
+msb_get_bit(buf, bn)
+	u_char *buf;
+{
+	int pos_byte = bn >> 3;
+	int pos_bit  = 7 - (bn & 7);
+
+	return (buf[pos_byte] >> pos_bit) & 1;
+}
+
+static void
+msb_set_bit(buf, bn, bit)
+	u_char *buf;
+{
+	int pos_byte = bn >> 3;
+	int pos_bit  = 7 - (bn & 7);
+
+	buf[pos_byte] |= (bit << pos_bit);
+}
+
+void
+gsm0610_tidsp_to_libgsm(inbytes, outbytes)
+	u_char *inbytes, *outbytes;
+{
+	int i, di, si;
+
+	bzero(outbytes, 33);
+	outbytes[0] = 0xD0;
+	for (i = 0; i < 260; i++) {
+		di = gsm0610_bitorder[i];
+		si = (i > 181) ? i + 4 : i;
+		msb_set_bit(outbytes, 4 + di, msb_get_bit(inbytes, si));
+        }
+}
+
+void
+gsm0610_libgsm_to_tidsp(inbytes, outbytes)
+	u_char *inbytes, *outbytes;
+{
+	int i, di, si;
+
+	bzero(outbytes, 33);
+	for (i = 0; i < 260; i++) {
+		si = gsm0610_bitorder[i];
+		di = (i > 181) ? i + 4 : i;
+		msb_set_bit(outbytes, di, msb_get_bit(inbytes, 4 + si));
+        }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/init.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+/*
+ * This module contains the initialization code for fc-shell.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "localsock.h"
+
+init()
+{
+	static u_char want_rvt_lost[9] = {CLI2RVI_WANT_RVTRACE,
+					  0xFF, 0xFF, 0xFF, 0xFF,
+					  0x00, 0x00, 0x00, 0x00};
+
+	localsock_prep_for_length_rx();
+	send_init_command(want_rvt_lost, 9);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,96 @@
+/*
+ * This module contains the main() function for fc-shell.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+char *socket_pathname = "/tmp/rvinterf_socket";
+char *rvinterf_ttyport;
+int ttyhacks, dflag;
+
+int sock;
+
+extern char *rvinterf_Bopt, *rvinterf_lopt, *rvinterf_wopt;
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c, sopt = 0;
+	fd_set fds;
+
+	while ((c = getopt(argc, argv, "B:dl:p:s:w:")) != EOF)
+		switch (c) {
+		case 'B':
+			rvinterf_Bopt = optarg;
+			continue;
+		case 'd':
+			dflag++;
+			continue;
+		case 'l':
+			rvinterf_lopt = optarg;
+			continue;
+		case 'p':
+			rvinterf_ttyport = optarg;
+			continue;
+		case 's':
+			socket_pathname = optarg;
+			sopt++;
+			continue;
+		case 'w':
+			rvinterf_wopt = optarg;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: %s [options] [command]\n", argv[0]);
+			exit(1);
+		}
+	if (rvinterf_ttyport) {
+		if (sopt) {
+			fprintf(stderr,
+			"%s error: -p and -s options are mutually exclusive\n",
+				argv[0]);
+			exit(1);
+		}
+		launch_rvinterf(rvinterf_ttyport);
+	} else {
+		if (rvinterf_Bopt || rvinterf_lopt || rvinterf_wopt) {
+			fprintf(stderr,
+"%s error: -B, -l and -w options are meaningful only when launching rvinterf\n",
+				argv[0]);
+			exit(1);
+		}
+		connect_local_socket();
+	}
+
+	if (argv[optind])
+		return oneshot_command(argc - optind, argv + optind);
+
+	ttyhacks = isatty(0) && !dflag;
+	init();
+	tty_init();
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(0, &fds);
+		FD_SET(sock, &fds);
+		c = select(sock+1, &fds, 0, 0, 0);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			tty_cleanup();
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(0, &fds))
+			handle_tty_input();
+		if (FD_ISSET(sock, &fds))
+			handle_rvinterf_input();
+		fflush(stdout);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/oneshot.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,54 @@
+/*
+ * This module implements the one-shot command mode of fc-shell.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "limits.h"
+
+extern int cmd_poweroff();
+extern int cmd_send_oneshot();
+extern int cmd_sp_oneshot();
+extern int cmd_tchdl_oneshot();
+extern int cmd_tgtreset();
+
+static struct cmdtab {
+	char *cmd;
+	int minargs;
+	int maxargs;
+	int (*func)();
+} cmdtab[] = {
+	{"poweroff", 0, 0, cmd_poweroff},
+	{"send", 1, MAX_PKT_TO_TARGET, cmd_send_oneshot},
+	{"sp", 2, 2, cmd_sp_oneshot},
+	{"tch-dl", 1, 1, cmd_tchdl_oneshot},
+	{"tgtreset", 0, 0, cmd_tgtreset},
+	{0, 0, 0, 0}
+};
+
+oneshot_command(argc, argv)
+	char **argv;
+{
+	struct cmdtab *tp;
+
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr,
+			"error: \"%s\" is not a valid one-shot command\n",
+			argv[0]);
+		exit(1);
+	}
+	if (argc - 1 > tp->maxargs) {
+		fprintf(stderr, "%s: too many arguments\n", tp->cmd);
+		exit(1);
+	}
+	if (argc - 1 < tp->minargs) {
+		fprintf(stderr, "%s: too few arguments\n", tp->cmd);
+		exit(1);
+	}
+	return tp->func(argc, argv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/parse.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,53 @@
+/*
+ * This module implements the parser helper function that allows
+ * the same code to be reused between interactive and one-shot
+ * versions of the same command.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+parse_interactive_command_into_argv(argstr, argv, min_arg, max_arg, argcp)
+	char *argstr, **argv;
+	int min_arg, max_arg, *argcp;
+{
+	char *cp, **ap;
+
+	cp = argstr;
+	for (ap = argv; ; ) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp || *cp == '#')
+			break;
+		if (ap - argv >= max_arg) {
+			printf("error: too many arguments\n");
+			return(-1);
+		}
+		if (*cp == '"') {
+			*ap++ = ++cp;
+			while (*cp && *cp != '"')
+				cp++;
+			if (*cp != '"') {
+				printf("error: unterminated quoted string\n");
+				return(-1);
+			}
+			*cp++ = '\0';
+		} else {
+			*ap++ = cp;
+			while (*cp && !isspace(*cp))
+				cp++;
+			if (*cp)
+				*cp++ = '\0';
+		}
+	}
+	if (ap - argv < min_arg) {
+		printf("error: too few arguments\n");
+		return(-1);
+	}
+	*ap = 0;
+	*argcp = ap - argv;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/pktsort.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,83 @@
+/*
+ * Here we sort out incoming packets from the target relayed via rvinterf.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "localsock.h"
+#include "localtypes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+static void
+process_rvt()
+{
+	u32 useid;
+
+	if (rvi_msg_len < 7) {
+		tty_cleanup();
+		fprintf(stderr, "Error: rvinterf sent us an invalid RVT msg\n");
+		exit(1);
+	}
+	useid = rvi_msg[2] << 24 | rvi_msg[3] << 16 | rvi_msg[4] << 8
+		| rvi_msg[5];
+	switch (useid) {
+	case 0:
+		handle_useid_0();
+		return;
+	default:
+		tty_cleanup();
+		fprintf(stderr, "unexpected fwd of USEID %08X from rvinterf\n",
+			useid);
+		exit(1);
+	}
+}
+
+static void
+gpf_packet_rx()
+{
+	char fmtbuf[MAX_PKT_FROM_TARGET*8];	/* size it generously */
+
+	format_g23_packet(rvi_msg + 1, rvi_msg_len - 1, fmtbuf);
+	async_msg_output(fmtbuf);
+}
+
+static void
+response_from_ati()
+{
+	char buf[MAX_PKT_FROM_TARGET*4+2];
+
+	strcpy(buf, "ATI: ");
+	safe_print_trace(rvi_msg + 2, rvi_msg_len - 2, buf + 5);
+	async_msg_output(buf);
+}
+
+void
+process_pkt_from_target()
+{
+	switch (rvi_msg[1]) {
+	case RVT_RV_HEADER:
+		process_rvt();
+		return;
+	case RVT_L23_HEADER:
+		gpf_packet_rx();
+		return;
+	case RVT_AT_HEADER:
+		response_from_ati();
+		return;
+	case RVT_TCH_HEADER:
+		tch_packet_rx();
+		return;
+	default:
+		tty_cleanup();
+		fprintf(stderr, "unexpected fwd of MUX %02X from rvinterf\n",
+			rvi_msg[1]);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/poweroff.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,44 @@
+/*
+ * fc-shell poweroff and tgtreset commands
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "etm.h"
+
+send_etm_cmd(buf, len)
+	u_char *buf;
+{
+	int i, c;
+
+	buf[0] = RVT_TM_HEADER;
+	c = 0;
+	for (i = 1; i <= len; i++)
+		c ^= buf[i];
+	buf[i] = c;
+	send_pkt_to_target(buf, len + 2);
+	return 0;
+}
+
+cmd_poweroff()
+{
+	u_char cmdpkt[7];
+
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_CODEC_WR;
+	cmdpkt[3] = 30;		/* VRPCDEV */
+	cmdpkt[4] = 0x01;	/* low 8 bits */
+	cmdpkt[5] = 0;		/* high 2 bits */
+	return send_etm_cmd(cmdpkt, 5);
+}
+
+cmd_tgtreset()
+{
+	u_char cmdpkt[4];
+
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_RESET;
+	return send_etm_cmd(cmdpkt, 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/rxctl.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,103 @@
+/*
+ * This module contains the code for enabling and disabling the receiving
+ * of various packet types via rvinterf.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "localsock.h"
+
+void
+send_rxctl_cmd(channel, enable)
+{
+	u_char cmdbuf[2];
+
+	cmdbuf[0] = enable ? CLI2RVI_WANT_MUXPROTO : CLI2RVI_DROP_MUXPROTO;
+	cmdbuf[1] = channel;
+	send_init_command(cmdbuf, 2);
+}
+
+void
+ati_rx_control(newstate)
+{
+	static int state = 0;
+
+	if (state == newstate)
+		return;
+	send_rxctl_cmd(RVT_AT_HEADER, newstate);
+	state = newstate;
+}
+
+void
+gpf_rx_control(newstate)
+{
+	static int state = 0;
+
+	if (state == newstate)
+		return;
+	send_rxctl_cmd(RVT_L23_HEADER, newstate);
+	state = newstate;
+}
+
+void
+tch_rx_control(newstate)
+{
+	static int state = 0;
+
+	if (state == newstate)
+		return;
+	send_rxctl_cmd(RVT_TCH_HEADER, newstate);
+	state = newstate;
+}
+
+void
+rxctl_user_cmd(args, enable)
+	char *args;
+{
+	char *cp, *np;
+	int gotsome = 0;
+
+	for (cp = args; ; ) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp) {
+			if (!gotsome)
+				printf("error: argument required\n");
+			return;
+		}
+		for (np = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		if (!strcmp(np, "ati"))
+			ati_rx_control(enable);
+		else if (!strcmp(np, "gpf"))
+			gpf_rx_control(enable);
+		else if (!strcmp(np, "tch"))
+			tch_rx_control(enable);
+		else {
+			printf("error: unknown channel \"%s\"\n", np);
+			return;
+		}
+		gotsome++;
+	}
+}
+
+void
+cmd_enable(args)
+	char *args;
+{
+	rxctl_user_cmd(args, 1);
+}
+
+void
+cmd_disable(args)
+	char *args;
+{
+	rxctl_user_cmd(args, 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/sendarb.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,51 @@
+/*
+ * Command for sending arbitrary packets to the target
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "limits.h"
+
+cmd_send_common(argc, argv)
+	char **argv;
+{
+	u_char sendpkt[MAX_PKT_TO_TARGET];
+	unsigned pktlen = argc, i;
+	char *endp;
+
+	for (i = 0; i < pktlen; i++) {
+		sendpkt[i] = strtoul(argv[i], &endp, 16);
+		if (*endp) {
+			printf(
+		"error: all arguments to send command must be hex bytes\n");
+			return(1);
+		}
+	}
+	/* send it! */
+	send_pkt_to_target(sendpkt, pktlen);
+	return(0);
+}
+
+void
+cmd_send_interactive(argstr)
+	char *argstr;
+{
+	char *argv[MAX_PKT_TO_TARGET+1];
+	int argc, rc;
+
+	rc = parse_interactive_command_into_argv(argstr, argv, 1,
+						 MAX_PKT_TO_TARGET, &argc);
+	if (rc < 0)
+		return;
+	cmd_send_common(argc, argv);
+}
+
+cmd_send_oneshot(argc, argv)
+	char **argv;
+{
+	return cmd_send_common(argc - 1, argv + 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/sendsp.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,82 @@
+/*
+ * Functions for sending GPF system primitives to the target
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+
+send_gpf_sysprim(stackdest, primstr)
+	char *stackdest, *primstr;
+{
+	unsigned intlen;
+	u_char sendpkt[MAX_PKT_TO_TARGET+1];
+	unsigned pktlen;
+
+	if (strlen(stackdest) > 4) {
+		printf(
+		  "error: stack destination arg may not exceed 4 characters\n");
+		return(1);
+	}
+	intlen = 12 + strlen(primstr);
+	pktlen = intlen + 4;
+	if (pktlen > MAX_PKT_TO_TARGET) {
+		printf("error: max pkt to target limit exceeded\n");
+		return(1);
+	}
+	/* fill out the packet */
+	sendpkt[0] = RVT_L23_HEADER;
+	sendpkt[1] = 0xB7;	/* system prim */
+	sendpkt[2] = intlen;
+	sendpkt[3] = intlen >> 8;
+	/* send zeros for the timestamp */
+	sendpkt[4] = 0;
+	sendpkt[5] = 0;
+	sendpkt[6] = 0;
+	sendpkt[7] = 0;
+	/* as far as TI's sw is concerned, we are PCO */
+	sendpkt[8] = 'P';
+	sendpkt[9] = 'C';
+	sendpkt[10] = 'O';
+	sendpkt[11] = ' ';
+	sprintf(sendpkt + 12, "%-4s%s", stackdest, primstr);
+	/* send it! */
+	send_pkt_to_target(sendpkt, pktlen);
+	return(0);
+}
+
+void
+cmd_sp_interactive(args)
+	char *args;
+{
+	char *cp, *np;
+
+	for (cp = args; isspace(*cp); cp++)
+		;
+	if (!*cp) {
+err:		printf("error: two arguments required\n");
+		return;
+	}
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (!*cp)
+		goto err;
+	*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (!*cp)
+		goto err;
+	gpf_rx_control(1);
+	send_gpf_sysprim(np, cp);
+}
+
+cmd_sp_oneshot(argc, argv)
+	char **argv;
+{
+	return send_gpf_sysprim(argv[1], argv[2]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/tchcmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,185 @@
+/*
+ * Commands for manipulating the experimental TCH rerouting feature
+ * of FreeCalypso GSM firmware
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "tch_feature.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+static int tch_rawdump_mode;
+
+send_tch_config_req(config)
+{
+	u_char sendpkt[3];
+
+	sendpkt[0] = RVT_TCH_HEADER;
+	sendpkt[1] = TCH_CONFIG_REQ;
+	sendpkt[2] = config;
+	/* send it! */
+	send_pkt_to_target(sendpkt, 3);
+	return(0);
+}
+
+cmd_tchdl_common(argc, argv)
+	char **argv;
+{
+	int config;
+
+	if (!strcmp(argv[0], "enable") || !strcmp(argv[0], "on") ||
+	    !strcmp(argv[0], "1"))
+		config = 1;
+	else if (!strcmp(argv[0], "disable") || !strcmp(argv[0], "off") ||
+		 !strcmp(argv[0], "0"))
+		config = 0;
+	else {
+		printf("error: boolean argument required\n");
+		return(1);
+	}
+	return send_tch_config_req(config);
+}
+
+void
+cmd_tchdl_interactive(argstr)
+	char *argstr;
+{
+	char *argv[2];
+	int argc, rc;
+
+	rc = parse_interactive_command_into_argv(argstr, argv, 1, 1, &argc);
+	if (rc < 0)
+		return;
+	tch_rx_control(1);
+	cmd_tchdl_common(argc, argv);
+}
+
+cmd_tchdl_oneshot(argc, argv)
+	char **argv;
+{
+	return cmd_tchdl_common(argc - 1, argv + 1);
+}
+
+static void
+tch_rawdump()
+{
+	char buf[MAX_PKT_FROM_TARGET*3+5], *dp;
+	u_char *cp, *endp;
+
+	cp = rvi_msg + 2;
+	endp = rvi_msg + rvi_msg_len;
+	strcpy(buf, "TCH:");
+	dp = buf + 4;
+	while (cp < endp) {
+		sprintf(dp, " %02X", *cp++);
+		dp += 3;
+	}
+	*dp = '\0';
+	async_msg_output(buf);
+}
+
+void
+tch_packet_rx()
+{
+	char buf[128];
+
+	if (tch_rawdump_mode) {
+		tch_rawdump();
+		return;
+	}
+	if (rvi_msg_len < 3) {
+inv:		async_msg_output("Error: invalid TCH packet received");
+		return;
+	}
+	switch (rvi_msg[2]) {
+	case TCH_CONFIG_CONF:
+		if (rvi_msg_len != 4)
+			goto inv;
+		if (rvi_msg[3] & 0xFE)
+			goto inv;
+		sprintf(buf, "TCH_CONFIG_CONF: DL forwarding is %s",
+			rvi_msg[3] ? "enabled" : "disabled");
+		async_msg_output(buf);
+		return;
+	case TCH_ULBITS_CONF:
+		if (rvi_msg_len != 3)
+			goto inv;
+		tch_ulbits_conf();
+		return;
+	case TCH_DLBITS_IND:
+		if (rvi_msg_len != 43)
+			goto inv;
+		tch_dlbits_handler();
+		return;
+	default:
+		goto inv;
+	}
+}
+
+static void
+cmd_tch_dumpraw(argc, argv)
+	char **argv;
+{
+	if (argc < 2) {
+		printf("error: too few arguments\n");
+		return;
+	}
+	if (!strcmp(argv[1], "enable") || !strcmp(argv[1], "on") ||
+	    !strcmp(argv[1], "1"))
+		tch_rawdump_mode = 1;
+	else if (!strcmp(argv[1], "disable") || !strcmp(argv[1], "off") ||
+		 !strcmp(argv[1], "0"))
+		tch_rawdump_mode = 0;
+	else
+		printf("error: boolean argument required\n");
+}
+
+static void
+cmd_tch_status(argc, argv)
+	char **argv;
+{
+	if (argc > 1) {
+		printf("error: too many arguments\n");
+		return;
+	}
+	show_tch_record_status();
+	show_tch_play_status();
+	printf("TCH raw dump mode is %s\n",
+		tch_rawdump_mode ? "enabled" : "disabled");
+}
+
+void
+cmd_tch_dispatch(argstr)
+	char *argstr;
+{
+	char *argv[3];
+	int argc, rc;
+
+	rc = parse_interactive_command_into_argv(argstr, argv, 1, 2, &argc);
+	if (rc < 0)
+		return;
+	if (!strcmp(argv[0], "dump-raw")) {
+		cmd_tch_dumpraw(argc, argv);
+		return;
+	}
+	if (!strcmp(argv[0], "play")) {
+		cmd_tch_play(argc, argv);
+		return;
+	}
+	if (!strcmp(argv[0], "record")) {
+		cmd_tch_record(argc, argv);
+		return;
+	}
+	if (!strcmp(argv[0], "status")) {
+		cmd_tch_status(argc, argv);
+		return;
+	}
+	printf("error: invalid tch subcommand\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/tchplay.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,118 @@
+/*
+ * TCH uplink play-from-file functionality
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "tch_feature.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+extern void async_msg_output();
+
+static FILE *gsm_data_file;
+static int queued_frames;
+
+#define	QUEUE_LIMIT	3
+
+static void
+sync_msgout(msg)
+	char *msg;
+{
+	printf("%s\n", msg);
+}
+
+static void
+fill_uplink(msgout)
+	void (*msgout)();
+{
+	u_char readbytes[33], sendpkt[35];
+	int cc;
+
+	sendpkt[0] = RVT_TCH_HEADER;
+	sendpkt[1] = TCH_ULBITS_REQ;
+	while (queued_frames < QUEUE_LIMIT) {
+		cc = fread(readbytes, 1, 33, gsm_data_file);
+		if (cc < 33) {
+			if (cc)
+			  msgout("TCH UL: extra bytes at the end of the file");
+			msgout("TCH UL: file play finished");
+			gsm_data_file = 0;
+			return;
+		}
+		if ((readbytes[0] & 0xF0) != 0xD0) {
+			msgout("TCH UL: bad file input, play aborted");
+			gsm_data_file = 0;
+			return;
+		}
+		gsm0610_libgsm_to_tidsp(readbytes, sendpkt + 2);
+		send_pkt_to_target(sendpkt, 35);
+		queued_frames++;
+	}
+}
+
+void
+tch_ulbits_conf()
+{
+	if (queued_frames > 0)
+		queued_frames--;
+	if (gsm_data_file)
+		fill_uplink(async_msg_output);
+}
+
+static void
+cmd_tch_play_start(filename)
+	char *filename;
+{
+	if (gsm_data_file) {
+		printf("error: tch play session already in progress\n");
+		return;
+	}
+	gsm_data_file = fopen(filename, "r");
+	if (!gsm_data_file) {
+		perror(filename);
+		return;
+	}
+	printf("Starting TCH UL play from file\n");
+	tch_rx_control(1);
+	fill_uplink(sync_msgout);
+}
+
+static void
+cmd_tch_play_stop()
+{
+	if (!gsm_data_file) {
+		printf("error: no tch play session in progress\n");
+		return;
+	}
+	fclose(gsm_data_file);
+	gsm_data_file = 0;
+	printf("TCH UL play from file terminated\n");
+}
+
+void
+cmd_tch_play(argc, argv)
+	char **argv;
+{
+	if (argc < 2) {
+		printf("error: too few arguments\n");
+		return;
+	}
+	if (strcmp(argv[1], "stop"))
+		cmd_tch_play_start(argv[1]);
+	else
+		cmd_tch_play_stop();
+}
+
+void
+show_tch_play_status()
+{
+	printf("TCH UL play from file: %s\n",
+		gsm_data_file ? "RUNNING" : "not running");
+	printf("Outstanding UL frames: %d\n", queued_frames);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/tchrec.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,83 @@
+/*
+ * TCH downlink recording functionality
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "tch_feature.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+static FILE *gsm_data_file;
+static u_long frame_count;
+
+void
+tch_dlbits_handler()
+{
+	u_char writebytes[33];
+
+	if (!gsm_data_file)
+		return;
+	gsm0610_tidsp_to_libgsm(rvi_msg + 9, writebytes);
+	fwrite(writebytes, 1, 33, gsm_data_file);
+	frame_count++;
+}
+
+static void
+cmd_tch_record_start(filename)
+	char *filename;
+{
+	if (gsm_data_file) {
+		printf("error: tch record session already in progress\n");
+		return;
+	}
+	gsm_data_file = fopen(filename, "w");
+	if (!gsm_data_file) {
+		perror(filename);
+		return;
+	}
+	printf("Starting TCH DL recording\n");
+	tch_rx_control(1);
+	send_tch_config_req(1);
+	frame_count = 0;
+}
+
+static void
+cmd_tch_record_stop()
+{
+	if (!gsm_data_file) {
+		printf("error: no tch record session in progress\n");
+		return;
+	}
+	fclose(gsm_data_file);
+	gsm_data_file = 0;
+	printf("TCH DL recording stopped, captured %lu speech frames\n",
+		frame_count);
+	send_tch_config_req(0);
+}
+
+void
+cmd_tch_record(argc, argv)
+	char **argv;
+{
+	if (argc < 2) {
+		printf("error: too few arguments\n");
+		return;
+	}
+	if (strcmp(argv[1], "stop"))
+		cmd_tch_record_start(argv[1]);
+	else
+		cmd_tch_record_stop();
+}
+
+void
+show_tch_record_status()
+{
+	printf("TCH DL recording: %s\n",
+		gsm_data_file ? "RUNNING" : "not running");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/asyncshell/usercmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,74 @@
+/*
+ * This module implements interactive fc-shell command dispatch.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char usercmd[];
+
+extern void cmd_disable();
+extern void cmd_enable();
+extern void cmd_poweroff();
+extern void cmd_sendat();
+extern void cmd_send_interactive();
+extern void cmd_sp_interactive();
+extern void cmd_tch_dispatch();
+extern void cmd_tchdl_interactive();
+extern void cmd_tgtreset();
+
+void
+cmd_exit()
+{
+	tty_cleanup();
+	exit(0);
+}
+
+static struct cmdtab {
+	char *cmd;
+	void (*func)();
+} cmdtab[] = {
+	{"disable", cmd_disable},
+	{"enable", cmd_enable},
+	{"exit", cmd_exit},
+	{"poweroff", cmd_poweroff},
+	{"quit", cmd_exit},
+	{"send", cmd_send_interactive},
+	{"sp", cmd_sp_interactive},
+	{"str", cmd_sendat},
+	{"tch", cmd_tch_dispatch},
+	{"tch-dl", cmd_tchdl_interactive},
+	{"tgtreset", cmd_tgtreset},
+	{0, 0}
+};
+
+void
+dispatch_user_cmd()
+{
+	char *cp, *np;
+	struct cmdtab *tp;
+
+	for (cp = usercmd; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#')
+		return;
+	if (!strncmp(cp, "AT", 2) || !strncmp(cp, "at", 2)) {
+		cmd_sendat(cp);
+		return;
+	}
+	for (np = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, np))
+			break;
+	if (tp->func)
+		tp->func(cp);
+	else
+		printf("error: no such command\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/ctracedec/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,17 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	ctracedec
+OBJS=	decode.o doprnt.o main.o processlog.o readtab.o
+INSTBIN=/usr/local/bin
+
+all:	${PROG}
+
+${PROG}: ${OBJS}
+	${CC} ${CFLAGS} -o $@ ${OBJS}
+
+install:	${PROG}
+	mkdir -p ${INSTBIN}
+	install -c ${PROG} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROG}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/ctracedec/decode.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,150 @@
+/*
+ * This module implements the actual decoding of compressed traces.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char *str2ind_tab_filename;
+extern int str2ind_array_size;
+extern char **str2ind_orig_strings;
+extern char **str2ind_param_strings;
+
+#define	MAX_PRINTF_PARAMS	16
+
+static int cindex, cindex_nchars;
+static u_char param_bytes_array[256];
+static int param_bytes_count;
+static char *format_string, *param_type_string;
+static int num_printf_params;
+static u_long printf_params[MAX_PRINTF_PARAMS];
+static char output_buf[2048];
+
+decode_hex_digit(c)
+{
+	if (isdigit(c))
+		return(c - '0');
+	else if (isupper(c))
+		return(c - 'A' + 10);
+	else
+		return(c - 'a' + 10);
+}
+
+static int
+decode_idx_and_params(line)
+	char *line;
+{
+	char *cp;
+	u_char *dp;
+
+	for (cp = line; isdigit(*cp); cp++)
+		;
+	if (*cp && *cp != ' ')
+		return(-1);
+	cindex = atoi(line);
+	cindex_nchars = cp - line;
+	for (dp = param_bytes_array; *cp; ) {
+		if (*cp++ != ' ')
+			return(-1);
+		if (!isxdigit(cp[0]) || !isxdigit(cp[1]))
+			return(-1);
+		*dp++ = decode_hex_digit(cp[0]) << 4 | decode_hex_digit(cp[1]);
+		cp += 2;
+	}
+	param_bytes_count = dp - param_bytes_array;
+	return(0);
+}
+
+static void
+decode_parameters(filename_for_errs, lineno_for_errs)
+	char *filename_for_errs;
+{
+	int pi, type;
+	u_char *bp, *endp;
+
+	bp = param_bytes_array;
+	endp = bp + param_bytes_count;
+	for (pi = 0; pi < num_printf_params; pi++) {
+		type = param_type_string[pi];
+		switch (type) {
+		case 'c':
+			if (bp >= endp) {
+wrong_param_byte_count:		fprintf(stderr,
+	"%s line %d: wrong number of parameter bytes for %s entry #%d\n",
+					filename_for_errs, lineno_for_errs,
+					str2ind_tab_filename, cindex);
+				exit(1);
+			}
+			printf_params[pi] = *bp++;
+			continue;
+		case 'i':
+		case 'p':
+		case '*':
+			if (bp > endp - 4)
+				goto wrong_param_byte_count;
+			printf_params[pi] = (u_long) bp[0] |
+					    (u_long) bp[1] << 8 |
+					    (u_long) bp[2] << 16 |
+					    (u_long) bp[3] << 24;
+			bp += 4;
+			continue;
+		case 's':
+			printf_params[pi] = (u_long) bp;
+			for (;;) {
+				if (bp >= endp) {
+					fprintf(stderr,
+	"%s line %d: unterminated string parameter in compressed trace\n",
+						filename_for_errs,
+						lineno_for_errs);
+					exit(1);
+				}
+				if (!*bp++)
+					break;
+			}
+			continue;
+		default:
+			fprintf(stderr,
+			"%s entry #%d: parameter type \'%c\' not supported\n",
+				str2ind_tab_filename, cindex, type);
+			exit(1);
+		}
+	}
+	if (bp != endp)
+		goto wrong_param_byte_count;
+}
+
+process_ctrace_line(line, cindex_offset, filename_for_errs, lineno_for_errs)
+	char *line, *filename_for_errs;
+{
+	if (decode_idx_and_params(line + cindex_offset) < 0) {
+		fprintf(stderr,
+			"%s line %d: unable to decode compressed trace line\n",
+			filename_for_errs, lineno_for_errs);
+		exit(1);
+	}
+	if (cindex >= str2ind_array_size) {
+		fprintf(stderr,
+			"%s line %d: index %d exceeds the range of %s\n",
+			filename_for_errs, lineno_for_errs, cindex,
+			str2ind_tab_filename);
+		exit(1);
+	}
+	format_string = str2ind_orig_strings[cindex];
+	param_type_string = str2ind_param_strings[cindex];
+	num_printf_params = strlen(param_type_string);
+	if (num_printf_params > MAX_PRINTF_PARAMS) {
+		fprintf(stderr,
+			"error: entry #%d in %s has too many parameters\n",
+			cindex, str2ind_tab_filename);
+		exit(1);
+	}
+	decode_parameters(filename_for_errs, lineno_for_errs);
+	ind2str_doprnt(format_string, printf_params, output_buf);
+	printf("%.*s \"%s\"\n", cindex_offset + cindex_nchars, line,
+		output_buf);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/ctracedec/doprnt.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,322 @@
+/*
+ * We need our own implementation of an sprintf-like function in order
+ * to expand compressed traces with format strings from str2ind.tab
+ * and parameters decoded from the serial byte stream.  This module is
+ * a hacked-up version of the guts of the printf family of functions
+ * from 4.3BSD-Tahoe.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+static void
+safe_out_char(c, pp)
+	int c;
+	char **pp;
+{
+	char *dp;
+
+	c &= 0xFF;
+	dp = *pp;
+	if (c & 0x80) {
+		*dp++ = 'M';
+		*dp++ = '-';
+		c &= 0x7F;
+	}
+	if (c < 0x20) {
+		*dp++ = '^';
+		*dp++ = c + '@';
+	} else if (c == 0x7F) {
+		*dp++ = '^';
+		*dp++ = '?';
+	} else
+		*dp++ = c;
+	*pp = dp;
+}
+
+#define	PUTC(ch)	safe_out_char((ch), &outp)
+
+#define	ARG() \
+	_ulong = *argp++;
+
+#define	BUF	12
+
+#define	todigit(c)	((c) - '0')
+#define	tochar(n)	((n) + '0')
+
+#define	LONGINT		0x01		/* long integer */
+#define	LONGDBL		0x02		/* long double; unimplemented */
+#define	SHORTINT	0x04		/* short integer */
+#define	ALT		0x08		/* alternate form */
+#define	LADJUST		0x10		/* left adjustment */
+#define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
+#define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
+
+ind2str_doprnt(fmt0, argp, outp)
+	u_char *fmt0, *outp;
+	u_long *argp;
+{
+	register u_char *fmt;	/* format string */
+	register int ch;	/* character from fmt */
+	register int cnt;	/* return value accumulator */
+	register int n;		/* random handy integer */
+	register char *t;	/* buffer pointer */
+	u_long _ulong;		/* integer arguments %[diouxX] */
+	int base;		/* base for [diouxX] conversion */
+	int dprec;		/* decimal precision in [diouxX] */
+	int fieldsz;		/* field size expanded by sign, etc */
+	int flags;		/* flags as above */
+	int prec;		/* precision from format (%.3d), or -1 */
+	int realsz;		/* field size expanded by decimal precision */
+	int size;		/* size of converted field or string */
+	int width;		/* width from format (%8d), or 0 */
+	char sign;		/* sign prefix (' ', '+', '-', or \0) */
+	char softsign;		/* temporary negative sign for floats */
+	char *digs;		/* digits for [diouxX] conversion */
+	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
+
+	fmt = fmt0;
+	digs = "0123456789abcdef";
+	for (cnt = 0;; ++fmt) {
+		for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
+			PUTC(ch);
+		if (!ch) {
+			*outp = '\0';
+			return (cnt);
+		}
+
+		flags = 0; dprec = 0; width = 0;
+		prec = -1;
+		sign = '\0';
+
+rflag:		switch (*++fmt) {
+		case ' ':
+			/*
+			 * ``If the space and + flags both appear, the space
+			 * flag will be ignored.''
+			 *	-- ANSI X3J11
+			 */
+			if (!sign)
+				sign = ' ';
+			goto rflag;
+		case '#':
+			flags |= ALT;
+			goto rflag;
+		case '*':
+			/*
+			 * ``A negative field width argument is taken as a
+			 * - flag followed by a  positive field width.''
+			 *	-- ANSI X3J11
+			 * They don't exclude field widths read from args.
+			 */
+			if ((width = *argp++) >= 0)
+				goto rflag;
+			width = -width;
+			/* FALLTHROUGH */
+		case '-':
+			flags |= LADJUST;
+			goto rflag;
+		case '+':
+			sign = '+';
+			goto rflag;
+		case '.':
+			if (*++fmt == '*')
+				n = *argp++;
+			else {
+				n = 0;
+				while (isascii(*fmt) && isdigit(*fmt))
+					n = 10 * n + todigit(*fmt++);
+				--fmt;
+			}
+			prec = n < 0 ? -1 : n;
+			goto rflag;
+		case '0':
+			/*
+			 * ``Note that 0 is taken as a flag, not as the
+			 * beginning of a field width.''
+			 *	-- ANSI X3J11
+			 */
+			flags |= ZEROPAD;
+			goto rflag;
+		case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			n = 0;
+			do {
+				n = 10 * n + todigit(*fmt);
+			} while (isascii(*++fmt) && isdigit(*fmt));
+			width = n;
+			--fmt;
+			goto rflag;
+		case 'L':
+			flags |= LONGDBL;
+			goto rflag;
+		case 'h':
+			flags |= SHORTINT;
+			goto rflag;
+		case 'l':
+			flags |= LONGINT;
+			goto rflag;
+		case 'c':
+			*(t = buf) = *argp++;
+			size = 1;
+			sign = '\0';
+			goto pforw;
+		case 'D':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'd':
+		case 'i':
+			ARG();
+			if ((long)_ulong < 0) {
+				_ulong = -_ulong;
+				sign = '-';
+			}
+			base = 10;
+			goto number;
+		case 'O':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'o':
+			ARG();
+			base = 8;
+			goto nosign;
+		case 'p':
+			/*
+			 * ``The argument shall be a pointer to void.  The
+			 * value of the pointer is converted to a sequence
+			 * of printable characters, in an implementation-
+			 * defined manner.''
+			 *	-- ANSI X3J11
+			 */
+			/* NOSTRICT */
+			_ulong = *argp++;
+			base = 16;
+			goto nosign;
+		case 's':
+			if (!(t = (char *)*argp++))
+				t = "(null)";
+			if (prec >= 0) {
+				/*
+				 * can't use strlen; can only look for the
+				 * NUL in the first `prec' characters, and
+				 * strlen() will go further.
+				 */
+				char *p;
+
+				for (p = t, size = 0; size < prec; p++, size++)
+					if (*p == '\0')
+						break;
+			} else
+				size = strlen(t);
+			sign = '\0';
+			goto pforw;
+		case 'U':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'u':
+			ARG();
+			base = 10;
+			goto nosign;
+		case 'X':
+			digs = "0123456789ABCDEF";
+			/* FALLTHROUGH */
+		case 'x':
+			ARG();
+			base = 16;
+			/* leading 0x/X only if non-zero */
+			if (flags & ALT && _ulong != 0)
+				flags |= HEXPREFIX;
+
+			/* unsigned conversions */
+nosign:			sign = '\0';
+			/*
+			 * ``... diouXx conversions ... if a precision is
+			 * specified, the 0 flag will be ignored.''
+			 *	-- ANSI X3J11
+			 */
+number:			if ((dprec = prec) >= 0)
+				flags &= ~ZEROPAD;
+
+			/*
+			 * ``The result of converting a zero value with an
+			 * explicit precision of zero is no characters.''
+			 *	-- ANSI X3J11
+			 */
+			t = buf + BUF;
+			if (_ulong != 0 || prec != 0) {
+				do {
+					*--t = digs[_ulong % base];
+					_ulong /= base;
+				} while (_ulong);
+				digs = "0123456789abcdef";
+				if (flags & ALT && base == 8 && *t != '0')
+					*--t = '0'; /* octal leading 0 */
+			}
+			size = buf + BUF - t;
+
+pforw:
+			/*
+			 * All reasonable formats wind up here.  At this point,
+			 * `t' points to a string which (if not flags&LADJUST)
+			 * should be padded out to `width' places.  If
+			 * flags&ZEROPAD, it should first be prefixed by any
+			 * sign or other prefix; otherwise, it should be blank
+			 * padded before the prefix is emitted.  After any
+			 * left-hand padding and prefixing, emit zeroes
+			 * required by a decimal [diouxX] precision, then print
+			 * the string proper, then emit zeroes required by any
+			 * leftover floating precision; finally, if LADJUST,
+			 * pad with blanks.
+			 */
+
+			/*
+			 * compute actual size, so we know how much to pad
+			 * fieldsz excludes decimal prec; realsz includes it
+			 */
+			fieldsz = size;
+			if (sign)
+				fieldsz++;
+			if (flags & HEXPREFIX)
+				fieldsz += 2;
+			realsz = dprec > fieldsz ? dprec : fieldsz;
+
+			/* right-adjusting blank padding */
+			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
+				for (n = realsz; n < width; n++)
+					PUTC(' ');
+			/* prefix */
+			if (sign)
+				PUTC(sign);
+			if (flags & HEXPREFIX) {
+				PUTC('0');
+				PUTC((char)*fmt);
+			}
+			/* right-adjusting zero padding */
+			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
+				for (n = realsz; n < width; n++)
+					PUTC('0');
+			/* leading zeroes from decimal precision */
+			for (n = fieldsz; n < dprec; n++)
+				PUTC('0');
+
+			for (n = size; --n >= 0; )
+				PUTC(*t++);
+			/* left-adjusting padding (always blank) */
+			if (flags & LADJUST)
+				for (n = realsz; n < width; n++)
+					PUTC(' ');
+			/* finally, adjust cnt */
+			cnt += width > realsz ? width : realsz;
+			break;
+		case '\0':	/* "%?" prints ?, unless ? is NULL */
+			*outp = '\0';
+			return (cnt);
+		default:
+			PUTC((char)*fmt);
+			cnt++;
+		}
+	}
+	/* NOTREACHED */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/ctracedec/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,26 @@
+/*
+ * This module contains the main() function for ctracedec
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+char *str2ind_tab_filename;
+
+main(argc, argv)
+	char **argv;
+{
+	int i;
+
+	if (argc < 3) {
+		fprintf(stderr,
+			"usage: %s str2ind.tab logfile [more log files]\n",
+			argv[0]);
+		exit(1);
+	}
+	str2ind_tab_filename = argv[1];
+	read_str2ind_tab();
+	for (i = 2; i < argc; i++)
+		process_log_file(argv[i]);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/ctracedec/processlog.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,81 @@
+/*
+ * This module contains the code that processes rvtdump/rvinterf log files
+ * at the high level and identifies which lines are compressed traces.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+is_logline_ctrace(line)
+	char *line;
+{
+	char *cp = line;
+
+	if (*cp++ != '[')
+		return(0);
+	if (!isdigit(*cp++))
+		return(0);
+	if (!isdigit(*cp++))
+		return(0);
+	if (*cp++ != ':')
+		return(0);
+	if (!isdigit(*cp++))
+		return(0);
+	if (!isdigit(*cp++))
+		return(0);
+	if (*cp++ != ':')
+		return(0);
+	if (!isdigit(*cp++))
+		return(0);
+	if (!isdigit(*cp++))
+		return(0);
+	if (strncmp(cp, "] GPF trace ", 12))
+		return(0);
+	cp += 12;
+	while (isalpha(*cp)) {
+		while (*cp && !isspace(*cp))
+			cp++;
+		if (isspace(*cp))
+			cp++;
+		else
+			return(0);
+	}
+	if (isdigit(*cp))
+		return(cp - line);
+	else
+		return(0);
+}
+
+process_log_file(filename)
+	char *filename;
+{
+	FILE *f;
+	char linebuf[512], *cp;
+	int lineno, i;
+
+	f = fopen(filename, "r");
+	if (!f) {
+		perror(filename);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) {
+		cp = index(linebuf, '\n');
+		if (!cp) {
+			fprintf(stderr,
+			    "error: %s line %d is too long or unterminated\n",
+				filename, lineno);
+			exit(1);
+		}
+		*cp = '\0';
+		i = is_logline_ctrace(linebuf);
+		if (i)
+			process_ctrace_line(linebuf, i, filename, lineno);
+		else
+			puts(linebuf);
+	}
+	fclose(f);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/ctracedec/readtab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,145 @@
+/*
+ * This module handles the reading and parsing of str2ind.tab
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char *str2ind_tab_filename;
+
+int str2ind_array_size;
+char **str2ind_orig_strings;
+char **str2ind_param_strings;
+
+static FILE *readF;
+static char linebuf[256];
+static int lineno;
+
+static void
+read_line()
+{
+	char *cp;
+
+	if (!fgets(linebuf, sizeof linebuf, readF)) {
+		fprintf(stderr, "error: premature EOF reading from %s\n",
+			str2ind_tab_filename);
+		exit(1);
+	}
+	lineno++;
+	cp = index(linebuf, '\n');
+	if (!cp) {
+		fprintf(stderr,
+			"error: %s line %d is too long or unterminated\n",
+			str2ind_tab_filename, lineno);
+		exit(1);
+	}
+	*cp = '\0';
+	if (cp > linebuf && cp[-1] == '\r')
+		cp[-1] = '\0';
+}
+
+static void
+line_pure_num()
+{
+	char *cp;
+
+	for (cp = linebuf; isspace(*cp); cp++)
+		;
+	if (!isdigit(*cp)) {
+inv:		fprintf(stderr, "%s line %d: pure number expected\n",
+			str2ind_tab_filename, lineno);
+		exit(1);
+	}
+	while (isdigit(*cp))
+		cp++;
+	if (*cp)
+		goto inv;
+}
+
+static char *
+copystr(str)
+	char *str;
+{
+	static char null = '\0';
+	char *buf;
+
+	if (str[0]) {
+		buf = malloc(strlen(str) + 1);
+		if (!buf) {
+			perror("malloc");
+			exit(1);
+		}
+		strcpy(buf, str);
+		return(buf);
+	} else
+		return(&null);
+}
+
+static void
+process_record_line(idx)
+{
+	char *cp, *cp2;
+	int i;
+
+	for (cp = linebuf; isspace(*cp); cp++)
+		;
+	if (!isdigit(*cp)) {
+syntaxerr:	fprintf(stderr, "%s line %d: unexpected syntax\n",
+			str2ind_tab_filename, lineno);
+		exit(1);
+	}
+	while (isdigit(*cp))
+		cp++;
+	if (*cp++ != ',')
+		goto syntaxerr;
+	i = atoi(linebuf);
+	if (i != idx) {
+		fprintf(stderr, "%s line %d lists wrong index (expected %d)\n",
+			str2ind_tab_filename, lineno, idx);
+		exit(1);
+	}
+	cp2 = index(cp, ',');
+	if (!cp2)
+		goto syntaxerr;
+	*cp2++ = '\0';
+	str2ind_param_strings[idx] = copystr(cp);
+	str2ind_orig_strings[idx] = copystr(cp2);
+}
+
+read_str2ind_tab()
+{
+	int idx;
+
+	readF = fopen(str2ind_tab_filename, "r");
+	if (!readF) {
+		perror(str2ind_tab_filename);
+		exit(1);
+	}
+	/* skip the timestamp line: the user is responsible for matching */
+	read_line();
+	line_pure_num();
+	/* read the line with the array size */
+	read_line();
+	line_pure_num();
+	str2ind_array_size = atoi(linebuf);
+	if (str2ind_array_size < 1) {
+		fprintf(stderr, "error: %s gives index array size < 1\n",
+			str2ind_tab_filename);
+		exit(1);
+	}
+	str2ind_orig_strings = malloc(sizeof(char *) * str2ind_array_size);
+	str2ind_param_strings = malloc(sizeof(char *) * str2ind_array_size);
+	if (!str2ind_orig_strings || !str2ind_param_strings) {
+		perror("malloc");
+		exit(1);
+	}
+	for (idx = 0; idx < str2ind_array_size; idx++) {
+		read_line();
+		process_record_line(idx);
+	}
+	fclose(readF);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/doc/README.old	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,70 @@
+You are looking at the suite of FreeCalypso tools for talking to the RVTMUX
+interface provided by TI-based GSM firmwares.  If you haven't already, please
+read ../doc/RVTMUX first.
+
+The fundamental difference between these tools and loadtools is that loadtools
+operate on a GSM device while its regular firmware is shut down, whereas the
+present rvinterf tools talk to active running GSM firmwares.
+
+The following tools are currently implemented:
+
+rvtdump		Opens the serial port, decodes TI's binary packet protocol, and
+		simply dumps every received/decoded packet on stdout in a human-
+		readable form.  No provision for sending anything to the target.
+		Intended use: observing the debug trace output which all TI
+		firmwares emit as standard "background noise".  This utility
+		allows one to observe/log/study the "noise" that appears on
+		Pirelli's USB-serial port (running Pirelli's original fw),
+		as well as that emitted on the IrDA (headset jack) port on the
+		GTA02 by mokoN/leo2moko firmwares.
+
+rvinterf	Provides a bidirectional interface to RVTMUX on the host side.
+		It dumps and/or logs the "background noise" emitted by the
+		target just like rvtdump, but also creates a local UNIX domain
+		socket on the host machine to which other programs can connect,
+		replicating the MUXing function on the host side.
+
+fc-tmsh		Interactive asynchronous test mode shell.  This program connects
+		to a target GSM device through rvinterf and allows a developer-
+		operator to send various ETM commands to the target.  ETM
+		responses are decoded (sometimes only lightly) and displayed.
+		fc-tmsh is fully asynchronous in that it continuously listens
+		(via select(2)) both for user input and for packets from the
+		target at the same time, translating any user-entered commands
+		into packets to the target and conversely, scribbling on the
+		terminal when a packet arrives from the target.  It has no
+		knowledge of any correspondence between commands and responses
+		they normally elicit.
+
+g23sh		Like fc-tmsh (same asynchronous design), but for GPF/G23 rather
+		than ETM.  This tool and FreeCalypso project's understanding of
+		GPF/G23 in general are currently in the earliest stages, so it
+		is premature to try to describe it any further at this point.
+
+fc-sendsp	Precursor to g23sh; even less worthy of further documentation.
+
+fc-fsio		This program uses RVTMUX, ETM and TMFFS2 to access the live file
+		system of a running GSM firmware.  Of the existing proprietary
+		firmwares, the only one that implements the TMFFS2 protocol
+		required for fc-fsio is Pirelli's, to the best of our knowledge.
+		This program connects to the target through rvinterf, but it
+		differs from fc-tmsh in that it operates in a synchronous
+		manner: the flow of operation is driven by user commands (just
+		like in fc-loadtool), and every time the program sends an ETM
+		command packet to the target, it expects a lock-step response.
+
+tfc139		See ../doc/Compal-unlock
+
+The fc-fsio, fc-tmsh and g23sh tools connect to the target not directly, but
+via rvinterf.  Two usage scenarios are supported:
+
+1. The user explicitly runs rvinterf (either directly or secondary to fc-xram,
+   when testing an experimental FreeCalypso firmware ramImage), leaves it
+   running (either backgrounded or in its own terminal window), and then runs
+   one of the "client" programs: fc-fsio, fc-tmsh or g23sh.  The two programs
+   connect via local UNIX domain sockets on the host machine.
+
+2. All of the "client" programs under discussion can also launch rvinterf
+   themselves.  An instance of rvinterf lauched in this manner becomes a child
+   process of the "client" program, terminating together with it, and the two
+   processes communicate via an unnamed and unbound socket pair.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/doc/README.older	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,89 @@
+I (Spacefalcon the Outlaw, FreeCalypso developer) am still learning what kinds
+of traffic may be passed across TI's RVTMUX binary-packet serial interface.  We
+already know that much of this traffic is debug trace output, i.e.,
+unidirectional and essentially unconditional output from the GSM device.  All
+of the "standard" firmwares we have (mokoN, our leo2moko which functions almost
+identically, and Pirelli's fw) produce massive volumes of such trace output in
+normal operation.  We already know that this "unsolicited" trace output comes
+in at least 3 different flavors:
+
+* RiViera traces emitted by rvf_send_trace()
+* L1 traces
+* G23 traces
+
+The RVTMUX interface can be used for more than just trace output, though: any
+component in TI's fw suite can send and/or register to receive binary packets.
+As I slowly work my way through various components which comprise TI's Leonardo
+fw whose semi-source we use as our reference version, learning what they do and
+reintegrating them in our own gsm-fw, I will undoubtedly uncover additional uses
+to which the RVTMUX interface is put.
+
+Aside from the trivial provision in the RVT module itself whereby an external
+host can send a command to the target to set a filter masking some of the RV
+trace output, so far the only entity I've come across which accepts packets from
+an external host is ETM (Enhanced Test Mode).  ETM implements a registration
+system of its own, whereby other modules can register with ETM to receive
+certain external command messages passing first through RVT, then through ETM.
+
+Because I do not yet have a clear mental picture of *every* function for which
+the RVTMUX interface will ever be used, it is correspondingly impractical to
+decide on a once-and-for-all design of what the host-side software for talking
+to this interface should be like.  Therefore, it is currently premature to
+expect any stability in the present rvinterf subdirectory of freecalypso-sw; I
+may implement something one day, then toss it away the next day (without
+providing much in the way of backward compatibility) when I come up with some
+other idea.
+
+The current roadmap for what the rvinterf suite of host tools is envisioned to
+look like eventually is as follows:
+
+rvtdump		Opens the serial port, decodes TI's binary packet protocol, and
+		simply dumps every received/decoded packet on stdout in a human-
+		readable form.  No provision for sending anything to the target.
+		Intended use: observing the debug trace output which all TI
+		firmwares emit as standard "background noise".  This utility has
+		already been written, and it allows one to observe/log/study the
+		"noise" that appears on Pirelli's USB-serial port (running
+		Pirelli's original fw), as well as that emitted on the IrDA
+		(headset jack) port on the GTA02 by mokoN/leo2moko firmwares.
+
+rvinterf	My plan is to make a copy of rvtdump, called rvinterf, and have
+		it act very much like rvtdump: receive TI's packets from the
+		serial port, decode them and print the decoded form on stdout.
+		However, rvinterf will also create a listening UNIX domain
+		socket to which other programs in the present suite will
+		connect.  These other programs connecting through rvinterf will
+		be able to send packets to the target, as well as register to
+		receive certain kinds of target->host message packets.
+
+fc-tmsh		FreeCalypso Test Mode Shell is my vision for the utility which
+		will provide a practically usable interface to ETM.  ETM's
+		general mode of operation seems to be (weasel phrase inserted
+		because many other fw components may connect through ETM, and I
+		have yet to study all of them) command-response: an external
+		host sends a command to ETM, that command gets dispatched to the
+		proper registered handler, the command function is executed, a
+		response packet is composed, and finally that response is sent
+		back to the host.  But because all code on the target side is
+		under active development and debugging, we should not expect
+		perfect lock-step behaviour on the interface; instead, our
+		fc-tmsh should be fundamentally asynchronous: when the user
+		enters a command, the appropriate command packet is sent to the
+		target, but we are prepared for target->host messages at any
+		time, without enforcing strict correspondence to issued
+		commands: let the developer-operator sort that out instead.
+
+The usage scenario I envision is that one will need to run rvinterf first
+(either directly or through fc-xram) in one terminal window, leave it running,
+then run fc-tmsh in another terminal window, and have it connect to rvinterf
+via the local UNIX domain socket interface.  Why such complexity, why not have
+one program do everything?  I suspect that in many debug/experimentation
+sessions it will be necessary to use fc-tmsh on "noisy" targets, i.e., in
+scenarios where the target is continuously spewing its "normal" voluminous debug
+trace output, such that the "interesting" output as in responses to commands
+gets drowned in the noise.  In such a scenario it would be helpful to have one
+terminal window in which one sees the transcript of the fc-tmsh session,
+consisting of issued commands and received ETM responses without the general
+noise, and another window in which one sees all RVTMUX interface activity in
+real time - the latter would allow one to observe commands having side effects
+outside of ETM, such as crashing the whole fw. :-)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/doc/rvinterf.usage	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,59 @@
+Rvinterf (the specific program by this name) is an extended version of rvtdump
+(see rvtdump.usage) that decodes and dumps and/or logs any and all output
+generated by the firmware running on the target just like rvtdump, but also
+creates a local UNIX domain socket on the host machine to which "client"
+programs can connect.  "Client" programs connecting to rvinterf via this local
+socket interface can:
+
+1. Receive copies of selected RVTMUX packets coming from the target;
+
+2. Send arbitrary RVTMUX packets toward the target.
+
+Rvinterf is invoked just like rvtdump:
+
+rvinterf [options] /dev/ttyXXX
+
+The following options have the same meaning as in rvtdump, see rvtdump.usage
+for the details: -b, -B, -d and -l.  The only difference is that -b without -l
+is potentially useful and thus allowed.
+
+Additional rvinterf options which don't exist in rvtdump are:
+
+-n
+
+	Suppress the output on stdout like -b, but don't fork into background.
+	This option is passed by "client" programs when they invoke rvinterf
+	behind the scenes instead of connecting to an already-running rvinterf
+	instance.
+
+-s pathname_for_socket
+
+	By default the local UNIX domain socket created by rvinterf is bound to
+	/tmp/rvinterf_socket; this option allows any other pathname to be
+	specified.
+
+-S <file descriptor number>
+
+	This option is not meant for direct use by human users.  It is passed
+	by "client" programs when they invoke rvinterf behind the scenes with
+	an unnamed and unbound socket pair instead of conecting to an already-
+	running rvinterf instance.
+
+-w number_in_seconds
+
+	It has been discovered experimentally that if an RVTMUX packet is sent
+	to a target when the latter is in the "deep sleep" state, the target
+	wakes up, but the packet that was sent is not received correctly.  TI's
+	reference fw seems to wait for 10 s after last serial activity before
+	falling into deep sleep (provided that all other conditions for that
+	sleep mode are satisfied), hence the following workaround has been
+	implemented in rvinterf: if a packet is to be sent to the target and
+	more than a set time has elapsed since the last transmitted packet, the
+	packet is preceded by a "wake-up shot" of 64 0 bytes (outside of a
+	packet, ignored by the fw) and a 100 ms pause.  This hack is not pretty,
+	but it appears to do its job of making RVTMUX communication with target
+	firmwares reliable.
+
+	The -w option changes the elapsed time threshold at which the "wake-up
+	shot" is sent; the default is 7 s.  Specify -w 0 to disable this hack
+	altogether.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/doc/rvtdump.usage	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,40 @@
+Rvtdump is a utility that listens on a serial port, receives traces or any other
+packets emitted by the running firmware of a GSM device in TI's RVTMUX format,
+decodes them into readable ASCII and emits them to stdout and/or to a log file.
+It is to be invoked as follows:
+
+rvtdump [options] /dev/ttyXXX
+
+where the sole non-option argument is the serial port it should open and listen
+on.
+
+The available options are:
+
+-b
+
+	Normally the rvtdump process remains in the foreground and emits its
+	output on stdout.  The -b option suppresses the normal output and causes
+	rvtdump to put itself in the background: fork at startup, then have the
+	parent exit while the child remains running.  -b is not useful and not
+	allowed without -l.
+
+-B baud
+
+	Selects which RVTMUX serial channel baud rate our tool should listen
+	for.  Defaults to 115200 baud, which appears to be TI's default and is
+	correct for mokoN, leo2moko and Pirelli's fw.  Use -B 57600 for Compal's
+	RVTMUX, the one accessible via **16379#.
+
+-d <file descriptor number>
+
+	This option is not meant for direct use by human users.  It is inserted
+	automatically when rvtdump is launched from fc-xram as the secondary
+	program that immediately takes over the serial channel.
+
+-l logfile
+
+	Log all received and decoded packets into the specified file in addition
+	to (without -b) or instead of (with -b) dumping them on stdout.  Each
+	line in the log file is also time-stamped; the timestamps are in GMT
+	(gmtime(3)) instead of local time - Spacefalcon the Outlaw dislikes
+	local times.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/doc/tfc139.usage	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,38 @@
+The tfc139 hack-utility (see ../../doc/Compal-unlock) is based on the
+rvinterf/rvtdump skeleton, and it needs to be invoked as follows:
+
+tfc139 [options] /dev/ttyXXX
+
+In the well-tested use case of breaking into TFC139 phones with fw version
+8.8.17, no options are normally needed, but the following options are supported:
+
+-a address
+
+	This option changes the RAM address into which the "shellcode" is to be
+	written; the argument is always interpreted as hex.  The default is
+	0x800000, as used by the mot931c.exe closed source tool on whose
+	reverse-engineering our hack-utility is based.
+
+-B baud
+
+	This option changes the serial baud rate just like in rvinterf and
+	rvtdump, but the default is 57600 as needed for breaking into TFC139
+	firmware.
+
+-l logfile
+
+	Log activity in a file, just like rvinterf and rvtdump.
+
+-s address
+
+	Just like mot931c.exe has been observed to do, we start our stack
+	smashing attempts at a certain address, and keep incrementing by 4
+	until we either succeed or crash the fw in some other way that does not
+	help us.  This option changes the starting address for these stack
+	smashing attempts; the argument is always interpreted as hex.  The
+	default is 0x837C54, as observed from the reverse engineering of
+	mot931c.
+
+-w number_in_seconds
+
+	See rvinterf.usage; the option is the same for tfc139 as for rvinterf.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,44 @@
+CC=	gcc
+CFLAGS=	-O2 -I../include
+PROGS=	fc-dspapidump fc-fsio fc-getpirimei fc-olddump fc-pirhackinit
+INSTBIN=/usr/local/bin
+
+DSPDUMP_OBJS=	connect.o dspapidump.o interf.o launchrvif.o memops.o \
+		simplemain.o
+
+FSIO_OBJS=	connect.o dispatch.o fdcmd.o fileio.o fsbasics.o fscmdtab.o \
+		fserr.o fsiomain.o fsmisc.o fspath.o fsread.o fsupload.o \
+		fswrite.o interf.o launchrvif.o memcmd.o memops.o rfcap.o \
+		stddirs.o symlink.o
+
+OLDDUMP_OBJS=	connect.o interf.o launchrvif.o memops.o olddump.o simplemain.o
+
+PIRIMEI_OBJS=	connect.o interf.o launchrvif.o memops.o pirimei.o \
+		pirimeimain.o simplemain.o
+
+PIRHACK_OBJS=	connect.o fileio.o fserr.o interf.o launchrvif.o memops.o \
+		pirhackinit.o pirimei.o rfcap.o simplemain.o stddirs.o
+
+all:	${PROGS}
+
+fc-dspapidump:	${DSPDUMP_OBJS}
+	${CC} ${CFLAGS} -o $@ ${DSPDUMP_OBJS}
+
+fc-fsio:	${FSIO_OBJS}
+	${CC} ${CFLAGS} -o $@ ${FSIO_OBJS}
+
+fc-olddump:	${OLDDUMP_OBJS}
+	${CC} ${CFLAGS} -o $@ ${OLDDUMP_OBJS}
+
+fc-getpirimei:	${PIRIMEI_OBJS}
+	${CC} ${CFLAGS} -o $@ ${PIRIMEI_OBJS} -lcrypto
+
+fc-pirhackinit:	${PIRHACK_OBJS}
+	${CC} ${CFLAGS} -o $@ ${PIRHACK_OBJS} -lcrypto
+
+install:	${PROGS}
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/cmdtab.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,8 @@
+struct cmdtab {
+	char *cmd;
+	int minargs;
+	int maxargs;
+	int (*func)();
+};
+
+#define	MAX_CMD_ARGS	16
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/connect.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,56 @@
+/*
+ * Connecting to an already running rvinterf process
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pktmux.h"
+#include "localsock.h"
+#include "exitcodes.h"
+
+char *socket_pathname = "/tmp/rvinterf_socket";
+int sock;
+
+connect_local_socket()
+{
+	/* local socket binding voodoo copied from osmocon */
+	struct sockaddr_un local;
+	unsigned int namelen;
+	int rc;
+
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		perror("socket(AF_UNIX, SOCK_STREAM, 0)");
+		exit(ERROR_UNIX);
+	}
+
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, socket_pathname, sizeof(local.sun_path));
+	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	namelen = SUN_LEN(&local);
+#else
+	namelen = strlen(local.sun_path) +
+		  offsetof(struct sockaddr_un, sun_path) + 1;
+#endif
+
+	rc = connect(sock, (struct sockaddr *) &local, namelen);
+	if (rc != 0) {
+		perror(socket_pathname);
+		exit(ERROR_RVINTERF);
+	}
+
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/dispatch.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,131 @@
+/*
+ * This module implements the command dispatch for fc-fsio
+ * and possibly other similar utilities in the future.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "cmdtab.h"
+#include "exitcodes.h"
+
+extern struct cmdtab cmdtab[];
+
+parse_and_dispatch_cmd(cmd, is_script)
+	char *cmd;
+{
+	char *argv[MAX_CMD_ARGS+2];
+	char *cp, **ap;
+	struct cmdtab *tp;
+
+	for (cp = cmd; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#')
+		return(0);
+	if (is_script)
+		printf("Script command: %s\n", cp);
+	argv[0] = cp;
+	while (*cp && !isspace(*cp))
+		cp++;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr, "error: no such command\n");
+		return(ERROR_USAGE);
+	}
+	for (ap = argv + 1; ; ) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp || *cp == '#')
+			break;
+		if (ap - argv - 1 >= tp->maxargs) {
+			fprintf(stderr, "error: too many arguments\n");
+			return(ERROR_USAGE);
+		}
+		if (*cp == '"') {
+			*ap++ = ++cp;
+			while (*cp && *cp != '"')
+				cp++;
+			if (*cp != '"') {
+				fprintf(stderr,
+					"error: unterminated quoted string\n");
+				return(ERROR_USAGE);
+			}
+			*cp++ = '\0';
+		} else {
+			*ap++ = cp;
+			while (*cp && !isspace(*cp))
+				cp++;
+			if (*cp)
+				*cp++ = '\0';
+		}
+	}
+	if (ap - argv - 1 < tp->minargs) {
+		fprintf(stderr, "error: too few arguments\n");
+		return(ERROR_USAGE);
+	}
+	*ap = 0;
+	return tp->func(ap - argv, argv);
+}
+
+dispatch_ready_argv(argc, argv)
+	char **argv;
+{
+	struct cmdtab *tp;
+
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr, "error: no such command\n");
+		return(ERROR_USAGE);
+	}
+	if (argc - 1 > tp->maxargs) {
+		fprintf(stderr, "error: too many arguments\n");
+		return(ERROR_USAGE);
+	}
+	if (argc - 1 < tp->minargs) {
+		fprintf(stderr, "error: too few arguments\n");
+		return(ERROR_USAGE);
+	}
+	return tp->func(argc, argv);
+}
+
+cmd_exec(argc, argv)
+	char **argv;
+{
+	FILE *f;
+	char linebuf[512], *cp;
+	int lineno, retval = 0;
+
+	f = fopen(argv[1], "r");
+	if (!f) {
+		perror(argv[1]);
+		return(ERROR_USAGE);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) {
+		cp = index(linebuf, '\n');
+		if (!cp) {
+			fprintf(stderr, "%s line %d: missing newline\n",
+				argv[1], lineno);
+			fclose(f);
+			return(ERROR_USAGE);
+		}
+		*cp = '\0';
+		retval = parse_and_dispatch_cmd(linebuf, 1);
+		if (retval)
+			break;
+	}
+	fclose(f);
+	return(retval);
+}
+
+cmd_exit()
+{
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/dspapidump.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,39 @@
+/*
+ * This utility uses ETM in synchronous mode to read and dump the contents
+ * of the DSP API RAM in a target Calypso GSM device while the firmware is
+ * running.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stdint.h>
+#include <endian.h>
+#include "exitcodes.h"
+
+#define	APIF_ADDR		0xFFD00000
+#define	API_SIZE_IN_WORDS	0x2000
+
+single_op_main()
+{
+	uint16_t buf[64], *linebase;
+	unsigned off;
+	int rc, i, j;
+
+	for (off = 0; off < API_SIZE_IN_WORDS; ) {
+		rc = do_memory_read_16(APIF_ADDR + off * 2, buf, 0x40);
+		if (rc)
+			return(rc);
+		for (i = 0; i < 8; i++) {
+			printf("%04X:", off);
+			linebase = buf + i * 8;
+			for (j = 0; j < 8; j++)
+				printf(" %04X", le16toh(linebase[j]));
+			putchar('\n');
+			off += 8;
+		}
+	}
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/exitcodes.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,5 @@
+#define	ERROR_USAGE	1
+#define	ERROR_TARGET	2
+#define	ERROR_RVINTERF	3
+#define	ERROR_UNIX	4
+#define	ERROR_BUG	5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fdcmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,87 @@
+/*
+ * File descriptor debug commands
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "cmdtab.h"
+#include "exitcodes.h"
+
+cmd_fd_open(argc, argv)
+	char **argv;
+{
+	int rc, fd;
+
+	rc = fd_open(argv[1], strtoul(argv[2], 0, 16), &fd);
+	if (rc)
+		return(rc);
+	printf("%d\n", fd);
+	return(0);
+}
+
+cmd_fd_read(argc, argv)
+	char **argv;
+{
+	u_char databuf[MAX_READ_DATA];
+	int rc, sz, off, l;
+
+	rc = fd_read(strtoul(argv[1], 0, 0), databuf, strtoul(argv[2], 0, 0),
+			&sz);
+	if (rc)
+		return(rc);
+	printf("%d bytes read\n", sz);
+	for (off = 0; off < sz; off += 16) {
+		l = sz - off;
+		if (l > 16)
+			l = 16;
+		hexdump_line(off, databuf + off, l);
+	}
+	return(0);
+}
+
+cmd_fd_close(argc, argv)
+	char **argv;
+{
+	return fd_close(strtoul(argv[1], 0, 0));
+}
+
+struct cmdtab fd_cmds[] = {
+	{"close", 1, 1, cmd_fd_close},
+	{"open", 2, 2, cmd_fd_open},
+	{"read", 2, 2, cmd_fd_read},
+	{0, 0, 0, 0}
+};
+
+cmd_fd(argc, argv)
+	char **argv;
+{
+	struct cmdtab *tp;
+	int extargs;
+
+	for (tp = fd_cmds; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[1]))
+			break;
+	if (!tp->func) {
+		fprintf(stderr, "error: no such fd command\n");
+		return(ERROR_USAGE);
+	}
+	extargs = argc - 2;
+	if (extargs > tp->maxargs) {
+		fprintf(stderr, "error: too many arguments\n");
+		return(ERROR_USAGE);
+	}
+	if (extargs < tp->minargs) {
+		fprintf(stderr, "error: too few arguments\n");
+		return(ERROR_USAGE);
+	}
+	return tp->func(argc - 1, argv + 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fileio.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,401 @@
+/*
+ * FFS2 file descriptor I/O operations
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "ffserr.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "exitcodes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+fd_open(pathname, flags, fdrtn)
+	char *pathname;
+	int flags, *fdrtn;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen;
+
+	slen = strlen(pathname);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_OPEN;
+	*dp++ = slen + 1;
+	strcpy(dp, pathname);
+	dp += slen + 1;
+	*dp++ = flags;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("open fd", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != 6) {
+		printf("error: open fd response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	*fdrtn = rvi_msg[4];
+	return(0);
+}
+
+fd_read(fd, databuf, rdsize, rdret)
+	u_char *databuf;
+	int fd, rdsize, *rdret;
+{
+	u_char cmdpkt[6];
+	int rc, sz;
+
+	if (rdsize > MAX_READ_DATA) {
+		printf("error: # of bytes to read may not exceed %d\n",
+			MAX_READ_DATA);
+		return(ERROR_USAGE);
+	}
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_READ;
+	cmdpkt[3] = fd;
+	cmdpkt[4] = rdsize;
+	rc = etm_pkt_exch(cmdpkt, 4);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("read fd", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len < 6) {
+		*rdret = 0;
+		return(0);
+	}
+	sz = rvi_msg[4];
+	if (rvi_msg_len != sz + 6 || sz > rdsize) {
+		printf("error: read fd response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	bcopy(rvi_msg + 5, databuf, sz);
+	*rdret = sz;
+	return(0);
+}
+
+fd_write(fd, data, wrsize)
+	u_char *data;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc;
+
+	if (wrsize > MAX_PKT_TO_TARGET - 6) {
+		printf("error: fd write data fails to fit in the packet\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_WRITE;
+	*dp++ = fd;
+	*dp++ = wrsize;
+	bcopy(data, dp, wrsize);
+	dp += wrsize;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("fd write", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != 6) {
+		printf("error: TMFFS_WRITE response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[4] != wrsize) {
+		printf("fd write error: # of bytes written != requested\n");
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
+
+fd_close(fd)
+{
+	u_char cmdpkt[5];
+	int rc;
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_CLOSE;
+	cmdpkt[3] = fd;
+	rc = etm_pkt_exch(cmdpkt, 3);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("close fd", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != 5) {
+		printf("error: close fd response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
+
+do_file_read(pathname, databuf, rdsize, rdret)
+	char *pathname;
+	u_char *databuf;
+	int rdsize, *rdret;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen, sz;
+
+	slen = strlen(pathname);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	if (rdsize > MAX_READ_DATA) {
+		printf("error: # of bytes to read may not exceed %d\n",
+			MAX_READ_DATA);
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_FILE_READ;
+	*dp++ = slen + 1;
+	strcpy(dp, pathname);
+	dp += slen + 1;
+	*dp++ = rdsize;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("read file", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len < 6) {
+		*rdret = 0;
+		return(0);
+	}
+	sz = rvi_msg[4];
+	if (rvi_msg_len != sz + 6 || sz > rdsize) {
+		printf("error: read file response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	bcopy(rvi_msg + 5, databuf, sz);
+	*rdret = sz;
+	return(0);
+}
+
+max_short_file_write(pathname)
+	char *pathname;
+{
+	return MAX_PKT_TO_TARGET - 3 - (strlen(pathname) + 2) - 3;
+}
+
+do_short_fwrite(pathname, data, datalen)
+	char *pathname;
+	u_char *data;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen;
+
+	slen = strlen(pathname);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	if (datalen > max_short_file_write(pathname)) {
+		printf("error: short write data fails to fit in the packet\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_FILE_WRITE;
+	*dp++ = slen + 1;
+	strcpy(dp, pathname);
+	dp += slen + 1;
+	*dp++ = datalen;
+	bcopy(data, dp, datalen);
+	dp += datalen;
+	*dp++ = FFS_O_CREATE | FFS_O_TRUNC;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg_len != 5) {
+		printf("error: TMFFS_FILE_WRITE response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[3]) {
+		report_ffs_err("short file write", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
+
+do_opendir(pathname, statertn, countrtn)
+	char *pathname;
+	u_char *statertn;
+	int *countrtn;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen;
+
+	slen = strlen(pathname);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_OPENDIR;
+	*dp++ = slen + 1;
+	strcpy(dp, pathname);
+	dp += slen + 1;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("opendir", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != 11 || rvi_msg[5] != 4) {
+		printf("error: opendir response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	*countrtn = rvi_msg[4];
+	bcopy(rvi_msg + 6, statertn, 4);
+	return(0);
+}
+
+do_readdir(state, namebuf, namebuflen)
+	u_char *state;
+	char *namebuf;
+{
+	u_char cmdpkt[10];
+	int rc, slen;
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_READDIR;
+	cmdpkt[3] = 4;
+	bcopy(state, cmdpkt+4, 4);
+	cmdpkt[8] = TMFFS_STRING_SIZE;
+	rc = etm_pkt_exch(cmdpkt, 8);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("readdir", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len < 14) {
+malformed:	printf("error: readdir response is malformed\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[5] != 4)
+		goto malformed;
+	slen = rvi_msg[10];
+	if (slen < 2 || rvi_msg_len != slen + 12)
+		goto malformed;
+	if (slen > namebuflen) {
+		printf("error: readdir response exceeds provided buffer\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[11 + slen - 1])	/* must be terminating NUL */
+		goto malformed;
+	bcopy(rvi_msg + 6, state, 4);
+	strcpy(namebuf, rvi_msg + 11);
+	return(0);
+}
+
+do_xlstat(pathname, result)
+	char *pathname;
+	struct stat_info *result;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen;
+
+	slen = strlen(pathname);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_XLSTAT;
+	*dp++ = slen + 1;
+	strcpy(dp, pathname);
+	dp += slen + 1;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("xlstat", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != 30 || rvi_msg[4] != 24) {
+		printf("error: xlstat response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	result->type = rvi_msg[5];
+	result->flags = rvi_msg[6];
+	result->inode = rvi_msg[7] | rvi_msg[8] << 8;
+	result->size = rvi_msg[9] | rvi_msg[10] << 8 | rvi_msg[11] << 16 |
+			rvi_msg[12] << 24;
+	result->space = rvi_msg[13] | rvi_msg[14] << 8 | rvi_msg[15] << 16 |
+			rvi_msg[16] << 24;
+	result->location = rvi_msg[17] | rvi_msg[18] << 8 | rvi_msg[19] << 16 |
+				rvi_msg[20] << 24;
+	result->block = rvi_msg[22];
+	result->sequence = rvi_msg[23] | rvi_msg[24] << 8;
+	result->updates = rvi_msg[25] | rvi_msg[26] << 8;
+	return(0);
+}
+
+do_mkdir_existok(pathname)
+	char *pathname;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen;
+	struct stat_info stat;
+
+	slen = strlen(pathname);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_MKDIR;
+	*dp++ = slen + 1;
+	strcpy(dp, pathname);
+	dp += slen + 1;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg_len != 5) {
+		printf("error: mkdir response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (!rvi_msg[3])	/* success */
+		return(0);
+	if (rvi_msg[3] != TMFFS_ERR_EXISTS) {
+		report_ffs_err("mkdir", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	/* object already exists: OK if it's a directory, error otherwise */
+	rc = do_xlstat(pathname, &stat);
+	if (rc)
+		return(rc);
+	if (stat.type == OT_DIR)
+		return(0);
+	else {
+		printf("error: %s exists and is not a directory\n", pathname);
+		return(ERROR_TARGET);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fsbasics.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,96 @@
+/*
+ * Basic FFS2 operations
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "ffslimits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "exitcodes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+cmd_ffs2ver()
+{
+	u_char cmdpkt[4];
+	int rc;
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_VERSION;
+	rc = etm_pkt_exch(cmdpkt, 2);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		printf("FFS2 error %d\n", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != 7) {
+		printf("error: FFS2 version response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	printf("FFS2 version: %02X.%02X\n", rvi_msg[5], rvi_msg[4]);
+	return(0);
+}
+
+cmd_ls(argc, argv)
+	char **argv;
+{
+	u_char state[4];
+	char namebuf[256];
+	int nument, i, rc;
+
+	rc = do_opendir(argv[1], state, &nument);
+	if (rc)
+		return(rc);
+	if (!nument) {
+		printf("<empty dir>\n");
+		return(0);
+	}
+	for (i = 0; i < nument; i++) {
+		rc = do_readdir(state, namebuf, sizeof namebuf);
+		if (rc)
+			return(rc);
+		printf("%s\n", namebuf);
+	}
+	return(0);
+}
+
+cmd_stat(argc, argv)
+	char **argv;
+{
+	struct stat_info stat;
+	int rc;
+	char *type;
+
+	rc = do_xlstat(argv[1], &stat);
+	if (rc)
+		return(rc);
+	switch (stat.type) {
+	case OT_FILE:
+		type = "file";
+		break;
+	case OT_DIR:
+		type = "directory";
+		break;
+	case OT_LINK:
+		type = "symlink";
+		break;
+	default:
+		type = "???";
+	}
+	printf("Type: %s%s\n", type,
+		stat.flags & OF_READONLY ? ", read-only" : "");
+	printf("inode %x\n", stat.inode);
+	printf("size %u, space %u\n", stat.size, stat.space);
+	printf("location=%x, block %d\n", stat.location, stat.block);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fscmdtab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,66 @@
+/*
+ * fc-fsio command dispatch table
+ */
+
+#include "cmdtab.h"
+
+extern int cmd_cpout();
+extern int cmd_cpout_file();
+extern int cmd_delete();
+extern int cmd_dieid();
+extern int cmd_exec();
+extern int cmd_exit();
+extern int cmd_fd();
+extern int cmd_ffs2ver();
+extern int cmd_format();
+extern int cmd_fwrite();
+extern int cmd_hd();
+extern int cmd_ll();
+extern int cmd_ls();
+extern int cmd_memdump();
+extern int cmd_mkdir();
+extern int cmd_omemdump();
+extern int cmd_preformat();
+extern int cmd_readlink();
+extern int cmd_set_imeisv();
+extern int cmd_set_pcm_string();
+extern int cmd_set_rfcap();
+extern int cmd_stat();
+extern int cmd_symlink();
+extern int cmd_uploadfs();
+extern int cmd_upload_file();
+extern int cmd_upload_subtree();
+
+extern int create_std_dirs();
+
+struct cmdtab cmdtab[] = {
+	{"cpout", 2, 2, cmd_cpout},
+	{"cpout-file", 2, 2, cmd_cpout_file},
+	{"create-std-dirs", 0, 0, create_std_dirs},
+	{"delete", 1, 1, cmd_delete},
+	{"dieid", 0, 0, cmd_dieid},
+	{"exec", 1, 1, cmd_exec},
+	{"exit", 0, 0, cmd_exit},
+	{"fd", 2, 3, cmd_fd},
+	{"ffs2ver", 0, 0, cmd_ffs2ver},
+	{"format", 1, 1, cmd_format},
+	{"fwrite", 3, 3, cmd_fwrite},
+	{"hd", 1, 1, cmd_hd},
+	{"ll", 1, 1, cmd_ll},
+	{"ls", 1, 1, cmd_ls},
+	{"memdump", 2, 2, cmd_memdump},
+	{"mkdir", 1, 1, cmd_mkdir},
+	{"mk-std-dirs", 0, 0, create_std_dirs},
+	{"omemdump", 2, 2, cmd_omemdump},
+	{"preformat", 0, 0, cmd_preformat},
+	{"readlink", 1, 1, cmd_readlink},
+	{"set-imeisv", 2, 2, cmd_set_imeisv},
+	{"set-pcm-string", 2, 2, cmd_set_pcm_string},
+	{"set-rfcap", 1, 1, cmd_set_rfcap},
+	{"stat", 1, 1, cmd_stat},
+	{"symlink", 2, 2, cmd_symlink},
+	{"upload-file", 2, 2, cmd_upload_file},
+	{"upload-fs", 1, 1, cmd_uploadfs},
+	{"upload-subtree", 2, 2, cmd_upload_subtree},
+	{0, 0, 0, 0}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fserr.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,69 @@
+/*
+ * FFS error decoding
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "ffserr.h"
+
+static struct errtab {
+	int	errcode;
+	char	*desc;
+} errtab[] = {
+	{TMFFS_ERR_NODEVICE, "EFFS_NODEVICE: flash device unknown"},
+	{TMFFS_ERR_CORRUPTED, "EFFS_CORRUPTED: filesystem corrupted"},
+	{TMFFS_ERR_NOPREFORMAT, "EFFS_NOPREFORMAT: ffs not preformatted"},
+	{TMFFS_ERR_NOFORMAT, "EFFS_NOFORMAT: ffs not formatted"},
+	{TMFFS_ERR_BADFORMAT,
+		"EFFS_BADFORMAT: incompatible ffs version, reformat needed"},
+	{TMFFS_ERR_MAGIC, "EFFS_MAGIC: bad magic"},
+	{TMFFS_ERR_AGAIN, "EFFS_AGAIN: not ready, try again later"},
+	{TMFFS_ERR_NOSYS, "EFFS_NOSYS: function not implemented"},
+	{TMFFS_ERR_DRIVER, "EFFS_DRIVER: ffs device driver error"},
+	{TMFFS_ERR_NOSPACE, "EFFS_NOSPACE: out of data space"},
+	{TMFFS_ERR_FSFULL, "EFFS_FSFULL: file system full, no free inodes"},
+	{TMFFS_ERR_BADNAME, "EFFS_BADNAME: bad filename"},
+	{TMFFS_ERR_NOTFOUND, "EFFS_NOTFOUND: object not found"},
+	{TMFFS_ERR_EXISTS, "EFFS_EXISTS: object exists"},
+	{TMFFS_ERR_ACCESS, "EFFS_ACCESS: access permission violation"},
+	{TMFFS_ERR_NAMETOOLONG, "EFFS_NAMETOOLONG"},
+	{TMFFS_ERR_INVALID, "EFFS_INVALID"},
+	{TMFFS_ERR_DIRNOTEMPTY, "EFFS_DIRNOTEMPTY"},
+	{TMFFS_ERR_NOTADIR, "EFFS_NOTADIR"},
+	{TMFFS_ERR_SPARE, "EFFS_SPARE"},
+	{TMFFS_ERR_FILETOOBIG, "EFFS_FILETOOBIG"},
+	{TMFFS_ERR_NOTAFILE, "EFFS_NOTAFILE"},
+	{TMFFS_ERR_PATHTOODEEP, "EFFS_PATHTOODEEP"},
+	{TMFFS_ERR_NUMFD, "EFFS_NUMFD: max number of open files reached"},
+	{TMFFS_ERR_BADFD, "EFFS_BADFD: bad file descriptor"},
+	{TMFFS_ERR_BADOP, "EFFS_BADOP: bad operation"},
+	{TMFFS_ERR_LOCKED, "EFFS_LOCKED: the file is locked"},
+	{TMFFS_ERR_TOOBIG, "EFFS_TOOBIG: tmffs buffer overflow"},
+	{TMFFS_ERR_MEMORY, "EFFS_MEMORY: out of memory"},
+	{TMFFS_ERR_MSGSEND, "EFFS_MSGSEND: message send failed"},
+	{TMFFS_ERR_SIBLINGLOOP, "EFFS_SIBLINGLOOP: directory sibling loop"},
+	{TMFFS_ERR_NOBLOCKS, "EFFS_NOBLOCKS: debug error?"},
+	{TMFFS_ERR_DBR, "EFFS_DBR: debug error?"},
+	{TMFFS_ERR_RECLAIMLOOP, "EFFS_RECLAIMLOOP: debug error?"},
+	{0, 0}
+};
+
+report_ffs_err(oper, errcode)
+	char *oper;
+{
+	struct errtab *tp;
+	char *errdesc;
+
+	for (tp = errtab; tp->errcode; tp++)
+		if (tp->errcode == errcode)
+			break;
+	errdesc = tp->desc;
+	if (!errdesc)
+		errdesc = "unknown";
+	printf("%s: FFS error %d (%s)\n", oper, errcode, errdesc);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fsiomain.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,76 @@
+/*
+ * This module contains the main() function for fc-fsio.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "exitcodes.h"
+
+extern char *socket_pathname;
+extern char *rvinterf_ttyport, *rvinterf_Bopt, *rvinterf_lopt, *rvinterf_wopt;
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c, sopt = 0;
+	char command[512];
+
+	while ((c = getopt(argc, argv, "B:l:p:s:w:")) != EOF)
+		switch (c) {
+		case 'B':
+			rvinterf_Bopt = optarg;
+			continue;
+		case 'l':
+			rvinterf_lopt = optarg;
+			continue;
+		case 'p':
+			rvinterf_ttyport = optarg;
+			continue;
+		case 's':
+			socket_pathname = optarg;
+			sopt++;
+			continue;
+		case 'w':
+			rvinterf_wopt = optarg;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: %s [options] [command]\n", argv[0]);
+			exit(ERROR_USAGE);
+		}
+	if (rvinterf_ttyport) {
+		if (sopt) {
+			fprintf(stderr,
+			"%s error: -p and -s options are mutually exclusive\n",
+				argv[0]);
+			exit(ERROR_USAGE);
+		}
+		launch_rvinterf();
+	} else {
+		if (rvinterf_Bopt || rvinterf_lopt || rvinterf_wopt) {
+			fprintf(stderr,
+"%s error: -B, -l and -w options are meaningful only when launching rvinterf\n",
+				argv[0]);
+			exit(ERROR_USAGE);
+		}
+		connect_local_socket();
+	}
+
+	setlinebuf(stdout);
+	if (argv[optind])
+		return dispatch_ready_argv(argc - optind, argv + optind);
+	for (;;) {
+		if (isatty(0)) {
+			rx_control(0);
+			fputs("fsio> ", stdout);
+			fflush(stdout);
+		}
+		if (!fgets(command, sizeof command, stdin))
+			exit(0);
+		parse_and_dispatch_cmd(command, 0);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fsmisc.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,149 @@
+/*
+ * Miscellaneous (dangerous!) FFS2 operations
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "ffslimits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "exitcodes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+cmd_format(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: argument exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_FORMAT;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	/* magic is 0x2BAD, 16-bit little-endian */
+	*dp++ = 0xAD;
+	*dp++ = 0x2B;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg_len != 5) {
+		printf("error: TMFFS_FORMAT response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[3]) {
+		report_ffs_err("format", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
+
+cmd_preformat()
+{
+	u_char cmdpkt[6];
+	int rc;
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_PREFORMAT;
+	/* magic is 0xDEAD, 16-bit little-endian */
+	cmdpkt[3] = 0xAD;
+	cmdpkt[4] = 0xDE;
+	rc = etm_pkt_exch(cmdpkt, 4);
+	if (rc)
+		return(rc);
+	if (rvi_msg_len != 5) {
+		printf("error: TMFFS_PREFORMAT response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[3]) {
+		report_ffs_err("preformat", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
+
+cmd_set_imeisv(argc, argv)
+	char **argv;
+{
+	char *filename, *cp, digits[16];
+	u_char bytes[8];
+	int pcm_order, i;
+
+	if (!strcmp(argv[1], "pcm")) {
+		filename = "/pcm/IMEI";
+		pcm_order = 1;
+	} else if (!strcmp(argv[1], "fc")) {
+		filename = "/etc/IMEISV";
+		pcm_order = 0;
+	} else {
+		fprintf(stderr,
+	"error: IMEISV storage type argument must be \"pcm\" or \"fc\"\n");
+		return(ERROR_USAGE);
+	}
+	cp = argv[2];
+	if (!isdigit(*cp)) {
+inv:		fprintf(stderr,
+			"error: 2nd argument must have 16 decimal digits\n");
+		return(ERROR_USAGE);
+	}
+	for (i = 0; i < 16; i++) {
+		if (ispunct(*cp))
+			cp++;
+		if (!isdigit(*cp))
+			goto inv;
+		digits[i] = *cp++ - '0';
+	}
+	if (*cp)
+		goto inv;
+	for (i = 0; i < 8; i++)
+		bytes[i] = pcm_order ? digits[i*2+1] << 4 | digits[i*2]
+				     : digits[i*2] << 4 | digits[i*2+1];
+	printf("Writing \"%02X %02X %02X %02X %02X %02X %02X %02X\" into %s\n",
+		bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
+		bytes[6], bytes[7], filename);
+	return do_short_fwrite(filename, bytes, 8);
+}
+
+cmd_set_pcm_string(argc, argv)
+	char **argv;
+{
+	char filename[16];
+
+	if (strcmp(argv[1], "CGMI") && strcmp(argv[1], "CGMM") &&
+	    strcmp(argv[1], "CGMR") && strcmp(argv[1], "CGSN")) {
+		fprintf(stderr,
+		"error: \"%s\" is not a recognized PCM string file name\n",
+			argv[1]);
+		return(ERROR_USAGE);
+	}
+	sprintf(filename, "/pcm/%s", argv[1]);
+	if (strlen(argv[2]) > 20) {
+		fprintf(stderr,
+			"error: %s string may not exceed 20 characters\n",
+			filename);
+		return(ERROR_USAGE);
+	}
+	return do_short_fwrite(filename, argv[2], strlen(argv[2]));
+}
+
+cmd_set_rfcap(argc, argv)
+	char **argv;
+{
+	return set_rfcap(argv[1]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fspath.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,70 @@
+/*
+ * FFS pathname manipulation functions
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "ffs.h"
+#include "limits.h"
+#include "ffslimits.h"
+#include "exitcodes.h"
+
+validate_ffs_pathname(cand)
+	char *cand;
+{
+	char *cp;
+	int depth, c;
+
+	cp = cand;
+	if (*cp++ != '/') {
+		fprintf(stderr, "error: FFS pathnames must be absolute\n");
+		return(-1);
+	}
+	for (depth = 0; *cp; depth++) {
+		if (*cp == '/') {
+			fprintf(stderr,
+		"error: FFS pathname must not contain duplicate slashes\n");
+			return(-1);
+		}
+		for (c = 0; *cp && *cp != '/'; cp++)
+			c++;
+		if (c > MAX_FN_COMPONENT) {
+			fprintf(stderr,
+				"error: FFS pathname component is too long\n");
+			return(-1);
+		}
+		if (!*cp)
+			continue;
+		cp++;
+		if (!*cp) {
+			fprintf(stderr,
+		"error: FFS pathname must not end with a trailing slash\n");
+			return(-1);
+		}
+	}
+	if (depth > MAX_NAME_DEPTH) {
+		fprintf(stderr, "error: FFS pathname exceeds depth limit\n");
+		return(-1);
+	}
+	return(depth);
+}
+
+char *
+pathname_for_ffs_child(parent, childbuf)
+	char *parent, *childbuf;
+{
+	int depth;
+	char *cp;
+
+	depth = validate_ffs_pathname(parent);
+	if (depth < 0 || depth >= MAX_NAME_DEPTH)
+		return(0);
+	strcpy(childbuf, parent);
+	cp = index(childbuf, '\0');
+	if (depth)
+		*cp++ = '/';
+	return(cp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fsread.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,291 @@
+/*
+ * Commands for reading the content of a GSM device's file system
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "ffslimits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "exitcodes.h"
+
+extern char *pathname_for_ffs_child();
+
+void
+ll_print_line(pathname, stat)
+	char *pathname;
+	struct stat_info *stat;
+{
+	char readonly;
+	char rlbuf[256];
+
+	if (stat->flags & OF_READONLY)
+		readonly = 'r';
+	else
+		readonly = ' ';
+	switch (stat->type) {
+	case OT_FILE:
+		printf("f%c %7u %s\n", readonly, stat->size, pathname);
+		return;
+	case OT_DIR:
+		printf("d%c         %s\n", readonly, pathname);
+		return;
+	case OT_LINK:
+		if (do_readlink_sancheck(pathname, rlbuf))
+			strcpy(rlbuf, "<invalid>");
+		printf("l%c         %s -> %s\n", readonly, pathname, rlbuf);
+		return;
+	default:
+		printf("?%c         %s\n", readonly, pathname);
+	}
+}
+
+cmd_ll(argc, argv)
+	char **argv;
+{
+	struct stat_info stat;
+	u_char rdstate[4];
+	char rdbuf[MAX_FN_COMPONENT+1], childpath[MAX_FULL_PATHNAME+1], *childp;
+	int nument, i, rc;
+
+	if (validate_ffs_pathname(argv[1]) < 0)
+		return(ERROR_USAGE);	/* err msg already printed */
+	rc = do_xlstat(argv[1], &stat);
+	if (rc)
+		return(rc);
+	if (stat.type != OT_DIR) {
+		ll_print_line(argv[1], &stat);
+		return(0);
+	}
+	rc = do_opendir(argv[1], rdstate, &nument);
+	if (rc)
+		return(rc);
+	if (!nument) {
+		printf("<empty dir>\n");
+		return(0);
+	}
+	childp = pathname_for_ffs_child(argv[1], childpath);
+	if (!childp) {
+		printf("error: non-empty dir at the limit of pathname depth\n");
+		return(ERROR_TARGET);
+	}
+	for (i = 0; i < nument; i++) {
+		rc = do_readdir(rdstate, rdbuf, MAX_FN_COMPONENT+1);
+		if (rc)
+			return(rc);
+		if (index(rdbuf, '/')) {
+			printf("error: readdir result contains a slash\n");
+			return(ERROR_TARGET);
+		}
+		strcpy(childp, rdbuf);
+		rc = do_xlstat(childpath, &stat);
+		if (rc) {
+			printf("xlstat failed on %s\n", childpath);
+			return(rc);
+		}
+		ll_print_line(childpath, &stat);
+	}
+	return(0);
+}
+
+void
+hexdump_line(offset, buf, len)
+	u_char *buf;
+{
+	int i, c;
+
+	printf("%02X:  ", offset);
+	for (i = 0; i < 16; i++) {
+		if (i < len)
+			printf("%02X ", buf[i]);
+		else
+			fputs("   ", stdout);
+		if (i == 7 || i == 15)
+			putchar(' ');
+	}
+	for (i = 0; i < len; i++) {
+		c = buf[i];
+		if (c < ' ' || c > '~')
+			c = '.';
+		putchar(c);
+	}
+	putchar('\n');
+}
+
+cmd_hd(argc, argv)
+	char **argv;
+{
+	u_char databuf[MAX_READ_DATA];
+	int rc, sz, off, l;
+
+	rc = do_file_read(argv[1], databuf, MAX_READ_DATA, &sz);
+	if (rc)
+		return(rc);
+	printf("%d bytes read\n", sz);
+	for (off = 0; off < sz; off += 16) {
+		l = sz - off;
+		if (l > 16)
+			l = 16;
+		hexdump_line(off, databuf + off, l);
+	}
+	return(0);
+}
+
+cpout_object(ffspath, hostpath)
+	char *ffspath, *hostpath;
+{
+	struct stat_info stat;
+	int rc;
+
+	rc = do_xlstat(ffspath, &stat);
+	if (rc)
+		return(rc);
+	switch (stat.type) {
+	case OT_FILE:
+		return cpout_file(ffspath, hostpath);
+	case OT_DIR:
+		return cpout_dir(ffspath, hostpath);
+	case OT_LINK:
+		printf("skipping FFS symlink %s\n", ffspath);
+		return(0);
+	default:
+		printf("error: stat returned bad objtype for %s\n", ffspath);
+		return(ERROR_TARGET);
+	}
+}
+
+cpout_file(ffspath, hostpath)
+	char *ffspath, *hostpath;
+{
+	int tfd;
+	FILE *of;
+	u_char buf[MAX_READ_DATA];
+	int rc, sz;
+
+	printf("copying %s\n", ffspath);
+	rc = fd_open(ffspath, FFS_O_RDONLY, &tfd);
+	if (rc)
+		return(rc);
+	of = fopen(hostpath, "w");
+	if (!of) {
+		perror(hostpath);
+		fd_close(tfd);
+		return(ERROR_UNIX);
+	}
+	for (;;) {
+		rc = fd_read(tfd, buf, MAX_READ_DATA, &sz);
+		if (rc) {
+			fd_close(tfd);
+			fclose(of);
+			return(rc);
+		}
+		if (!sz)
+			break;
+		fwrite(buf, 1, sz, of);
+	}
+	fclose(of);
+	return fd_close(tfd);
+}
+
+host_mkdir(pathname)
+	char *pathname;
+{
+	int rc;
+	struct stat st;
+
+	rc = stat(pathname, &st);
+	if (rc < 0) {
+		rc = mkdir(pathname, 0777);
+		if (rc < 0) {
+			perror(pathname);
+			return(ERROR_UNIX);
+		}
+		return(0);
+	} else {
+		if (S_ISDIR(st.st_mode))
+			return(0);
+		else {
+			fprintf(stderr,
+			"error: %s already exists and is not a directory\n",
+				pathname);
+			return(ERROR_UNIX);
+		}
+	}
+}
+
+cpout_dir(ffspath_dir, hostpath_dir)
+	char *ffspath_dir, *hostpath_dir;
+{
+	u_char rdstate[4];
+	char rdbuf[MAX_FN_COMPONENT+1], ffspath_child[MAX_FULL_PATHNAME+1];
+	char *childp;
+	char hostpath_child[MAXPATHLEN];
+	int nument, i, rc, childerr;
+
+	printf("dir %s\n", ffspath_dir);
+	rc = host_mkdir(hostpath_dir);
+	if (rc)
+		return(rc);
+	rc = do_opendir(ffspath_dir, rdstate, &nument);
+	if (rc)
+		return(rc);
+	if (!nument)
+		return(0);
+	childp = pathname_for_ffs_child(ffspath_dir, ffspath_child);
+	if (!childp) {
+		printf("error: non-empty dir at the limit of pathname depth\n");
+		return(ERROR_TARGET);
+	}
+	childerr = 0;
+	for (i = 0; i < nument; i++) {
+		rc = do_readdir(rdstate, rdbuf, MAX_FN_COMPONENT+1);
+		if (rc)
+			return(rc);
+		if (index(rdbuf, '/')) {
+			printf("error: readdir result contains a slash\n");
+			return(ERROR_TARGET);
+		}
+		strcpy(childp, rdbuf);
+		if (rdbuf[0] == '.') {
+			printf("skipping %s\n", ffspath_child);
+			continue;
+		}
+		if (strlen(hostpath_dir) + strlen(rdbuf) + 2 >
+		    sizeof hostpath_child) {
+			fprintf(stderr,
+				"error: host side pathname buffer overflow\n");
+			return(ERROR_UNIX);
+		}
+		sprintf(hostpath_child, "%s/%s", hostpath_dir, rdbuf);
+		rc = cpout_object(ffspath_child, hostpath_child);
+		if (rc && rc != ERROR_TARGET)
+			return(rc);
+		if (rc)
+			childerr = rc;
+	}
+	return(childerr);
+}
+
+cmd_cpout(argc, argv)
+	char **argv;
+{
+	if (validate_ffs_pathname(argv[1]) < 0)
+		return(ERROR_USAGE);	/* err msg already printed */
+	return cpout_object(argv[1], argv[2]);
+}
+
+cmd_cpout_file(argc, argv)
+	char **argv;
+{
+	return cpout_file(argv[1], argv[2]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fsupload.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,146 @@
+/*
+ * upload-fs implementation
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "ffserr.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "ffslimits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "exitcodes.h"
+
+uploadfs_level(srcpath, depth, prefix)
+	char *srcpath, *prefix;
+{
+	char ffs_childpath[MAX_FULL_PATHNAME+1], *ffs_childp;
+	DIR *rdd;
+	struct dirent *dirent;
+	char hostpath_child[MAXPATHLEN];
+	struct stat hst;
+	int rc;
+
+	strcpy(ffs_childpath, prefix);
+	ffs_childp = index(ffs_childpath, '\0');
+	*ffs_childp++ = '/';
+	rdd = opendir(srcpath);
+	if (!rdd) {
+		perror(srcpath);
+		return(ERROR_UNIX);
+	}
+	while (dirent = readdir(rdd)) {
+		if (dirent->d_name[0] == '.')
+			continue;
+		if (strlen(dirent->d_name) > MAX_FN_COMPONENT) {
+			fprintf(stderr,
+		"error: \"%s\" in %s exceeds the FFS component name limit\n",
+				dirent->d_name, srcpath);
+			closedir(rdd);
+			return(ERROR_USAGE);
+		}
+		if (strlen(srcpath) + strlen(dirent->d_name) + 2 >
+		    sizeof hostpath_child) {
+			fprintf(stderr,
+				"error: host side pathname buffer overflow\n");
+			closedir(rdd);
+			return(ERROR_UNIX);
+		}
+		sprintf(hostpath_child, "%s/%s", srcpath, dirent->d_name);
+		if (lstat(hostpath_child, &hst) < 0) {
+			perror(hostpath_child);
+			closedir(rdd);
+			return(ERROR_UNIX);
+		}
+		strcpy(ffs_childp, dirent->d_name);
+		switch (hst.st_mode & S_IFMT) {
+		case S_IFREG:
+			printf("uploading %s\n", ffs_childpath);
+			rc = fwrite_from_file(ffs_childpath, hostpath_child);
+			if (rc) {
+				closedir(rdd);
+				return(rc);
+			}
+			break;
+		case S_IFDIR:
+			if (depth >= MAX_NAME_DEPTH-1) {
+				fprintf(stderr,
+				"error: directory nesting too deep at %s\n",
+					hostpath_child);
+				closedir(rdd);
+				return(ERROR_USAGE);
+			}
+			printf("mkdir %s\n", ffs_childpath);
+			rc = do_mkdir_existok(ffs_childpath);
+			if (rc) {
+				closedir(rdd);
+				return(rc);
+			}
+			rc = uploadfs_level(hostpath_child, depth + 1,
+						ffs_childpath);
+			if (rc) {
+				closedir(rdd);
+				return(rc);
+			}
+			break;
+		default:
+			fprintf(stderr,
+			"error: %s is neither a regular file nor a directory\n",
+				hostpath_child);
+			closedir(rdd);
+			return(ERROR_USAGE);
+		}
+	}
+	closedir(rdd);
+	return(0);
+}
+
+cmd_uploadfs(argc, argv)
+	char **argv;
+{
+	return uploadfs_level(argv[1], 0, "");
+}
+
+cmd_upload_file(argc, argv)
+	char **argv;
+{
+	if (strlen(argv[2]) >= TMFFS_STRING_SIZE) {
+		fprintf(stderr,
+			"error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	return fwrite_from_file(argv[2], argv[1]);
+}
+
+cmd_upload_subtree(argc, argv)
+	char **argv;
+{
+	int rc, depth;
+
+	depth = validate_ffs_pathname(argv[2]);
+	if (depth < 0)
+		return(ERROR_USAGE);	/* error msg already printed */
+	if (depth == 0) {
+		fprintf(stderr, "please use upload-fs command instead\n");
+		return(ERROR_USAGE);
+	}
+	if (depth >= MAX_NAME_DEPTH) {
+		fprintf(stderr, "cannot upload into max-depth directory\n");
+		return(ERROR_USAGE);
+	}
+	printf("mkdir %s\n", argv[2]);
+	rc = do_mkdir_existok(argv[2]);
+	if (rc)
+		return(rc);
+	return uploadfs_level(argv[1], depth, argv[2]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/fswrite.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,163 @@
+/*
+ * FFS write operation commands
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "ffserr.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "exitcodes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+cmd_mkdir(argc, argv)
+	char **argv;
+{
+	return do_mkdir_existok(argv[1]);
+}
+
+cmd_delete(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_REMOVE;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg_len != 5) {
+		printf("error: TMFFS_REMOVE response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[3]) {
+		report_ffs_err("ffs_remove", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
+
+hexdigit(c)
+{
+	if (isdigit(c))
+		return(c - '0');
+	else if (isupper(c))
+		return(c - 'A' + 10);
+	else
+		return(c - 'a' + 10);
+}
+
+fwrite_hex_string(pathname, strarg)
+	char *pathname, *strarg;
+{
+	u_char buf[256];
+	int maxlen, len;
+	char *cp;
+
+	maxlen = max_short_file_write(pathname);
+	for (cp = strarg, len = 0; ; cp += 2) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp)
+			break;
+		if (!isxdigit(cp[0]) || !isxdigit(cp[1])) {
+			fprintf(stderr, "error: invalid hex string argument\n");
+			return(ERROR_USAGE);
+		}
+		if (len >= maxlen) {
+			fprintf(stderr,
+			"error: hex string exceeds write packet limit\n");
+			return(ERROR_USAGE);
+		}
+		buf[len++] = hexdigit(cp[0]) << 4 | hexdigit(cp[1]);
+	}
+	return do_short_fwrite(pathname, buf, len);
+}
+
+fwrite_from_file(pathname, srcfile)
+	char *pathname, *srcfile;
+{
+	u_char buf[240];
+	FILE *srcf;
+	int rc, cc, first, tfd;
+
+	srcf = fopen(srcfile, "r");
+	if (!srcf) {
+		perror(srcfile);
+		return(ERROR_UNIX);
+	}
+	for (first = 1; cc = fread(buf, 1, sizeof buf, srcf); first = 0) {
+		if (first) {
+			if (cc < sizeof buf &&
+			    cc <= max_short_file_write(pathname)) {
+				fclose(srcf);
+				return do_short_fwrite(pathname, buf, cc);
+			}
+			rc = fd_open(pathname,
+				     FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC,
+				     &tfd);
+			if (rc) {
+				fclose(srcf);
+				return(rc);
+			}
+		}
+		rc = fd_write(tfd, buf, cc);
+		if (rc) {
+			fclose(srcf);
+			fd_close(tfd);
+			return(rc);
+		}
+	}
+	fclose(srcf);
+	if (first) {
+		/* 0 length file: do an open-for-write to create it */
+		rc = fd_open(pathname,
+				FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC,
+				&tfd);
+		if (rc)
+			return(rc);
+	}
+	return fd_close(tfd);
+}
+
+cmd_fwrite(argc, argv)
+	char **argv;
+{
+	if (strlen(argv[1]) >= TMFFS_STRING_SIZE) {
+		fprintf(stderr,
+			"error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	if (!strcmp(argv[2], "ascii"))
+		return do_short_fwrite(argv[1], argv[3], strlen(argv[3]));
+	else if (!strcmp(argv[2], "hex"))
+		return fwrite_hex_string(argv[1], argv[3]);
+	else if (!strcmp(argv[2], "file"))
+		return fwrite_from_file(argv[1], argv[3]);
+	else {
+		fprintf(stderr,
+"error: middle argument to fwrite cmd must be \"ascii\", \"hex\" or \"file\"\n"
+			);
+		return(ERROR_USAGE);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/interf.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,156 @@
+/*
+ * In this module we implement our synchronous interface to the target
+ * via rvinterf.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "limits.h"
+#include "localsock.h"
+#include "pktmux.h"
+#include "exitcodes.h"
+
+extern int sock;
+
+int rx_enable_state;
+u_char rvi_msg[LOCALSOCK_MAX_MSG];
+int rvi_msg_len;
+
+static void
+collect_bytes_from_rvi(buf, nbytes)
+	u_char *buf;
+{
+	int cc;
+
+	while (nbytes) {
+		cc = read(sock, buf, nbytes);
+		if (cc <= 0) {
+			perror("read from rvinterf socket");
+			exit(ERROR_RVINTERF);
+		}
+		buf += cc;
+		nbytes -= cc;
+	}
+}
+
+collect_rvi_msg()
+{
+	u_char lenbuf[2];
+
+	collect_bytes_from_rvi(lenbuf, 2);
+	rvi_msg_len = lenbuf[0] << 8 | lenbuf[1];
+	if (rvi_msg_len < 1 || rvi_msg_len > LOCALSOCK_MAX_MSG) {
+		fprintf(stderr, "Invalid length from rvinterf: %02X%02X\n",
+			lenbuf[0], lenbuf[1]);
+		exit(ERROR_RVINTERF);
+	}
+	collect_bytes_from_rvi(rvi_msg, rvi_msg_len);
+	return(0);
+}
+
+send_rvimisc_command(cmdpkt, cmdlen)
+	u_char *cmdpkt;
+{
+	u_char lenbuf[2];
+
+	lenbuf[0] = 0;
+	lenbuf[1] = cmdlen;
+	write(sock, lenbuf, 2);
+	write(sock, cmdpkt, cmdlen);
+}
+
+rx_control(enable)
+{
+	u_char cmdpkt[2];
+	int cmdlen;
+
+	/* are we already in the desired state? */
+	if (rx_enable_state == enable)
+		return(0);
+	/* no, do the work */
+	if (enable) {
+		cmdpkt[0] = CLI2RVI_WANT_MUXPROTO;
+		cmdpkt[1] = RVT_TM_HEADER;
+		cmdlen = 2;
+	} else {
+		cmdpkt[0] = CLI2RVI_RESET_PACKET_RX;
+		cmdlen = 1;
+	}
+	send_rvimisc_command(cmdpkt, cmdlen);
+	collect_rvi_msg();
+	if (rvi_msg[0] != RVI2CLI_LOCAL_CMD_RESP || rvi_msg_len < 2) {
+		fprintf(stderr,
+		"error: unexpected response to rvinterf local command\n");
+		exit(ERROR_RVINTERF);
+	}
+	if (rvi_msg[1] != '+') {
+		fprintf(stderr, "Error from rvinterf: %.*s\n", rvi_msg_len - 1,
+			rvi_msg + 1);
+		exit(ERROR_RVINTERF);
+	}
+	rx_enable_state = enable;
+	return(0);
+}
+
+send_pkt_to_target(pkt, pktlen)
+	u_char *pkt;
+{
+	u_char hdrbuf[3];
+	int len1;
+
+	len1 = pktlen + 1;
+	hdrbuf[0] = len1 >> 8;
+	hdrbuf[1] = len1 & 0xFF;
+	hdrbuf[2] = CLI2RVI_PKT_TO_TARGET;
+	write(sock, hdrbuf, 3);
+	write(sock, pkt, pktlen);
+}
+
+target_pkt_exch(outpkt, outpktlen)
+	u_char *outpkt;
+{
+	rx_control(1);
+	send_pkt_to_target(outpkt, outpktlen);
+	collect_rvi_msg();
+	if (rvi_msg[0] != RVI2CLI_PKT_FROM_TARGET) {
+		fprintf(stderr,
+			"error: unexpected response type from rvinterf\n");
+		exit(ERROR_RVINTERF);
+	}
+	return(0);
+}
+
+etm_pkt_exch(outbuf, outlen)
+	u_char *outbuf;
+{
+	int i, c;
+
+	outbuf[0] = RVT_TM_HEADER;
+	c = 0;
+	for (i = 1; i <= outlen; i++)
+		c ^= outbuf[i];
+	outbuf[i] = c;
+	target_pkt_exch(outbuf, outlen + 2);
+	if (rvi_msg[1] != RVT_TM_HEADER) {
+		printf("error: packet from target is not ETM!\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len < 5) {
+		printf("error: ETM response packet is too short\n");
+		return(ERROR_TARGET);
+	}
+	c = 0;
+	for (i = 2; i < rvi_msg_len; i++)
+		c ^= rvi_msg[i];
+	if (c) {
+		printf("ETM response checksum error!\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[2] != outbuf[1]) {
+		printf("error: target response is from wrong ETM component\n");
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/launchrvif.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,63 @@
+/*
+ * This module implements the optional "behind the scenes" invokation
+ * of rvinterf from fc-fsio etc.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "exitcodes.h"
+
+static char rvinterf_pathname[] = "/usr/local/bin/rvinterf";
+
+extern int sock;
+
+char *rvinterf_ttyport, *rvinterf_Bopt, *rvinterf_lopt, *rvinterf_wopt;
+
+launch_rvinterf()
+{
+	int sp[2], rc;
+	char *rvif_argv[11], Sarg[16], **ap;
+
+	rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sp);
+	if (rc < 0) {
+		perror("socketpair");
+		exit(ERROR_UNIX);
+	}
+	sock = sp[0];
+	sprintf(Sarg, "-S%d", sp[1]);
+	ap = rvif_argv;
+	*ap++ = "rvinterf";
+	*ap++ = Sarg;
+	*ap++ = "-n";
+	if (rvinterf_Bopt) {
+		*ap++ = "-B";
+		*ap++ = rvinterf_Bopt;
+	}
+	if (rvinterf_lopt) {
+		*ap++ = "-l";
+		*ap++ = rvinterf_lopt;
+	}
+	if (rvinterf_wopt) {
+		*ap++ = "-w";
+		*ap++ = rvinterf_wopt;
+	}
+	*ap++ = rvinterf_ttyport;
+	*ap = 0;
+	rc = vfork();
+	if (rc < 0) {
+		perror("vfork for launching rvinterf");
+		exit(ERROR_UNIX);
+	}
+	if (!rc) {
+		/* we are in the child - do the exec */
+		close(sp[0]);
+		execv(rvinterf_pathname, rvif_argv);
+		perror(rvinterf_pathname);
+		_exit(1);
+	}
+	close(sp[1]);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/localstruct.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,17 @@
+/*
+ * The struct defined below captures the results of a stat (actually xlstat)
+ * operation on the target; it is a host (aka local) struct, with host byte
+ * ordering, alignment and data types.
+ */
+
+struct stat_info {
+	u8	type;
+	u8	flags;
+	int	inode;
+	u32	size;     // size of data space occupied by object
+	u32	space;    // size of physical data space occupied by object
+	u32	location;
+	u8	block;
+	u16	sequence;
+	u16	updates;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/memcmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,94 @@
+/*
+ * User commands for reading memory regions and Calypso die ID via ETM
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "tm3.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "exitcodes.h"
+
+static void
+memdump_line(addr, buf, len)
+	u32 addr;
+	u_char *buf;
+{
+	int i, c;
+
+	printf("%08X:  ", addr);
+	for (i = 0; i < 16; i++) {
+		if (i < len)
+			printf("%02X ", buf[i]);
+		else
+			fputs("   ", stdout);
+		if (i == 7 || i == 15)
+			putchar(' ');
+	}
+	for (i = 0; i < len; i++) {
+		c = buf[i];
+		if (c < ' ' || c > '~')
+			c = '.';
+		putchar(c);
+	}
+	putchar('\n');
+}
+
+cmd_memdump(argc, argv)
+	char **argv;
+{
+	u_char databuf[MAX_MEMREAD_BYTES];
+	u32 memaddr;
+	int rc, sz, off, l;
+
+	memaddr = strtoul(argv[1], 0, 16);
+	sz = strtoul(argv[2], 0, 16);
+	rc = do_memory_read(memaddr, databuf, sz);
+	if (rc)
+		return(rc);
+	for (off = 0; off < sz; off += 16) {
+		l = sz - off;
+		if (l > 16)
+			l = 16;
+		memdump_line(memaddr + off, databuf + off, l);
+	}
+	return(0);
+}
+
+cmd_omemdump(argc, argv)
+	char **argv;
+{
+	u_char databuf[TM3_MEMREAD_MAX];
+	u32 memaddr;
+	int rc, sz, off, l;
+
+	memaddr = strtoul(argv[1], 0, 16);
+	sz = strtoul(argv[2], 0, 16);
+	rc = do_memory_read_tm3(memaddr, databuf, sz);
+	if (rc)
+		return(rc);
+	for (off = 0; off < sz; off += 16) {
+		l = sz - off;
+		if (l > 16)
+			l = 16;
+		memdump_line(memaddr + off, databuf + off, l);
+	}
+	return(0);
+}
+
+cmd_dieid()
+{
+	u_char buf[8];
+	int rc;
+
+	rc = do_dieid_read(buf);
+	if (rc)
+		return(rc);
+	printf("%02X %02X %02X %02X %02X %02X %02X %02X\n", buf[0], buf[1],
+		buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/memops.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,166 @@
+/*
+ * Functions for reading memory regions and Calypso die ID via ETM
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "tm3.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "exitcodes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+do_memory_read(memaddr, databuf, nbytes)
+	u32 memaddr;
+	u_char *databuf;
+{
+	u_char cmdpkt[10];
+	int rc;
+
+	if (nbytes > MAX_MEMREAD_BYTES) {
+		printf("error: # of bytes to read may not exceed %d\n",
+			MAX_MEMREAD_BYTES);
+		return(ERROR_USAGE);
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x01;
+	cmdpkt[4] = nbytes;
+	cmdpkt[5] = memaddr;
+	cmdpkt[6] = memaddr >> 8;
+	cmdpkt[7] = memaddr >> 16;
+	cmdpkt[8] = memaddr >> 24;
+	rc = etm_pkt_exch(cmdpkt, 8);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		printf("ETM error response to mem read request: 0x%02X\n",
+			rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != nbytes + 7) {
+		printf("error: mem read response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[4] != TMCORE_OPC_MEM || rvi_msg[5] != 0x01) {
+		printf("error: mem read response has wrong opcode\n");
+		return(ERROR_TARGET);
+	}
+	bcopy(rvi_msg + 6, databuf, nbytes);
+	return(0);
+}
+
+do_memory_read_16(memaddr, databuf, nwords)
+	u32 memaddr;
+	u_char *databuf;
+{
+	u_char cmdpkt[10];
+	int rc;
+
+	if (nwords > MAX_MEMREAD_16BIT) {
+		printf("error: # of 16-bit words to read may not exceed %d\n",
+			MAX_MEMREAD_16BIT);
+		return(ERROR_USAGE);
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x02;
+	cmdpkt[4] = nwords;
+	cmdpkt[5] = memaddr;
+	cmdpkt[6] = memaddr >> 8;
+	cmdpkt[7] = memaddr >> 16;
+	cmdpkt[8] = memaddr >> 24;
+	rc = etm_pkt_exch(cmdpkt, 8);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		printf("ETM error response to mem read 16 request: 0x%02X\n",
+			rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != nwords * 2 + 7) {
+		printf("error: mem read 16 response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[4] != TMCORE_OPC_MEM || rvi_msg[5] != 0x02) {
+		printf("error: mem read 16 response has wrong opcode\n");
+		return(ERROR_TARGET);
+	}
+	bcopy(rvi_msg + 6, databuf, nwords * 2);
+	return(0);
+}
+
+do_memory_read_tm3(memaddr, databuf, nbytes)
+	u32 memaddr;
+	u_char *databuf;
+{
+	u_char cmdpkt[11];
+	int rc;
+
+	if (nbytes > TM3_MEMREAD_MAX) {
+		printf("error: # of bytes to read may not exceed %d\n",
+			TM3_MEMREAD_MAX);
+		return(ERROR_USAGE);
+	}
+	cmdpkt[1] = MEM_READ;
+	cmdpkt[2] = memaddr;
+	cmdpkt[3] = memaddr >> 8;
+	cmdpkt[4] = memaddr >> 16;
+	cmdpkt[5] = memaddr >> 24;
+	cmdpkt[6] = nbytes;
+	cmdpkt[7] = 0;
+	cmdpkt[8] = 0;
+	cmdpkt[9] = 0;
+	rc = etm_pkt_exch(cmdpkt, 9);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		printf("TM3 error response to mem read request: 0x%02X\n",
+			rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != nbytes + 9) {
+		printf("error: mem read response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[4] != nbytes || rvi_msg[5] || rvi_msg[6] || rvi_msg[7]) {
+		printf("error: mem read response has wrong length echo\n");
+		return(ERROR_TARGET);
+	}
+	bcopy(rvi_msg + 8, databuf, nbytes);
+	return(0);
+}
+
+do_dieid_read(databuf)
+	u_char *databuf;
+{
+	u_char cmdpkt[4];
+	int rc;
+
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_DIEID;
+	rc = etm_pkt_exch(cmdpkt, 2);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		printf("ETM error response to die ID read request: 0x%02X\n",
+			rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len != 14) {
+		printf("error: die ID read response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[4] != TMCORE_OPC_DIEID) {
+		printf("error: die ID read response has wrong opcode\n");
+		return(ERROR_TARGET);
+	}
+	bcopy(rvi_msg + 5, databuf, 8);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/olddump.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,55 @@
+/*
+ * This utility uses the old TM3 memory read command (in a synchronous manner
+ * using our etmsync infrastructure) to read the memory of a GSM device running
+ * a compatible fw version; it was written as an aid for reverse-engineering
+ * bootloader-locked Mot C139 fw versions.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "tm3.h"
+#include "localtypes.h"
+#include "exitcodes.h"
+
+#define	CHUNK_SIZE	TM3_MEMREAD_MAX
+
+single_op_main(argc, argv)
+	char **argv;
+{
+	u32 addr, len, chunk;
+	char buf[CHUNK_SIZE];
+	FILE *outf;
+	int rc;
+
+	if (argc != 3) {
+		fprintf(stderr,
+		"usage: fc-olddump [options] start-addr dump-length binfile\n");
+		exit(ERROR_USAGE);
+	}
+	addr = strtoul(argv[0], 0, 16);
+	len = strtoul(argv[1], 0, 16);
+	outf = fopen(argv[2], "w");
+	if (!outf) {
+		perror(argv[2]);
+		exit(ERROR_UNIX);
+	}
+	while (len) {
+		chunk = len;
+		if (chunk > CHUNK_SIZE)
+			chunk = CHUNK_SIZE;
+		rc = do_memory_read_tm3(addr, buf, chunk);
+		if (rc)
+			exit(rc);
+		fwrite(buf, 1, chunk, outf);
+		putchar('.');
+		fflush(stdout);
+		addr += chunk;
+		len -= chunk;
+	}
+	putchar('\n');
+	fclose(outf);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/pirhackinit.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,160 @@
+/*
+ * This fc-pirhackinit utility is highly specific to the TCS211-on-Pirelli
+ * exercise.  DO NOT run it against Pirelli's stock firmware, nor is it needed
+ * when using our full-source FreeCalypso firmware.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+#include "localtypes.h"
+#include "exitcodes.h"
+
+extern u_char pirelli_imeisv[8];
+
+write_pcm_imei()
+{
+	static char destfile[] = "/pcm/IMEI";
+	u_char swapped[8];
+	int i, d1, d2;
+
+	printf("Writing %s\n", destfile);
+	for (i = 0; i < 8; i++) {
+		d1 = pirelli_imeisv[i] >> 4;
+		d2 = pirelli_imeisv[i] & 0xF;
+		swapped[i] = (d2 << 4) | d1;
+	}
+	return do_short_fwrite(destfile, swapped, 8);
+}
+
+read_mem_region(memaddr, databuf, total_bytes)
+	u32 memaddr;
+	u_char *databuf;
+{
+	int chunk, remain, rc;
+
+	for (remain = total_bytes; remain; remain -= chunk) {
+		chunk = remain;
+		if (chunk > MAX_MEMREAD_BYTES)
+			chunk = MAX_MEMREAD_BYTES;
+		rc = do_memory_read(memaddr, databuf, chunk);
+		if (rc)
+			return(rc);
+		memaddr += chunk;
+		databuf += chunk;
+	}
+	return(0);
+}
+
+write_buf_to_file(pathname, data, datalen)
+	char *pathname;
+	u_char *data;
+{
+	int tfd, rc, chunk, remain;
+
+	if (datalen <= max_short_file_write(pathname))
+		return do_short_fwrite(pathname, data, datalen);
+	/* do it the long way */
+	rc = fd_open(pathname, FFS_O_WRONLY | FFS_O_CREATE | FFS_O_TRUNC, &tfd);
+	if (rc)
+		return(rc);
+	for (remain = datalen; remain; remain -= chunk) {
+		chunk = remain;
+		if (chunk > 240)
+			chunk = 240;
+		rc = fd_write(tfd, data, chunk);
+		if (rc) {
+			fd_close(tfd);
+			return(rc);
+		}
+		data += chunk;
+	}
+	return fd_close(tfd);
+}
+
+copy_calib_record(memaddr, pathname, size)
+	u32 memaddr;
+	char *pathname;
+	int size;
+{
+	u_char *buf;
+	int rc;
+
+	buf = malloc(size);
+	if (!buf) {
+		perror("malloc");
+		exit(ERROR_UNIX);
+	}
+	rc = read_mem_region(memaddr, buf, size);
+	if (rc) {
+		free(buf);
+		return(rc);
+	}
+	rc = write_buf_to_file(pathname, buf, size);
+	free(buf);
+	return(rc);
+}
+
+static struct calmap {
+	u32	offset;
+	int	size;
+	char	*ti_equiv;
+} pirelli_cal_map[] = {
+	{0x06E5, 36,  "/sys/adccal"},
+	{0x072B, 512, "/gsm/rf/tx/ramps.900"},
+	{0x092C, 128, "/gsm/rf/tx/levels.900"},
+	{0x09AD, 128, "/gsm/rf/tx/calchan.900"},
+	{0x0A2E, 512, "/gsm/rf/tx/ramps.1800"},
+	{0x0C2F, 128, "/gsm/rf/tx/levels.1800"},
+	{0x0CB0, 128, "/gsm/rf/tx/calchan.1800"},
+	{0x0D31, 512, "/gsm/rf/tx/ramps.1900"},
+	{0x0F32, 128, "/gsm/rf/tx/levels.1900"},
+	{0x0FB3, 128, "/gsm/rf/tx/calchan.1900"},
+	{0x10AF, 40,  "/gsm/rf/rx/calchan.900"},
+	{0x10D8, 8,   "/gsm/rf/rx/agcparams.900"},
+	{0x10E1, 40,  "/gsm/rf/rx/calchan.1800"},
+	{0x110A, 8,   "/gsm/rf/rx/agcparams.1800"},
+	{0x1113, 40,  "/gsm/rf/rx/calchan.1900"},
+	{0x113C, 8,   "/gsm/rf/rx/agcparams.1900"},
+	{0, 0, 0}
+};
+
+copy_calib_data()
+{
+	struct calmap *tp;
+	int rc;
+
+	printf("Copying calibration records to FFS\n");
+	for (tp = pirelli_cal_map; tp->size; tp++) {
+		rc = copy_calib_record(0x027F0000 + tp->offset, tp->ti_equiv,
+					tp->size);
+		if (rc)
+			return(rc);
+	}
+	return(0);
+}
+
+single_op_main()
+{
+	int rc;
+
+	rc = get_pirelli_imei();
+	if (rc)
+		return(rc);
+	printf("Creating TCS211 file system directories\n");
+	rc = create_std_dirs();
+	if (rc)
+		return(rc);
+	rc = write_pcm_imei();
+	if (rc)
+		return(rc);
+	rc = copy_calib_data();
+	if (rc)
+		return(rc);
+	return set_rfcap("tri900");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/pirimei.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,44 @@
+/*
+ * Reading and decryption of Pirelli's factory IMEI record
+ */
+
+#include <sys/types.h>
+#include <openssl/des.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "exitcodes.h"
+
+u_char pirelli_imeisv[8];
+
+get_pirelli_imei()
+{
+	DES_cblock ciphertext[2], dieid_key, decrypted[2];
+	DES_key_schedule keysched;
+	int rc;
+	static char failmsg[] =
+	"decryption failed: no valid IMEI record or incompatible firmware\n";
+
+	printf("Requesting Calypso die ID\n");
+	rc = do_dieid_read(dieid_key);
+	if (rc)
+		return(rc);
+	printf("Reading IMEI record in Pirelli's factory data block\n");
+	rc = do_memory_read(0x027F0504, ciphertext, 16);
+	if (rc)
+		return(rc);
+	DES_set_key_unchecked(&dieid_key, &keysched);
+	DES_ecb_encrypt(&ciphertext[0], &decrypted[0], &keysched, DES_DECRYPT);
+	DES_ecb_encrypt(&ciphertext[1], &decrypted[1], &keysched, DES_DECRYPT);
+	if (bcmp(decrypted[1], dieid_key, 8)) {
+		printf(failmsg);
+		return(ERROR_TARGET);
+	}
+	bcopy(decrypted[0], pirelli_imeisv, 8);
+	printf("Factory IMEISV is %02X%02X%02X%02X-%02X%02X%02X-%02X\n",
+		pirelli_imeisv[0], pirelli_imeisv[1], pirelli_imeisv[2],
+		pirelli_imeisv[3], pirelli_imeisv[4], pirelli_imeisv[5],
+		pirelli_imeisv[6], pirelli_imeisv[7]);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/pirimeimain.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,6 @@
+/* just a rather silly wrapper */
+
+single_op_main()
+{
+	return get_pirelli_imei();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/rfcap.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,51 @@
+/*
+ * Setting of /gsm/com/rfcap
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "exitcodes.h"
+
+static struct band_table {
+	char	*keyword;
+	u_char	bytes[4];
+} band_table[] = {
+	{"dual-eu",	{0x00, 0x0B, 0x41, 0x00}},
+	{"dual-us",	{0x00, 0x14, 0x00, 0x14}},
+	{"tri900",	{0x00, 0x0F, 0x41, 0x10}},
+	{"tri850",	{0x00, 0x16, 0x01, 0x14}},
+	{"quad",	{0x00, 0x1F, 0x41, 0x14}},
+	{0,		{0x00, 0x00, 0x00, 0x00}}
+};
+
+static u_char rfcap_tail[12] = {0x00, 0x00, 0x00, 0x00,
+				0x50, 0x00, 0x00, 0xA5,
+				0x05, 0x00, 0xC0, 0x00};
+
+set_rfcap(band_config_kw)
+	char *band_config_kw;
+{
+	static char filename[] = "/gsm/com/rfcap";
+	u_char bytes[16];
+	struct band_table *tp;
+
+	for (tp = band_table; tp->keyword; tp++)
+		if (!strcmp(tp->keyword, band_config_kw))
+			break;
+	if (!tp->keyword) {
+		printf("error: band configuration \"%s\" not known\n",
+			band_config_kw);
+		return(ERROR_USAGE);
+	}
+	bcopy(tp->bytes, bytes, 4);
+	bcopy(rfcap_tail, bytes + 4, 12);
+
+	printf("Writing \"%02X %02X %02X %02X %02X %02X %02X %02X  %02X %02X %02X %02X %02X %02X %02X %02X\" into %s\n",
+		bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
+		bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11],
+		bytes[12], bytes[13], bytes[14], bytes[15], filename);
+	return do_short_fwrite(filename, bytes, 16);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/simplemain.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,63 @@
+/*
+ * This module contains the main() function for simple etmsync programs
+ * that execute a single operation without a command dispatcher.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "exitcodes.h"
+
+extern char *socket_pathname;
+extern char *rvinterf_ttyport, *rvinterf_Bopt, *rvinterf_lopt, *rvinterf_wopt;
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c, sopt = 0;
+
+	while ((c = getopt(argc, argv, "B:l:p:s:w:")) != EOF)
+		switch (c) {
+		case 'B':
+			rvinterf_Bopt = optarg;
+			continue;
+		case 'l':
+			rvinterf_lopt = optarg;
+			continue;
+		case 'p':
+			rvinterf_ttyport = optarg;
+			continue;
+		case 's':
+			socket_pathname = optarg;
+			sopt++;
+			continue;
+		case 'w':
+			rvinterf_wopt = optarg;
+			continue;
+		case '?':
+		default:
+			/* error msg already printed */
+			exit(ERROR_USAGE);
+		}
+	if (rvinterf_ttyport) {
+		if (sopt) {
+			fprintf(stderr,
+			"%s error: -p and -s options are mutually exclusive\n",
+				argv[0]);
+			exit(ERROR_USAGE);
+		}
+		launch_rvinterf();
+	} else {
+		if (rvinterf_Bopt || rvinterf_lopt || rvinterf_wopt) {
+			fprintf(stderr,
+"%s error: -B, -l and -w options are meaningful only when launching rvinterf\n",
+				argv[0]);
+			exit(ERROR_USAGE);
+		}
+		connect_local_socket();
+	}
+
+	return single_op_main(argc - optind, argv + optind);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/stddirs.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,39 @@
+/*
+ * An automated way to create the standard set of FFS directories
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "exitcodes.h"
+
+static char *std_dir_list[] = {
+	"/gsm",
+	"/gsm/com",
+	"/gsm/rf",
+	"/gsm/rf/rx",
+	"/gsm/rf/tx",
+	"/pcm",
+	"/sys",
+	"/mmi",
+	"/var",
+	"/var/dbg",
+	"/aud",
+	"/etc",
+	0
+};
+
+create_std_dirs()
+{
+	char **dirp;
+	int rc;
+
+	for (dirp = std_dir_list; *dirp; dirp++) {
+		rc = do_mkdir_existok(*dirp);
+		if (rc)
+			return(rc);
+	}
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/etmsync/symlink.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,141 @@
+/*
+ * Commands for experimenting with FFS symlinks
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+#include "limits.h"
+#include "ffslimits.h"
+#include "localtypes.h"
+#include "localstruct.h"
+#include "exitcodes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+do_symlink(target, realobj)
+	char *target, *realobj;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, targlen, reallen;
+
+	reallen = strlen(realobj);
+	if (reallen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	targlen = strlen(target);
+	if (3 + (reallen+2) + (targlen+2) + 1 > MAX_PKT_TO_TARGET) {
+		printf("error: symlink request fails to fit into packet\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_SYMLINK;
+	*dp++ = reallen + 1;
+	strcpy(dp, realobj);
+	dp += reallen + 1;
+	*dp++ = targlen + 1;
+	strcpy(dp, target);
+	dp += targlen + 1;
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg_len != 5) {
+		printf("error: TMFFS_SYMLINK response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg[3]) {
+		report_ffs_err("symlink", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
+
+cmd_symlink(argc, argv)
+	char **argv;
+{
+	return do_symlink(argv[1], argv[2]);
+}
+
+do_readlink(pathname, databuf, rdret)
+	char *pathname;
+	u_char *databuf;
+	int *rdret;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int rc, slen, sz;
+
+	slen = strlen(pathname);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return(ERROR_USAGE);
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_READLINK;
+	*dp++ = slen + 1;
+	strcpy(dp, pathname);
+	dp += slen + 1;
+	*dp++ = 0;	/* dummy 2nd buffer */
+	rc = etm_pkt_exch(cmdpkt, dp - cmdpkt - 1);
+	if (rc)
+		return(rc);
+	if (rvi_msg[3]) {
+		report_ffs_err("readlink", rvi_msg[3]);
+		return(ERROR_TARGET);
+	}
+	if (rvi_msg_len < 6) {
+		*rdret = 0;
+		return(0);
+	}
+	sz = rvi_msg[4];
+	if (rvi_msg_len != sz + 6) {
+		printf("error: readlink response has wrong length\n");
+		return(ERROR_TARGET);
+	}
+	bcopy(rvi_msg + 5, databuf, sz);
+	*rdret = sz;
+	return(0);
+}
+
+cmd_readlink(argc, argv)
+	char **argv;
+{
+	u_char databuf[256];
+	int rc, sz, off, l;
+
+	rc = do_readlink(argv[1], databuf, &sz);
+	if (rc)
+		return(rc);
+	printf("%d bytes read\n", sz);
+	for (off = 0; off < sz; off += 16) {
+		l = sz - off;
+		if (l > 16)
+			l = 16;
+		hexdump_line(off, databuf + off, l);
+	}
+	return(0);
+}
+
+do_readlink_sancheck(pathname, databuf)
+	char *pathname;
+	u_char *databuf;
+{
+	int rc, sz;
+
+	rc = do_readlink(pathname, databuf, &sz);
+	if (rc)
+		return(rc);
+	if (sz < 2 || databuf[sz-1]) {
+		printf("error: readlink on %s returned garbage\n", pathname);
+		return(ERROR_TARGET);
+	}
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/etm.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,53 @@
+/*
+ * This header file contains various definitions for talking to ETM.
+ */
+
+#define	ETM_USE_ID	0x001E0004
+
+/* ETM Module IDs */
+enum {
+    ETM_TM3        = 0x00, // Use of old TM3 protocol
+    ETM_CORE       = 0x01,
+    ETM_TMT        = 0x02, // Pseudo module
+    ETM_SH         = 0x03, // Pseudo module
+    ETM_TM3_MISC   = 0x04, // Pseudo module - Target side
+    ETM_RF         = 0x05, 
+    ETM_IMEI       = 0x06,
+    ETM_FFS2       = 0x07,
+    ETM_AUDIO      = 0x08,
+    ETM_TPU        = 0x09, // Not official part ETM
+    ETM_PWR        = 0x0A,
+    ETM_BT         = 0x0B,
+    ETM_L23        = 0x0C,
+    ETM_RESERVED10 = 0x0D,
+    ETM_RESERVED11 = 0x0E,
+    ETM_RESERVED12 = 0x0F,
+
+    ETM_CUST       = 0xC0, // Customize id
+    ETM_CUST1      = 0xC1, // Customize id
+    ETM_CUST2      = 0xC2, // Customize id
+    ETM_CUST3      = 0xC3, // Customize id
+    ETM_CUST4      = 0xC4, // Customize id
+    ETM_CUST5      = 0xC5, // Customize id
+    ETM_CUST6      = 0xC6, // Customize id
+    ETM_CUST7      = 0xC7, // Customize id
+    ETM_CUST8      = 0xC8, // Customize id
+
+    ETM_TEST       = 0xAA, // used for test of dll's
+    ETM_TASK       = 0xEE, // ETM TASK in Target
+
+    ETM_FFS1       = 0x70
+};
+
+/* ETM_CORE opcodes */
+#define	TMCORE_OPC_MEM		0x61
+#define	TMCORE_OPC_ECHO		0x62
+#define	TMCORE_OPC_RESET	0x63
+#define	TMCORE_OPC_DEBUG	0x64
+#define	TMCORE_OPC_VERSION	0x65
+#define	TMCORE_OPC_CODEC_RD	0x66
+#define	TMCORE_OPC_CODEC_WR	0x67
+#define	TMCORE_OPC_DIEID	0x68
+
+#define	MAX_MEMREAD_BYTES	238
+#define	MAX_MEMREAD_16BIT	119
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/ffs.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,114 @@
+/*
+ * A few generic FFS API definitions which apply to both TMFFS1 and TMFFS2
+ */
+
+enum FFS_OBJECT_TYPE {
+    OT_FILE    = 1,
+    OT_DIR     = 2,
+    OT_LINK    = 3,
+    OT_SEGMENT = 4
+};
+
+enum FFS_OBJECT_FLAGS {
+    OF_READONLY = 1<<4  // object cannot be modified
+};
+
+enum FFS_OPEN {
+    FFS_O_EMPTY  = 0x00,  // Okay?
+    FFS_O_CREATE = 0x01,
+    FFS_O_APPEND = 0x02,
+    FFS_O_EXCL   = 0x04,
+    FFS_O_TRUNC  = 0x08,
+    FFS_O_RDONLY = 0x10,
+    FFS_O_WRONLY = 0x20,
+    FFS_O_RDWR   = FFS_O_RDONLY | FFS_O_WRONLY
+};
+
+enum FFS_SEEK {
+    FFS_SEEK_SET = 0,
+    FFS_SEEK_CUR = 1,
+    FFS_SEEK_END = 2
+};
+
+enum FFS_QUERY {              // data size, description
+    Q_BYTES_FREE        =  1, // 4, number of free bytes in FFS
+    Q_BYTES_USED        =  2, // 4, number of used bytes in FFS
+    Q_BYTES_LOST        =  3, // 4, number of lost bytes in FFS
+    Q_BYTES_MAX         =  4, // 4, number of max available bytes in FFS
+    Q_BYTES_FREE_RAW    =  5, // 4, number of free raw bytes in FFS (used internal)
+
+    Q_FD_BUF_SIZE       = 10, // 4, size of buffer used by stream functions
+
+    Q_TM_BUFADDR        = 11, // 4, testmode buffer addr
+    Q_TM_BUFSIZE        = 12, // 4, testmode ffs buffer size
+    Q_DEV_BASE          = 13, // 4, FFS device base address
+    Q_CHUNK_SIZE_MAX    = 14, // 4, max size of chunks made by non stream fkt.
+
+    // FFS versions
+    Q_FFS_API_VERSION   = 16, // 2, FFS API Version
+    Q_FFS_DRV_VERSION   = 17, // 2, FFS Driver Version
+    Q_FFS_REVISION      = 18, // 2, FFS Revision (from PRCS)
+    Q_FFS_FORMAT_READ   = 19, // 2, FFS version as read from ffs
+    Q_FFS_LASTERROR     = 20, // 2, FFS last error (from init)
+    Q_FFS_FORMAT_WRITE  = 21, // 2, FFS version as written to ffs on format
+    Q_FFS_TM_VERSION    = 22, // 2, FFS Testmode version
+
+    // File system queries
+    Q_FILENAME_MAX      = 24, // 2, max filename length
+    Q_PATH_DEPTH_MAX    = 25, // 2, max path/directory nesting depth
+    Q_FD_MAX            = 26, // 2, max numbers of simultaneous open files
+
+    Q_OBJECTS_FREE      = 32, // 2, number of objects that can be created
+    Q_INODES_USED       = 33, // 2, number of inodes used
+    Q_INODES_LOST       = 34, // 2, number of inodes lost
+    Q_OBJECTS_USED      = 33, // 2, DEPRECATED: old name for Q_INODES_USED
+    Q_OBJECTS_LOST      = 34, // 2, DEPRECATED: old name for Q_INODES_LOST
+    Q_OBJECTS_MAX       = 35, // 2, max number of valid objects allowed
+    Q_INODES_MAX        = 36, // 2, physical total max number of inodes
+    Q_INODES_HIGH       = 37, // 2, watermark for when inodes will be reclaimed
+    Q_LOST_HIGH         = 38, // 2, watermark for when data block will be reclaimed
+
+    // Device queries
+    Q_DEV_MANUFACTURER  = 48, // 2, flash manufacturer ID
+    Q_DEV_DEVICE        = 49, // 2, flash device ID
+    Q_DEV_BLOCKS        = 50, // 2, number of FFS blocks in device
+    Q_DEV_ATOMSIZE      = 51, // 2, atomsize used by FFS for this device
+    Q_DEV_DRIVER        = 52, // 2, flash device driver
+
+    // All queries below here are for debug purpose only, are unsupported
+    // and can change at any time without notice!
+
+    // Miscellaneous/Internal
+    Q_BLOCKS_FREE_MIN   = 64, // 2, Number of spare blocks (0 or 1)
+
+    Q_BLOCKS_FREE       = 70, // 2, number of free blocks
+
+    // Debug queries
+    Q_FS_FLAGS          = 80,
+    Q_FS_INODES         = 81,
+    Q_FS_ROOT           = 82,
+
+    Q_OBJECTS_TOTAL     = 90, // 2, Accumulated number of valid objects
+    Q_TOTAL_OBJECTS     = 90, // 2, DEPRECATED: old name for Q_OBJECTS_TOTAL
+
+    Q_STATS_FIRST             = 100,
+    Q_STATS_DRECLAIMS         = 100,
+    Q_STATS_IRECLAIMS         = 101,
+    Q_STATS_BRECLAIMS         = 102,
+    Q_STATS_DATA_RECLAIMED    = 103,
+    Q_STATS_INODES_RECLAIMED  = 104,
+    Q_STATS_DATA_ALLOCATED    = 105,
+
+    Q_REQUEST_ID_LAST         = 110,
+
+    Q_DEBUG_FIRST             = 120,
+    Q_DEBUG_0                 = 120,
+    Q_DEBUG_1                 = 121,
+    Q_DEBUG_2                 = 122,
+    Q_DEBUG_3                 = 123,
+    Q_DEBUG_LAST              = 127,
+
+    // individual lines of the bstat array can be returned by the following
+    // id plus the bstat index of the line wanted.
+    Q_BSTAT                   = -128
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/ffserr.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,48 @@
+/*
+ * FFS error codes as returned in TMFFS2 response byte packets:
+ * these are positive, whereas the ones in the gsm-fw code
+ * are negative.
+ */
+
+enum TMFFS_ERRORS {
+    TMFFS_ERR_NODEVICE    = 1,  /* flash device unknown */
+    TMFFS_ERR_CORRUPTED   = 2,  /* filesystem corrupted!? */
+    TMFFS_ERR_NOPREFORMAT = 3,  /* ffs not preformatted */
+    TMFFS_ERR_NOFORMAT    = 4,  /* ffs not formatted */
+    TMFFS_ERR_BADFORMAT   = 5,  /* incompatible ffs version, re-format needed */
+    TMFFS_ERR_MAGIC       = 6,  /* bad magic */
+    TMFFS_ERR_AGAIN       = 7,  /* not ready, try again later */
+    TMFFS_ERR_NOSYS       = 8,  /* function not implemented */
+    TMFFS_ERR_DRIVER      = 9,  /* ffs device driver error */
+
+    TMFFS_ERR_NOSPACE     = 10, /* out of data space */
+    TMFFS_ERR_FSFULL      = 11, /* file system full, no free inodes */
+    TMFFS_ERR_BADNAME     = 12, /* bad filename */
+    TMFFS_ERR_NOTFOUND    = 13, /* object not found */
+    TMFFS_ERR_EXISTS      = 14, /* object exists */
+    TMFFS_ERR_ACCESS      = 15, /* access permission violation */
+    TMFFS_ERR_NAMETOOLONG = 16, /* filename too long */
+    TMFFS_ERR_INVALID     = 17, /* invalid argument */
+    TMFFS_ERR_DIRNOTEMPTY = 18, /* directory not empty */
+    TMFFS_ERR_NOTADIR     = 19, /* object is not a directory */
+    TMFFS_ERR_SPARE       = 20, /* SPARE */
+    TMFFS_ERR_FILETOOBIG  = 21, /* file too big */
+    TMFFS_ERR_NOTAFILE    = 22, /* object is not a file */
+    TMFFS_ERR_PATHTOODEEP = 23, /* path too deep */
+
+    TMFFS_ERR_NUMFD       = 24, /* Max number of open files reached */
+    TMFFS_ERR_BADFD       = 25, /* Bad file descriptor */
+    TMFFS_ERR_BADOP       = 26, /* Bad operation */
+    TMFFS_ERR_LOCKED      = 27, /* The file is locked */
+
+    TMFFS_ERR_TOOBIG      = 30, /* too big (tmffs buffer overflow) */
+    TMFFS_ERR_MEMORY      = 31, /* out of memory */
+    TMFFS_ERR_MSGSEND     = 32, /* message send failed */
+
+    /* debug errors - ??? */
+
+    TMFFS_ERR_SIBLINGLOOP = 40, /* directory sibling loop */
+    TMFFS_ERR_NOBLOCKS    = 41, /* No more blocks!? */
+    TMFFS_ERR_DBR         = 42, /* Data reclaim did not finish!? */
+    TMFFS_ERR_RECLAIMLOOP = 43  /* Data reclaim loop */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/ffslimits.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,22 @@
+/*
+ * Limits on FFS filenames and pathnames
+ *
+ * The deepest pathname allowed is one of the form /1/2/3/4/5/6, where the
+ * last component may be a file, a directory or a symlink; if this last
+ * component is a directory, it has to be empty, because any child of
+ * that directory would violate the depth limit.
+ *
+ * The proper FFS pathname form begins with a slash (all pathnames must
+ * be absolute, no Unix processes in the fw means no current directories),
+ * has exactly one slash in each separating place (no double slashes),
+ * and no trailing slash except in the special case of the root directory,
+ * whose full pathname is "/".
+ *
+ * Each component name is [1,20] characters long; combining this limit
+ * with the maximum depth of 6 puts the maximum length of a properly-formed
+ * full pathname at 126 characters.
+ */
+
+#define	MAX_FN_COMPONENT	20
+#define	MAX_NAME_DEPTH		6
+#define	MAX_FULL_PATHNAME	((MAX_FN_COMPONENT+1) * MAX_NAME_DEPTH)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/limits.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,32 @@
+/*
+ * For sizing our buffers etc in the rvinterf suite, including the local
+ * UNIX domain socket protocol between rvinterf and fc-tmsh etc, we need
+ * to have some limits on the message sizes in both host->target and
+ * target->host directions.
+ *
+ * For the host->target direction, the choice of message size limit is
+ * easy: the packet Rx code in RVT on the target side also has a limit
+ * (quite naturally, as it needs to use a static buffer to reassemble
+ * incoming packets as they arrive at the UART in unpredictable interrupt-
+ * sized chunks), so we set our limit to match that in RVT.
+ */
+
+#define	MAX_PKT_TO_TARGET	255
+
+/*
+ * In the other direction (target->host), there is no fixed limit
+ * definition easily visible in the target fw code: any fw component
+ * can call rvt_send_trace_cpy() or rvt_mem_alloc() followed by
+ * rvt_send_trace_no_cpy(), or some higher-level API that reduces to
+ * these functions, with a message of any size, subject only to memory
+ * limits, which obviously aren't as strict as a #define'd maximum
+ * message size.  Hence in this direction we use our own arbitrary
+ * choice of size limit.
+ */
+
+#define	MAX_PKT_FROM_TARGET	512
+
+/*
+ * Both limit definitions above counts all bytes between the opening and
+ * closing STX flags, but not DLEs inserted for binary transparency.
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/localsock.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,80 @@
+/*
+ * This header defines and describes (through comments) the local UNIX domain
+ * socket interface implemented between rvinterf and its clients like fc-tmsh.
+ *
+ * The UNIX domain sockets used for this ad hoc interface are of the
+ * SOCK_STREAM kind, but the true nature of the communication is message-based.
+ * We use the same trick that is used for DNS over TCP: every message in each
+ * direction is preceded by a 2-byte length.  This length is sent MSB first
+ * just like in DNS over TCP.  The limit on the size of these messages
+ * (for sizing buffers etc) is:
+ */
+
+#define	LOCALSOCK_MAX_MSG	1024
+
+/*
+ * Each message in the client->rvinterf direction (can be seen as command)
+ * begins (after the length) with an opcode byte as follows:
+ */
+
+#define	CLI2RVI_WANT_RVTRACE		0x00
+#define	CLI2RVI_WANT_MUXPROTO		0x01
+#define	CLI2RVI_PKT_TO_TARGET		0x02
+#define	CLI2RVI_RAWBYTES_TO_TARGET	0x03
+#define	CLI2RVI_RESET_PACKET_RX		0x04
+#define	CLI2RVI_DROP_MUXPROTO		0x05
+
+/*
+ * The first two commands (CLI2RVI_WANT_RVTRACE and CLI2RVI_WANT_MUXPROTO)
+ * are the means by which client programs inform rvinterf that they are
+ * interested in receiving copies of certain packets coming from the target.
+ *
+ * The CLI2RVI_WANT_RVTRACE opcode needs to be followed by a USEID mask value
+ * and a USEID match value, both in the network byte order, i.e., MSB first,
+ * for a total message length of 9 bytes.  For every RV trace message received
+ * from the target, rvinterf will iterate through all active clients to see who
+ * is interested: if the received USEID ANDed with the mask equals the match
+ * value, the message will be forwarded to that client.
+ *
+ * The CLI2RVI_WANT_MUXPROTO opcode needs to be followed by one byte
+ * identifying the RVTMUX protocol of interest, i.e., the first byte of the
+ * packets exchanged between the host and the target, e.g., 0x12 for L1 traces
+ * as defined in pktmux.h, for a total message length of 2 bytes.
+ *
+ * The CLI2RVI_RESET_PACKET_RX opcode resets the "interests" previously set
+ * with CLI2RVI_WANT_RVTRACE and/or CLI2RVI_WANT_MUXPROTO.  It is a "blanket"
+ * reset; the command message consists of just the opcode.  The
+ * CLI2RVI_DROP_MUXPROTO command is more specific and undoes the effect of a
+ * previous CLI2RVI_WANT_MUXPROTO; it needs to be followed by one byte
+ * identifying the RVTMUX protocol in question, just like CLI2RVI_WANT_MUXPROTO.
+ *
+ * The last two commands (CLI2RVI_PKT_TO_TARGET and CLI2RVI_RAWBYTES_TO_TARGET)
+ * cause data payload to be sent to the target serial port.  Payload following
+ * CLI2RVI_PKT_TO_TARGET (must not exceed MAX_PKT_TO_TARGET) is sent with the
+ * proper packet encapsulation per TI; bytes following
+ * CLI2RVI_RAWBYTES_TO_TARGET are sent raw.
+ */
+
+/*
+ * Each message in the rvinterf->client direction begins (after the length)
+ * with a message type byte as follows:
+ */
+
+#define	RVI2CLI_PKT_FROM_TARGET		0x00
+#define	RVI2CLI_LOCAL_CMD_RESP		0x01
+
+/*
+ * Messages beginning with RVI2CLI_PKT_FROM_TARGET are packets received
+ * from the target GSM device; the byte following this type code is the
+ * first byte of the packet from the target, e.g., 0x11 for RV traces or
+ * 0x12 for L1 traces.  Rvinterf will only start sending these messages
+ * to a client after that client has expressed interest in receiving
+ * target->host packets of a particular type.
+ *
+ * Messages beginning with RVI2CLI_LOCAL_CMD_RESP are generated locally
+ * by rvinterf itself as responses to commands, currently as responses to
+ * CLI2RVI_WANT_{RVTRACE,MUXPROTO}.  The byte following the
+ * RVT2CLI_LOCAL_CMD_RESP type code is ASCII '+' or ASCII '-', indicating
+ * success or error, respectively.  Any remaining bytes form a message
+ * for the user.
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/localtypes.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,7 @@
+/*
+ * Our own definition of u8/u16/u32
+ */
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/pktmux.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+/*
+ * Definitions for the RVT MUX over-the-wire protocol
+ */
+
+#define	STX	0x02
+#define	DLE	0x10
+
+#define RVT_RV_HEADER        0x11
+#define RVT_L1_HEADER        0x12
+#define RVT_L23_HEADER       0x13
+#define RVT_TM_HEADER        0x14
+#define RVT_RNET_HEADER      0x15
+#define RVT_PROF_HEADER      0x16
+#define RVT_GTTBACK_HEADER   0x17
+#define RVT_OTHER_HEADER     0x18
+/* FreeCalypso additions */
+#define RVT_AT_HEADER        0x1A
+#define RVT_EXTUI_HEADER     0x1B
+#define RVT_TCH_HEADER       0x1C
+#define RVT_KEEPALIVE_HEADER 0x1D
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/tch_feature.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,12 @@
+/*
+ * This header file contains definitions for the
+ * custom voice TCH rerouting feature that
+ * has been implemented as an experiment in the
+ * FreeCalypso GSM firmware.
+ */
+
+#define	TCH_CONFIG_REQ	0x11
+#define	TCH_CONFIG_CONF	0x12
+#define	TCH_ULBITS_REQ	0x13
+#define	TCH_ULBITS_CONF	0x14
+#define	TCH_DLBITS_IND	0x15
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/tm3.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,47 @@
+/*
+ * This header file contains various definitions for talking to the old
+ * non-enhanced Test Mode firmware component.
+ */
+
+/* CID opcodes */
+enum 
+{
+  TM_INIT                    = 0x20,
+  TM_MODE_SET                = 0x21,
+  VERSION_GET                = 0x22,
+  RF_ENABLE                  = 0x23,
+  STATS_READ                 = 0x24,
+  STATS_CONFIG_WRITE         = 0x25,
+  STATS_CONFIG_READ          = 0x26,
+  RF_PARAM_WRITE             = 0x30,
+  RF_PARAM_READ              = 0x31,
+  RF_TABLE_WRITE             = 0x32,
+  RF_TABLE_READ              = 0x33,
+  RX_PARAM_WRITE             = 0x34,
+  RX_PARAM_READ              = 0x35,
+  TX_PARAM_WRITE             = 0x36,
+  TX_PARAM_READ              = 0x37,
+  TX_TEMPLATE_WRITE          = 0x38,
+  TX_TEMPLATE_READ           = 0x39,
+  MEM_WRITE                  = 0x40,
+  MEM_READ                   = 0x41,
+  CODEC_WRITE                = 0x42,
+  CODEC_READ                 = 0x43,
+  MISC_PARAM_WRITE           = 0x44,
+  MISC_PARAM_READ            = 0x45,
+  MISC_TABLE_WRITE           = 0x46,
+  MISC_TABLE_READ            = 0x47,
+  MISC_ENABLE                = 0x48,
+  SPECIAL_PARAM_WRITE        = 0x50,
+  SPECIAL_PARAM_READ         = 0x51,
+  SPECIAL_TABLE_WRITE        = 0x52,
+  SPECIAL_TABLE_READ         = 0x53,
+  SPECIAL_ENABLE             = 0x54,
+
+  TPU_TABLE_WRITE            = 0x55,
+  TPU_TABLE_READ             = 0x56,
+
+  TM_FFS                     = 0x70
+};
+
+#define	TM3_MEMREAD_MAX	0x7C
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/tmffs1.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * FFS1 Protocol Indentifiers
+ ******************************************************************************/
+
+enum FFS1_PROTOCOL_IDENTIFIERS {
+    FPI_END = 0,         /* end */
+    FPI_BEGIN,           /* begin */
+    FPI_TMFFS_VERSION,   /* tmffs_version */
+
+    FPI_PREFORMAT,       /* preformat */
+    FPI_FORMAT,          /* format */
+
+    FPI_FCREATE,         /* fcreate */
+    FPI_FUPDATE,         /* fupdate */
+    FPI_FWRITE,          /* fwrite */
+    FPI_FREAD,           /* fread */
+    FPI_REMOVE,          /* remove */
+
+    FPI_MKDIR,           /* mkdir */
+    FPI_OPENDIR,         /* opendir */
+    FPI_READDIR,         /* readdir */
+
+    FPI_STAT,            /* stat */
+    FPI_LINKSTAT,        /* linkstat */
+
+    FPI_SYMLINK,         /* symlink */
+    FPI_READLINK,        /* readlink */
+
+    FPI_QUERY,           /* query */
+    FPI_FCONTROL,        /* fcontrol */
+
+    FPI_INIT,            /* init */
+    FPI_EXIT,            /* exit */
+
+    FPI_PCM_GETFILEINFO, /* getfileinfo */
+    FPI_PCM_READFILE,    /* readfile */
+    FPI_PCM_WRITEFILE,   /* writefile */
+    FPI_PCM_READRECORD,  /* readrecord */
+    FPI_PCM_WRITERECORD, /* writerecord */
+
+    FPI_BUFREAD,         /* buf_read */
+    FPI_BUFWRITE,        /* buf_write */
+    FPI_BUFSET,          /* buf_set */
+
+    FPI_UINT8,           /* UINT8 */
+    FPI_UINT16,          /* UINT16 */
+    FPI_UINT32,          /* UINT32 */
+    FPI_INT8,            /* INT8 */
+    FPI_INT16,           /* INT16 */
+    FPI_INT32,           /* INT32 */
+    FPI_BUFFER,          /* BUFFER */
+    FPI_DATA,            /* DATA */
+    FPI_STRBUF,          /* STRBUF */
+    FPI_STRING,          /* STRING */
+
+    FPI_TFFS             /* TFFS */
+
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/include/tmffs2.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,49 @@
+/******************************************************************************
+ * FFS2 Protocol Indentifiers
+ ******************************************************************************/
+
+enum FFS2_PROTOCOL_IDENTIFIERS {
+	TMFFS_FORMAT     = 'f',
+	TMFFS_PREFORMAT  = 'p',
+
+	TMFFS_MKDIR      = 'm',
+	TMFFS_OPENDIR    = 'o',
+	TMFFS_READDIR    = 'D',
+	TMFFS_REMOVE     = 'd',
+	TMFFS_RENAME     = 'n',
+	TMFFS_XLSTAT     = 'x',
+
+	TMFFS_SYMLINK    = 'y',
+	TMFFS_READLINK   = 'Y',
+
+	TMFFS_OPEN       = 'O',
+	TMFFS_CLOSE      = 'C',
+	TMFFS_READ       = 'R',
+	TMFFS_WRITE      = 'W',
+	TMFFS_SEEK       = 'S',
+
+	TMFFS_FTRUNCATE  = 'T',
+	TMFFS_TRUNCATE   = 't',
+
+	TMFFS_FILE_READ  = 'r',
+	TMFFS_FILE_WRITE = 'w',
+
+	TMFFS_FSTAT      = 'F',
+	TMFFS_LSTAT      = 'l',
+	TMFFS_STAT       = 's',
+
+	TMFFS_FCONTROL   = 'c',
+	TMFFS_QUERY      = 'q',
+
+	TMFFS_INIT       = 'i',
+	TMFFS_EXIT       = 'e', 
+
+	// Special
+	TMFFS_DIRXLSTAT  = 'X',
+
+	TMFFS_VERSION    = 'v',
+	TMFFS_TFFS       = 'z'
+}; 
+
+#define TMFFS_STRING_SIZE	127	/* includes the terminating NUL */
+#define	MAX_READ_DATA		254
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libasync/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,13 @@
+CC=	gcc
+CFLAGS=	-O2 -I../include
+OBJS=	init.o interf.o launchrvif.o rvtrace.o ttymagic.o
+LIB=	libasync.a
+
+all:	${LIB}
+
+${LIB}:	${OBJS}
+	ar rcu $@ ${OBJS}
+	ranlib $@
+
+clean:
+	rm -f *.[oa] errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libasync/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,3 @@
+This host side library contains common modules for tools like fc-tmsh and
+g23sh which operate asynchronously.  This code has been factored out of
+fc-tmsh.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libasync/init.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,66 @@
+/*
+ * This module contains the common initialization code for fc-tmsh and g23sh.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pktmux.h"
+#include "localsock.h"
+
+extern char *socket_pathname;
+extern int sock;
+
+connect_local_socket()
+{
+	/* local socket binding voodoo copied from osmocon */
+	struct sockaddr_un local;
+	unsigned int namelen;
+	int rc;
+
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		perror("socket(AF_UNIX, SOCK_STREAM, 0)");
+		exit(1);
+	}
+
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, socket_pathname, sizeof(local.sun_path));
+	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	namelen = SUN_LEN(&local);
+#else
+	namelen = strlen(local.sun_path) +
+		  offsetof(struct sockaddr_un, sun_path) + 1;
+#endif
+
+	rc = connect(sock, (struct sockaddr *) &local, namelen);
+	if (rc != 0) {
+		perror(socket_pathname);
+		exit(1);
+	}
+
+	return(0);
+}
+
+send_init_command(cmdpkt, cmdlen)
+	u_char *cmdpkt;
+{
+	u_char lenbuf[2];
+
+	lenbuf[0] = 0;
+	lenbuf[1] = cmdlen;
+	write(sock, lenbuf, 2);
+	write(sock, cmdpkt, cmdlen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libasync/interf.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,106 @@
+/*
+ * This module implements the link to rvinterf.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "localsock.h"
+
+extern int sock;
+
+u_char rvi_msg[LOCALSOCK_MAX_MSG];
+int rvi_msg_len;
+
+static int rx_state, rx_left;
+static u_char *rx_ptr;
+
+void
+localsock_prep_for_length_rx()
+{
+	rx_state = 0;
+	rx_ptr = rvi_msg;
+	rx_left = 2;
+}
+
+static void
+prep_for_message_rx()
+{
+	rx_state = 1;
+	rx_ptr = rvi_msg;
+	rx_left = rvi_msg_len;
+}
+
+void
+process_msg_from_rvinterf()
+{
+	switch (rvi_msg[0]) {
+	case RVI2CLI_PKT_FROM_TARGET:
+		process_pkt_from_target();
+		return;
+	case RVI2CLI_LOCAL_CMD_RESP:
+		if (rvi_msg_len < 2)
+			goto bad;
+		if (rvi_msg[1] == '+')
+			return;
+		tty_cleanup();
+		fprintf(stderr, "Error from rvinterf: %.*s\n", rvi_msg_len - 1,
+			rvi_msg + 1);
+		exit(1);
+	default:
+	bad:
+		tty_cleanup();
+		fprintf(stderr,
+			"Error: unexpected message type %02X from rvinterf\n",
+			rvi_msg[0]);
+		exit(1);
+	}
+}
+
+void
+handle_rvinterf_input()
+{
+	int cc;
+
+	cc = read(sock, rx_ptr, rx_left);
+	if (cc <= 0) {
+		tty_cleanup();
+		perror("read from rvinterf socket");
+		exit(1);
+	}
+	rx_ptr += cc;
+	rx_left -= cc;
+	if (rx_left)
+		return;
+	/* got the thing, process it */
+	if (rx_state) {
+		process_msg_from_rvinterf();
+		localsock_prep_for_length_rx();
+	} else {
+		rvi_msg_len = rvi_msg[0] << 8 | rvi_msg[1];
+		if (rvi_msg_len < 1 || rvi_msg_len > LOCALSOCK_MAX_MSG) {
+			tty_cleanup();
+			fprintf(stderr,
+				"Invalid length from rvinterf: %02X%02X\n",
+				rvi_msg[0], rvi_msg[1]);
+			exit(1);
+		}
+		prep_for_message_rx();
+	}
+}
+
+void
+send_pkt_to_target(pkt, pktlen)
+	u_char *pkt;
+{
+	u_char hdrbuf[3];
+	int len1;
+
+	len1 = pktlen + 1;
+	hdrbuf[0] = len1 >> 8;
+	hdrbuf[1] = len1 & 0xFF;
+	hdrbuf[2] = CLI2RVI_PKT_TO_TARGET;
+	write(sock, hdrbuf, 3);
+	write(sock, pkt, pktlen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libasync/launchrvif.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,63 @@
+/*
+ * This module implements the optional "behind the scenes" invokation
+ * of rvinterf from fc-tmsh.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static char rvinterf_pathname[] = "/usr/local/bin/rvinterf";
+
+extern int sock;
+
+char *rvinterf_Bopt, *rvinterf_lopt, *rvinterf_wopt;
+
+launch_rvinterf(ttyport)
+	char *ttyport;
+{
+	int sp[2], rc;
+	char *rvif_argv[11], Sarg[16], **ap;
+
+	rc = socketpair(AF_UNIX, SOCK_STREAM, 0, sp);
+	if (rc < 0) {
+		perror("socketpair");
+		exit(1);
+	}
+	sock = sp[0];
+	sprintf(Sarg, "-S%d", sp[1]);
+	ap = rvif_argv;
+	*ap++ = "rvinterf";
+	*ap++ = Sarg;
+	*ap++ = "-n";
+	if (rvinterf_Bopt) {
+		*ap++ = "-B";
+		*ap++ = rvinterf_Bopt;
+	}
+	if (rvinterf_lopt) {
+		*ap++ = "-l";
+		*ap++ = rvinterf_lopt;
+	}
+	if (rvinterf_wopt) {
+		*ap++ = "-w";
+		*ap++ = rvinterf_wopt;
+	}
+	*ap++ = ttyport;
+	*ap = 0;
+	rc = vfork();
+	if (rc < 0) {
+		perror("vfork for launching rvinterf");
+		exit(1);
+	}
+	if (!rc) {
+		/* we are in the child - do the exec */
+		close(sp[0]);
+		execv(rvinterf_pathname, rvif_argv);
+		perror(rvinterf_pathname);
+		_exit(1);
+	}
+	close(sp[1]);
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libasync/rvtrace.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,54 @@
+/*
+ * Here we detect and handle "Lost Message" packets.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "localsock.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+void
+safe_print_trace(src, srclen, dest)
+	u_char *src;
+	char *dest;
+{
+	int i, c;
+	char *dp;
+
+	dp = dest;
+	for (i = 0; i < srclen; i++) {
+		c = src[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	*dp = '\0';
+}
+
+void
+handle_useid_0()
+{
+	char buf[MAX_PKT_FROM_TARGET*4];
+
+	if (strncmp(rvi_msg + 7, "RVT: Lost Message", 17))
+		return;
+	safe_print_trace(rvi_msg + 7, rvi_msg_len - 7, buf);
+	async_msg_output(buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libasync/ttymagic.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,112 @@
+/*
+ * This module contains the tty "magic" code for fc-tmsh.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <termios.h>
+
+extern int ttyhacks;
+
+struct termios orig_termios, our_termios;
+
+#define	MAX_USER_CMD	78
+char usercmd[MAX_USER_CMD+1];
+int usercmd_len;
+
+void
+tty_init()
+{
+	if (!ttyhacks)
+		return;
+	tcgetattr(0, &orig_termios);
+	bcopy(&orig_termios, &our_termios, sizeof(struct termios));
+	our_termios.c_oflag &= ~(OCRNL | ONOCR | ONLRET);
+	our_termios.c_lflag &= ~(ICANON | ECHO);
+	our_termios.c_cc[VMIN] = 1;
+	our_termios.c_cc[VTIME] = 0;
+	tcsetattr(0, TCSAFLUSH, &our_termios);
+	putchar('>');
+	fflush(stdout);
+}
+
+void
+tty_cleanup()
+{
+	if (!ttyhacks)
+		return;
+	tcsetattr(0, TCSAFLUSH, &orig_termios);
+}
+
+void
+handle_tty_input()
+{
+	char buf[256];
+	int i, c, cc;
+
+	cc = read(0, buf, sizeof buf);
+	if (cc <= 0) {
+		tty_cleanup();
+		exit(0);
+	}
+	for (i = 0; i < cc; i++) {
+		c = buf[i];
+		if (c >= ' ' && c <= '~') {
+			if (usercmd_len >= MAX_USER_CMD)
+				continue;
+			usercmd[usercmd_len++] = c;
+			if (ttyhacks)
+				putchar(c);	/* echo */
+			continue;
+		}
+		switch (c) {
+		case '\b':
+		case 0x7F:
+			if (!usercmd_len)
+				continue;
+			usercmd_len--;
+			if (ttyhacks) {
+				putchar('\b');
+				putchar(' ');
+				putchar('\b');
+			}
+			continue;
+		case '\n':
+		case '\r':
+			usercmd[usercmd_len] = '\0';
+			if (ttyhacks)
+				putchar('\n');	/* echo */
+			dispatch_user_cmd();
+			usercmd_len = 0;
+			if (ttyhacks)
+				putchar('>');	/* new prompt */
+		}
+	}
+}
+
+void
+async_msg_output(msg)
+	char *msg;
+{
+	int msglen, i;
+
+	msglen = strlen(msg);
+	if (ttyhacks)
+		putchar('\r');
+	fputs(msg, stdout);
+	if (ttyhacks)
+		for (i = msglen; i < usercmd_len + 1; i++)
+			putchar(' ');
+	putchar('\n');
+	if (!ttyhacks)
+		return;
+	/* reprint the input line */
+	putchar('>');
+	if (!usercmd_len)
+		return;
+	fwrite(usercmd, 1, usercmd_len, stdout);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libg23/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,13 @@
+CC=	gcc
+CFLAGS=	-O2
+OBJS=	fmtdispatch.o fmtfunc.o
+LIB=	libg23.a
+
+all:	${LIB}
+
+${LIB}:	${OBJS}
+	ar rcu $@ ${OBJS}
+	ranlib $@
+
+clean:
+	rm -f *.[oa] errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libg23/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+The library built in this directory is a host side library, not for the target.
+This library implements some functions for handling packet exchanges with GPF,
+and it will be linked by some of the programs in the rvinterf suite.
+
+It needs to be noted that the RVTMUX channel belonging to GPF was named
+RVT_L23_HEADER by TI, and as a result, I thought that these packets related
+specifically to the higher layers of the protocol stack.  But now we know that
+hierarchically speaking, GPF sits *below* L1, not above, and GPF packets should
+not be automatically associated with G23.  This realization was made fairly
+late, thus "g23" appears in a bunch of function names, and in the name of this
+library.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libg23/fmtdispatch.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,144 @@
+/*
+ * This libg23 module exports the format_g23_packet() function, which
+ * validates the packet, then dispatches it to format_g23_trace(),
+ * format_g23_sysprim() or format_g23_psprim() as appropriate, or
+ * prints it in raw hex if malformed.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+static int
+basic_checks(rxpkt, rxpkt_len)
+	u_char *rxpkt;
+{
+	int i, c;
+
+	if (rxpkt_len < 17)
+		return(0);
+	/* check version bits in the header byte */
+	if ((rxpkt[1] & 0xC0) != 0x80)
+		return(0);
+	/* check the length */
+	c = rxpkt[2] | rxpkt[3] << 8;
+	if (c + 4 != rxpkt_len)
+		return(0);
+	/* ensure that the "from" and "to" are printable ASCII */
+	for (i = 8; i < 16; i++) {
+		c = rxpkt[i];
+		if (c < ' ' || c > '~')
+			return(0);
+	}
+	/* basic checks pass */
+	return(1);
+}
+
+static int
+psprim_extra_checks(rxpkt, rxpkt_len)
+	u_char *rxpkt;
+{
+	int i, c;
+
+	if (rxpkt_len < 24)
+		return(0);
+	/* "original rcvr" field needs to be printable ASCII */
+	for (i = 16; i < 20; i++) {
+		c = rxpkt[i];
+		if (c < ' ' || c > '~')
+			return(0);
+	}
+	/* checks pass */
+	return(1);
+}
+
+static void
+print_unknown_bin(rxpkt, rxpkt_len, outbuf)
+	u_char *rxpkt;
+	char *outbuf;
+{
+	int i;
+	char *dp;
+
+	dp = outbuf;
+	strcpy(dp, "GPF UNK:");
+	dp += 8;
+	for (i = 1; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+}
+
+static void
+print_old_ascii(rxpkt, rxpkt_len, outbuf)
+	u_char *rxpkt;
+	char *outbuf;
+{
+	char *dp;
+	int txtlen = rxpkt_len - 1;
+
+	dp = outbuf;
+	strcpy(dp, "GPF ASC: ");
+	dp += 9;
+	bcopy(rxpkt + 1, dp, txtlen);
+	dp += txtlen;
+	*dp = '\0';
+}
+
+static int
+is_old_ascii(rxpkt, rxpkt_len)
+	u_char *rxpkt;
+{
+	int i, c;
+
+	for (i = 1; i < rxpkt_len; i++) {
+		c = rxpkt[i];
+		if (c < ' ' || c > '~')
+			return(0);
+	}
+	return(1);
+}
+
+static void
+print_malformed(rxpkt, rxpkt_len, outbuf)
+	u_char *rxpkt;
+	char *outbuf;
+{
+	if (is_old_ascii(rxpkt, rxpkt_len))
+		print_old_ascii(rxpkt, rxpkt_len, outbuf);
+	else
+		print_unknown_bin(rxpkt, rxpkt_len, outbuf);
+}
+
+void
+format_g23_packet(rxpkt, rxpkt_len, outbuf)
+	u_char *rxpkt;
+	char *outbuf;
+{
+	if (!basic_checks(rxpkt, rxpkt_len)) {
+		print_malformed(rxpkt, rxpkt_len, outbuf);
+		return;
+	}
+	/* dispatch by type */
+	switch (rxpkt[1] & 0x30) {
+	case 0x10:
+		/* PS primitive */
+		if (psprim_extra_checks(rxpkt, rxpkt_len))
+			format_g23_psprim(rxpkt, rxpkt_len, outbuf);
+		else
+			print_malformed(rxpkt, rxpkt_len, outbuf);
+		return;
+	case 0x20:
+		/* trace */
+		format_g23_trace(rxpkt, rxpkt_len, outbuf);
+		return;
+	case 0x30:
+		/* system primitive */
+		format_g23_sysprim(rxpkt, rxpkt_len, outbuf);
+		return;
+	default:
+		print_malformed(rxpkt, rxpkt_len, outbuf);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/libg23/fmtfunc.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,167 @@
+/*
+ * This libg23 module exports functions for formatting 3 different kinds
+ * of GPF packets into human-readable form: traces, system primitives
+ * and protocol stack primitives.
+ *
+ * GPF packets passed to these functions for decoding MUST have already
+ * been verified to be well-formed for their respective type.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+static int
+entity_name_well_formed(p)
+	char *p;
+{
+	int i, len;
+
+	if (!isupper(p[0]))
+		return(0);
+	for (i = 0; i < 4; i++)
+		if (!isalnum(p[i]))
+			break;
+	len = i;
+	for (; i < 4; i++)
+		if (p[i] != ' ')
+			return(0);
+	return(len);
+}
+
+static void
+print_entity_name(raw, outp)
+	char *raw, **outp;
+{
+	int len;
+
+	len = entity_name_well_formed(raw);
+	if (len) {
+		sprintf(*outp, "%.*s", len, raw);
+		*outp += len;
+	} else {
+		sprintf(*outp, "\"%.4s\"", raw);
+		*outp += 6;
+	}
+}
+
+static void
+print_common_hdr(rxpkt, outp, typestr)
+	u_char *rxpkt;
+	char **outp, *typestr;
+{
+	sprintf(*outp, "GPF %s id=%02X ts=%02X%02X%02X%02X ", typestr,
+		rxpkt[1], rxpkt[7], rxpkt[6], rxpkt[5], rxpkt[4]);
+	*outp = index(*outp, '\0');
+	print_entity_name(rxpkt + 8, outp);
+	*(*outp)++ = '-';
+	*(*outp)++ = '>';
+	print_entity_name(rxpkt + 12, outp);
+	*(*outp)++ = ' ';
+}
+
+static void
+format_text(rxpkt, rxpkt_len, start_off, outp)
+	u_char *rxpkt;
+	char *outp;
+{
+	int i, c;
+
+	*outp++ = '\"';
+	for (i = start_off; i < rxpkt_len; i++) {
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*outp++ = 'M';
+			*outp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*outp++ = '^';
+			*outp++ = c + '@';
+		} else if (c == 0x7F) {
+			*outp++ = '^';
+			*outp++ = '?';
+		} else
+			*outp++ = c;
+	}
+	*outp++ = '\"';
+	*outp = '\0';
+}
+
+static void
+format_compressed_trace(rxpkt, rxpkt_len, start_off, outp)
+	u_char *rxpkt;
+	char *outp;
+{
+	int i;
+
+	i = start_off + 1;
+	sprintf(outp, "%d", rxpkt[i+1] << 8 | rxpkt[i]);
+	outp = index(outp, '\0');
+	i += 4;
+	for (; i < rxpkt_len; i++) {
+		sprintf(outp, " %02X", rxpkt[i]);
+		outp += 3;
+	}
+	*outp = '\0';
+}
+
+void
+format_g23_trace(rxpkt, rxpkt_len, outbuf)
+	u_char *rxpkt;
+	char *outbuf;
+{
+	char *outp = outbuf;
+	int i;
+
+	print_common_hdr(rxpkt, &outp, "trace");
+	i = 16;
+	if (rxpkt[i] < 0x20) {
+		sprintf(outp, "tc=%02X ", rxpkt[i]);
+		outp += 6;
+		i++;
+	}
+	if (rxpkt_len - i >= 5 && rxpkt[i] == '%' &&
+	    !rxpkt[i+3] && !rxpkt[i+4])
+		format_compressed_trace(rxpkt, rxpkt_len, i, outp);
+	else
+		format_text(rxpkt, rxpkt_len, i, outp);
+}
+
+void
+format_g23_sysprim(rxpkt, rxpkt_len, outbuf)
+	u_char *rxpkt;
+	char *outbuf;
+{
+	char *outp = outbuf;
+	int i;
+
+	print_common_hdr(rxpkt, &outp, "sysprim");
+	format_text(rxpkt, rxpkt_len, 16, outp);
+}
+
+void
+format_g23_psprim(rxpkt, rxpkt_len, outbuf)
+	u_char *rxpkt;
+	char *outbuf;
+{
+	char *outp = outbuf;
+	int i;
+
+	print_common_hdr(rxpkt, &outp, "PSprim");
+	/* original destination */
+	*outp++ = '(';
+	print_entity_name(rxpkt + 16, &outp);
+	*outp++ = ')';
+	/* opcode */
+	sprintf(outp, " %02X%02X%02X%02X",
+		rxpkt[23], rxpkt[22], rxpkt[21], rxpkt[20]);
+	outp += 9;
+	for (i = 24; i < rxpkt_len; i++) {
+		sprintf(outp, " %02X", rxpkt[i]);
+		outp += 3;
+	}
+	*outp = '\0';
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,31 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	rvtdump rvinterf tfc139
+INSTBIN=/usr/local/bin
+LIBG23=	../libg23/libg23.a
+
+RVTDUMP_OBJS=	format.o format_fc.o openport.o output.o packetrx.o rvtdump.o
+
+RVINTERF_OBJS=	clientcmd.o format.o format_fc.o localsock.o logsent.o \
+		openport.o output.o packetrx.o packettx.o pktfwd.o rviflcd.o \
+		rvifmain.o
+
+TFC139_OBJS=	format.o openport.o output.o packetrx.o packettx.o tfc139.o
+
+all:	${PROGS}
+
+rvtdump:	${RVTDUMP_OBJS} ${LIBG23}
+	${CC} ${CFLAGS} -o $@ ${RVTDUMP_OBJS} ${LIBG23}
+
+rvinterf:	${RVINTERF_OBJS} ${LIBG23}
+	${CC} ${CFLAGS} -o $@ ${RVINTERF_OBJS} ${LIBG23}
+
+tfc139:		${TFC139_OBJS} ${LIBG23}
+	${CC} ${CFLAGS} -o $@ ${TFC139_OBJS} ${LIBG23}
+
+install:	${PROGS}
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/client.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,22 @@
+/*
+ * The structure defined in this header file is malloced in rvinterf
+ * for every client program connection.
+ */
+
+#define	MAX_RVT_INTEREST	4
+
+typedef	unsigned u32;
+
+struct client {
+	struct	client *next;
+	int	fd;
+	int	rx_state;
+	u_char	rx_buf[LOCALSOCK_MAX_MSG];
+	int	rx_msglen;
+	u_char	*rx_ptr;
+	int	rx_left;
+	int	int_rvt_count;
+	u32	int_rvt_mask[MAX_RVT_INTEREST];
+	u32	int_rvt_match[MAX_RVT_INTEREST];
+	char	int_proto[12];
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/clientcmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,89 @@
+/*
+ * This rvinterf module implements the handling of client commands.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+#include "../include/localsock.h"
+#include "client.h"
+
+void
+process_msg_from_client(cli)
+	struct client *cli;
+{
+	int c;
+
+	switch (cli->rx_buf[0]) {
+	case CLI2RVI_WANT_RVTRACE:
+		if (cli->rx_msglen != 9) {
+			send_local_msg_to_client(cli, "-Bad command length");
+			return;
+		}
+		c = cli->int_rvt_count;
+		if (c >= MAX_RVT_INTEREST) {
+			send_local_msg_to_client(cli,
+					"-Error: too many RVT interests");
+			return;
+		}
+		cli->int_rvt_mask[c] = cli->rx_buf[1] << 24 |
+					cli->rx_buf[2] << 16 |
+					cli->rx_buf[3] << 8 | cli->rx_buf[4];
+		cli->int_rvt_match[c] = cli->rx_buf[5] << 24 |
+					cli->rx_buf[6] << 16 |
+					cli->rx_buf[7] << 8 | cli->rx_buf[8];
+		cli->int_rvt_count++;
+		send_local_msg_to_client(cli, "+OK");
+		return;
+	case CLI2RVI_WANT_MUXPROTO:
+		if (cli->rx_msglen != 2) {
+			send_local_msg_to_client(cli, "-Bad command length");
+			return;
+		}
+		if (cli->rx_buf[1] < 0x12 || cli->rx_buf[1] > 0x1D) {
+			send_local_msg_to_client(cli,
+					"-Unsupported protocol MUX value");
+			return;
+		}
+		cli->int_proto[cli->rx_buf[1] - 0x12] = 1;
+		send_local_msg_to_client(cli, "+OK");
+		return;
+	case CLI2RVI_DROP_MUXPROTO:
+		if (cli->rx_msglen != 2) {
+			send_local_msg_to_client(cli, "-Bad command length");
+			return;
+		}
+		if (cli->rx_buf[1] < 0x12 || cli->rx_buf[1] > 0x1D) {
+			send_local_msg_to_client(cli,
+					"-Unsupported protocol MUX value");
+			return;
+		}
+		cli->int_proto[cli->rx_buf[1] - 0x12] = 0;
+		send_local_msg_to_client(cli, "+OK");
+		return;
+	case CLI2RVI_RESET_PACKET_RX:
+		cli->int_rvt_count = 0;
+		bzero(cli->int_proto, sizeof(cli->int_proto));
+		send_local_msg_to_client(cli, "+OK");
+		return;
+	case CLI2RVI_PKT_TO_TARGET:
+		c = cli->rx_msglen - 1;
+		if (c < 1 || c > MAX_PKT_TO_TARGET) {
+			send_local_msg_to_client(cli,
+						 "-Invalid Tx packet length");
+			return;
+		}
+		send_pkt_to_target(cli->rx_buf + 1, c);
+		log_sent_packet(cli->rx_buf + 1, c);
+		return;
+	case CLI2RVI_RAWBYTES_TO_TARGET:
+		/* To be implemented */
+	default:
+		send_local_msg_to_client(cli, "-Unimplemented command");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/format.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,138 @@
+/*
+ * This module implements the decoding of Rx packets
+ * into human-readable form.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+static char fmtbuf[MAX_PKT_FROM_TARGET*8];	/* size it generously */
+
+void
+print_rv_trace()
+{
+	int i, c;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "RV ");
+	dp += 3;
+	/* the SWE static ID is sent MSB first */
+	for (i = 1; i <= 4; i++) {
+		sprintf(dp, "%02X", rxpkt[i]);
+		dp += 2;
+	}
+	/* severity level */
+	sprintf(dp, " %d ", rxpkt[5]);
+	dp = index(dp, '\0');
+	for (i = 6; i < rxpkt_len; i++) {
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	*dp = '\0';
+	output_line(fmtbuf);
+}
+
+void
+print_l1_trace()
+{
+	int i, c;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "L1: ");
+	dp += 4;
+	for (i = 1; i < rxpkt_len; i++) {
+		if ((i+1 < rxpkt_len) &&
+		    (rxpkt[i] == '\r' && rxpkt[i+1] == '\n' ||
+		     rxpkt[i] == '\n' && rxpkt[i+1] == '\r')) {
+			*dp = '\0';
+			output_line(fmtbuf);
+			if (i+2 == rxpkt_len)
+				return;
+			dp = fmtbuf;
+			*dp++ = '+';
+			*dp++ = ' ';
+			i++;
+			continue;
+		}
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	/* will get here only if no newline sequence at the end */
+	*dp = '\0';
+	output_line(fmtbuf);
+}
+
+void
+print_g23_trace()
+{
+	/* messy logic factored out into libg23 */
+	format_g23_packet(rxpkt, (int)rxpkt_len, fmtbuf);
+	output_line(fmtbuf);
+}
+
+void
+print_tm_output_raw()
+{
+	int i;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "TM:");
+	dp += 3;
+	for (i = 1; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	output_line(fmtbuf);
+}
+
+void
+print_unknown_packet()
+{
+	int i;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "UNK:");
+	dp += 4;
+	for (i = 0; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	output_line(fmtbuf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/format_fc.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,99 @@
+/*
+ * This module has been split off from format.c; it implements the decoding
+ * of those Rx packet types which have been invented in FreeCalypso.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+static char fmtbuf[MAX_PKT_FROM_TARGET*8];	/* size it generously */
+
+void
+print_ati_output()
+{
+	int i, c;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "ATI: ");
+	dp += 5;
+	for (i = 1; i < rxpkt_len; i++) {
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	*dp = '\0';
+	output_line(fmtbuf);
+}
+
+void
+print_fc_lld_msg()
+{
+	int i, c;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "LLD: ");
+	dp += 5;
+	for (i = 1; i < rxpkt_len; i++) {
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	*dp = '\0';
+	output_line(fmtbuf);
+}
+
+void
+print_tch_output_raw()
+{
+	int i;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "TCH:");
+	dp += 4;
+	for (i = 1; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	output_line(fmtbuf);
+}
+
+void
+report_extui_packet()
+{
+	sprintf(fmtbuf, "LCD OUT: row %u col %u-%u", rxpkt[1], rxpkt[2],
+		rxpkt[2] + (rxpkt_len - 3) / 2 - 1);
+	output_line(fmtbuf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/localsock.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,180 @@
+/*
+ * This rvinterf module handles the local UNIX domain socket interface
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../include/localsock.h"
+#include "client.h"
+
+int listener;
+
+extern struct client *client_head;
+extern int max_fd;
+extern char *socket_pathname;
+extern int socketpair_fd;
+
+create_listener_socket()
+{
+	/* local socket binding voodoo copied from osmocon */
+	struct sockaddr_un local;
+	unsigned int namelen;
+	int rc;
+
+	listener = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (listener < 0) {
+		perror("socket(AF_UNIX, SOCK_STREAM, 0)");
+		exit(1);
+	}
+
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, socket_pathname, sizeof(local.sun_path));
+	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+	unlink(local.sun_path);
+
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	namelen = SUN_LEN(&local);
+#else
+	namelen = strlen(local.sun_path) +
+		  offsetof(struct sockaddr_un, sun_path) + 1;
+#endif
+
+	rc = bind(listener, (struct sockaddr *) &local, namelen);
+	if (rc != 0) {
+		perror("bind on local socket");
+		exit(1);
+	}
+	rc = listen(listener, 3);
+	if (rc != 0) {
+		perror("listen");
+		exit(1);
+	}
+
+	if (listener > max_fd)
+		max_fd = listener;
+	return(0);
+}
+
+static void
+prep_for_length_rx(cli)
+	struct client *cli;
+{
+	cli->rx_state = 0;
+	cli->rx_ptr = cli->rx_buf;
+	cli->rx_left = 2;
+}
+
+static void
+prep_for_message_rx(cli)
+	struct client *cli;
+{
+	cli->rx_state = 1;
+	cli->rx_ptr = cli->rx_buf;
+	cli->rx_left = cli->rx_msglen;
+}
+
+handle_listener_select()
+{
+	struct sockaddr_un un_addr;
+	socklen_t len;
+	int rc;
+	struct client *newcli;
+
+	len = sizeof(un_addr);
+	rc = accept(listener, (struct sockaddr *) &un_addr, &len);
+	if (rc < 0) {
+		perror("rvinterf: accept");
+		exit(1);
+	}
+	if (rc > max_fd)
+		max_fd = rc;
+	newcli = malloc(sizeof(struct client));
+	if (!newcli) {
+		perror("rvinterf: malloc for new client");
+		exit(1);
+	}
+	bzero(newcli, sizeof(struct client));
+	newcli->fd = rc;
+	newcli->next = client_head;
+	client_head = newcli;
+	prep_for_length_rx(newcli);
+	output_line("*** Client program connected");
+	return(0);
+}
+
+create_socketpair_client()
+{
+	struct client *cli;
+
+	if (socketpair_fd > max_fd)
+		max_fd = socketpair_fd;
+	cli = malloc(sizeof(struct client));
+	if (!cli) {
+		perror("rvinterf: malloc for socketpair client");
+		exit(1);
+	}
+	bzero(cli, sizeof(struct client));
+	cli->fd = socketpair_fd;
+	client_head = cli;
+	prep_for_length_rx(cli);
+	return(0);
+}
+
+send_local_msg_to_client(cli, msg)
+	struct client *cli;
+	char *msg;
+{
+	int len, len1;
+	u_char hdr[3];
+
+	len = strlen(msg);
+	len1 = len + 1;
+	hdr[0] = len1 >> 8;
+	hdr[1] = len1 & 0xFF;
+	hdr[2] = RVI2CLI_LOCAL_CMD_RESP;
+	write(cli->fd, hdr, 3);
+	write(cli->fd, msg, len);
+}
+
+void
+handle_client_select(cli)
+	struct client *cli;
+{
+	int cc;
+
+	cc = read(cli->fd, cli->rx_ptr, cli->rx_left);
+	if (cc <= 0) {
+		/* normal client exit condition */
+		output_line("*** Client program disconnected");
+close_socket:	cli->rx_state = 2;
+		return;
+	}
+	cli->rx_ptr += cc;
+	cli->rx_left -= cc;
+	if (cli->rx_left)
+		return;
+	/* got the thing, process it */
+	if (cli->rx_state) {
+		prep_for_length_rx(cli);
+		process_msg_from_client(cli);
+	} else {
+		cli->rx_msglen = cli->rx_buf[0] << 8 | cli->rx_buf[1];
+		if (cli->rx_msglen < 1 || cli->rx_msglen > LOCALSOCK_MAX_MSG) {
+			send_local_msg_to_client(cli,
+					"-Invalid length, closing socket");
+			goto close_socket;
+		}
+		prep_for_message_rx(cli);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/logsent.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,85 @@
+/*
+ * This module implements the logging of sent packets
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+
+static void
+log_sent_ati(pkt, pktlen)
+	u_char *pkt;
+{
+	char buf[MAX_PKT_TO_TARGET*4+10];
+	int i, c;
+	char *dp;
+
+	strcpy(buf, "Sent to ATI: ");
+	dp = buf + 13;
+	for (i = 1; i < pktlen; i++) {
+		c = pkt[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	*dp = '\0';
+	output_line(buf);
+}
+
+static void
+log_sent_gpf(pkt, pktlen)
+	u_char *pkt;
+{
+	char buf[MAX_PKT_TO_TARGET*4+30];
+
+	strcpy(buf, "Sent ");
+	format_g23_packet(pkt, pktlen, buf + 5);
+	output_line(buf);
+}
+
+static void
+log_sent_other(pkt, pktlen)
+	u_char *pkt;
+{
+	char buf[MAX_PKT_TO_TARGET*3+5];
+	int i;
+	char *dp;
+
+	dp = buf;
+	strcpy(dp, "Sent");
+	dp += 4;
+	for (i = 0; i < pktlen; i++) {
+		sprintf(dp, " %02X", pkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	output_line(buf);
+}
+
+log_sent_packet(pkt, pktlen)
+	u_char *pkt;
+{
+	switch (pkt[0]) {
+	case RVT_L23_HEADER:
+		log_sent_gpf(pkt, pktlen);
+		return;
+	case RVT_AT_HEADER:
+		log_sent_ati(pkt, pktlen);
+		return;
+	default:
+		log_sent_other(pkt, pktlen);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/openport.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,82 @@
+/*
+ * This module takes care of opening the serial port and setting the
+ * proper "raw" termios modes, including the baud rate.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+int target_fd;
+char *baudrate_name = "115200";
+
+static struct baudrate {
+	char	*name;
+	speed_t	termios_code;
+} baud_rate_table[] = {
+	{"19200",	B19200},
+	{"38400",	B38400},
+	{"57600",	B57600},
+	{"115200",	B115200},
+	/* non-standard high baud rates "remapped" by CP2102 usb2serial IC */
+	{"203125",	B230400},
+	{"406250",	B460800},
+	{"812500",	B921600},
+	/* table search terminator */
+	{NULL,		B0}
+};
+
+static speed_t
+get_baud_rate()
+{
+	struct baudrate *br;
+
+	for (br = baud_rate_table; br->name; br++)
+		if (!strcmp(br->name, baudrate_name))
+			break;
+	if (!br->name) {
+		fprintf(stderr, "baud rate \"%s\" unknown/unsupported\n",
+			baudrate_name);
+		exit(1);
+	}
+	return br->termios_code;
+}
+
+open_target_serial(ttydev)
+	char *ttydev;
+{
+	struct termios target_termios;
+	speed_t br_code;
+
+	br_code = get_baud_rate();
+	target_fd = open(ttydev, O_RDWR|O_NONBLOCK);
+	if (target_fd < 0) {
+		perror(ttydev);
+		exit(1);
+	}
+	target_termios.c_iflag = IGNBRK;
+	target_termios.c_oflag = 0;
+	target_termios.c_cflag = CLOCAL|HUPCL|CREAD|CS8;
+	target_termios.c_lflag = 0;
+	target_termios.c_cc[VMIN] = 1;
+	target_termios.c_cc[VTIME] = 0;
+	cfsetispeed(&target_termios, br_code);
+	cfsetospeed(&target_termios, br_code);
+	if (tcsetattr(target_fd, TCSAFLUSH, &target_termios) < 0) {
+		perror("initial tcsetattr on target");
+		exit(1);
+	}
+	return 0;
+}
+
+set_serial_nonblock(state)
+	int state;
+{
+	ioctl(target_fd, FIONBIO, &state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/output.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,38 @@
+/*
+ * This module implements the output/logging function
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+extern int no_output;
+extern FILE *logF;
+extern time_t logtime;
+
+static struct tm last_tm;
+
+void
+output_line(item)
+	char *item;
+{
+	struct tm *curtm;
+
+	if (!no_output)
+		printf("%s\n", item);
+	if (!logF)
+		return;
+	curtm = gmtime(&logtime);
+	if (curtm->tm_year != last_tm.tm_year ||
+	    curtm->tm_mon != last_tm.tm_mon ||
+	    curtm->tm_mday != last_tm.tm_mday)
+		fprintf(logF, "%d-%02d-%02d (gmtime):\n", curtm->tm_year + 1900,
+			curtm->tm_mon+1, curtm->tm_mday);
+	fprintf(logF, "[%02d:%02d:%02d] %s\n", curtm->tm_hour, curtm->tm_min,
+		curtm->tm_sec, item);
+	bcopy(curtm, &last_tm, sizeof(struct tm));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/packetrx.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,87 @@
+/*
+ * This module handles the lowest level of serial packet Rx
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+
+extern int target_fd;
+
+u_char rxpkt[MAX_PKT_FROM_TARGET];
+size_t rxpkt_len;
+
+static int in_pkt, dle_state, toobig;
+
+static void
+process_inbyte(inb)
+{
+	char errbuf[128];
+
+	if (!in_pkt) {
+		if (inb != STX || dle_state) {
+			rxpkt_len++;
+			dle_state = (inb == DLE);
+			return;
+		}
+		if (rxpkt_len) {
+			sprintf(errbuf,
+				"Warning: Rx %u byte%s outside of a packet",
+				(unsigned)rxpkt_len, rxpkt_len != 1 ? "s" : "");
+			output_line(errbuf);
+			rxpkt_len = 0;
+		}
+		in_pkt = 1;
+		toobig = 0;
+		return;
+	}
+	if (dle_state) {
+		dle_state = 0;
+		if (inb != STX && inb != DLE) {
+			sprintf(errbuf,
+				"Rx framing error: %02X after DLE", inb);
+			output_line(errbuf);
+			in_pkt = 0;
+			rxpkt_len = 0;
+			return;
+		}
+		goto data;
+	}
+	if (inb == DLE) {
+		dle_state = 1;
+		return;
+	} else if (inb == STX) {
+		if (!rxpkt_len)
+			return;
+		in_pkt = 0;
+		handle_rx_packet();
+		rxpkt_len = 0;
+		return;
+	}
+data:	if (rxpkt_len >= MAX_PKT_FROM_TARGET) {
+		if (!toobig) {
+			output_line("Error: Rx packet too big!");
+			toobig = 1;
+		}
+		return;
+	}
+	rxpkt[rxpkt_len++] = inb;
+}
+
+void
+process_serial_rx()
+{
+	u_char rdbuf[512];
+	int cc, i;
+
+	cc = read(target_fd, rdbuf, sizeof rdbuf);
+	if (cc <= 0) {
+		perror("Error/EOF reading from target");
+		exit(1);
+	}
+	for (i = 0; i < cc; i++)
+		process_inbyte(rdbuf[i]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/packettx.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,49 @@
+/*
+ * This module handles the lowest level of serial packet Tx
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+
+extern int target_fd;
+extern int wakeup_after_sec;
+
+static u_char wakeup_shot[64];
+static struct timeval last_tx;
+
+send_pkt_to_target(pkt, pktlen)
+	u_char *pkt;
+{
+	u_char buf[MAX_PKT_TO_TARGET*2+2];
+	u_char *cp, *dp, *endp;
+	int c;
+	struct timeval curtime, timediff;
+
+	gettimeofday(&curtime, 0);
+	if (wakeup_after_sec) {
+		timersub(&curtime, &last_tx, &timediff);
+		if (timediff.tv_sec >= wakeup_after_sec) {
+			write(target_fd, wakeup_shot, sizeof wakeup_shot);
+			usleep(100000);
+		}
+	}
+	endp = pkt + pktlen;
+	dp = buf;
+	*dp++ = STX;
+	for (cp = pkt; cp < endp; cp++) {
+		c = *cp;
+		if (c == STX || c == DLE)
+			*dp++ = DLE;
+		*dp++ = c;
+	}
+	*dp++ = STX;
+	write(target_fd, buf, dp - buf);
+	bcopy(&curtime, &last_tx, sizeof(struct timeval));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/pktfwd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,62 @@
+/*
+ * This rvinterf module handles the forwarding of target->host packets
+ * to clients connected via the local socket interface.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../include/pktmux.h"
+#include "../include/localsock.h"
+#include "client.h"
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+extern struct client *client_head;
+
+forward_pkt_to_client(cli)
+	struct client *cli;
+{
+	int len1;
+	u_char hdr[3];
+
+	len1 = rxpkt_len + 1;
+	hdr[0] = len1 >> 8;
+	hdr[1] = len1 & 0xFF;
+	hdr[2] = RVI2CLI_PKT_FROM_TARGET;
+	write(cli->fd, hdr, 3);
+	write(cli->fd, rxpkt, rxpkt_len);
+}
+
+forward_rv_trace()
+{
+	u32 useid;
+	struct client *cli;
+	int i, match;
+
+	useid = rxpkt[1] << 24 | rxpkt[2] << 16 | rxpkt[3] << 8 | rxpkt[4];
+	for (cli = client_head; cli; cli = cli->next) {
+		match = 0;
+		for (i = 0; i < cli->int_rvt_count; i++)
+			if ((useid & cli->int_rvt_mask[i]) ==
+			    cli->int_rvt_match[i]) {
+				match = 1;
+				break;
+			}
+		if (match)
+			forward_pkt_to_client(cli);
+	}
+}
+
+forward_nonrvt_pkt()
+{
+	struct client *cli;
+
+	for (cli = client_head; cli; cli = cli->next)
+		if (cli->int_proto[rxpkt[0] - 0x12])
+			forward_pkt_to_client(cli);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/rviflcd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,40 @@
+/*
+ * This rvinterf module implements the piping of LCD output to fc-lcdemu
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+char *extlcd_program;
+FILE *extlcd_pout;
+u_char extlcd_invert;
+
+void
+open_extlcd_pipe()
+{
+	extlcd_pout = popen(extlcd_program, "w");
+	if (!extlcd_pout) {
+		perror(extlcd_program);
+		exit(1);
+	}
+}
+
+void
+output_to_extlcd()
+{
+	int i;
+
+	fprintf(extlcd_pout, "%u %u ", rxpkt[1], rxpkt[2]);
+	for (i = 3; i < rxpkt_len; i += 2)
+		fprintf(extlcd_pout, "%02X%02X", rxpkt[i+1] ^ extlcd_invert,
+			rxpkt[i] ^ extlcd_invert);
+	fputc('\n', extlcd_pout);
+	fflush(extlcd_pout);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/rvifmain.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,213 @@
+/*
+ * This module contains the main() function for rvinterf
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include "../include/pktmux.h"
+#include "../include/localsock.h"
+#include "client.h"
+
+extern int target_fd, listener;
+extern char *baudrate_name;
+extern char *extlcd_program;
+extern u_char extlcd_invert;
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+struct client *client_head;
+int socketpair_fd;
+
+char *logfname;
+FILE *logF;
+time_t logtime;
+int background, no_output;
+int max_fd;
+
+char *socket_pathname = "/tmp/rvinterf_socket";
+
+int wakeup_after_sec = 7;
+
+main(argc, argv)
+	char **argv;
+{
+	extern char *optarg;
+	extern int optind;
+	int c;
+	fd_set fds;
+	struct client *cli, **clip;
+
+	while ((c = getopt(argc, argv, "bB:d:l:ns:S:vw:X:")) != EOF)
+		switch (c) {
+		case 'b':
+			background++;
+			/* FALL THRU */
+		case 'n':
+			no_output++;
+			continue;
+		case 'B':
+			baudrate_name = optarg;
+			continue;
+		case 'd':
+			target_fd = atoi(optarg);
+			continue;
+		case 'l':
+			logfname = optarg;
+			continue;
+		case 's':
+			socket_pathname = optarg;
+			continue;
+		case 'S':
+			socketpair_fd = atoi(optarg);
+			continue;
+		case 'v':
+			extlcd_invert = 0xFF;
+			continue;
+		case 'w':
+			wakeup_after_sec = strtoul(optarg, 0, 0);
+			continue;
+		case 'X':
+			extlcd_program = optarg;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: %s [options] ttyport\n", argv[0]);
+			exit(1);
+		}
+	if (target_fd <= 0) {
+		if (argc - optind != 1)
+			goto usage;
+		open_target_serial(argv[optind]);
+	}
+	max_fd = target_fd;
+	if (extlcd_program)
+		open_extlcd_pipe();
+
+	set_serial_nonblock(0);
+	setlinebuf(stdout);
+	if (logfname) {
+		logF = fopen(logfname, "w");
+		if (!logF) {
+			perror(logfname);
+			exit(1);
+		}
+		setlinebuf(logF);
+		fprintf(logF, "*** Log of rvinterf session ***\n");
+	}
+	if (socketpair_fd)
+		create_socketpair_client();
+	else
+		create_listener_socket();
+	if (background) {
+		c = fork();
+		if (c < 0) {
+			perror("fork");
+			exit(1);
+		}
+		if (c) {
+			printf("rvinterf forked into background (pid %d)\n", c);
+			exit(0);
+		}
+	}
+	signal(SIGPIPE, SIG_IGN);
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		if (listener)
+			FD_SET(listener, &fds);
+		for (clip = &client_head; cli = *clip; ) {
+			if (cli->rx_state == 2) {
+				close(cli->fd);
+				*clip = cli->next;
+				free(cli);
+				continue;
+			}
+			FD_SET(cli->fd, &fds);
+			clip = &cli->next;
+		}
+		if (socketpair_fd && !client_head)
+			exit(0);
+		c = select(max_fd+1, &fds, 0, 0, 0);
+		time(&logtime);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(target_fd, &fds))
+			process_serial_rx();
+		if (listener && FD_ISSET(listener, &fds))
+			handle_listener_select();
+		for (cli = client_head; cli; cli = cli->next)
+			if (FD_ISSET(cli->fd, &fds))
+				handle_client_select(cli);
+	}
+}
+
+handle_rx_packet()
+{
+	switch (rxpkt[0]) {
+	case RVT_RV_HEADER:
+		if (rxpkt_len < 6)
+			goto unknown;
+		if (!no_output || logF)
+			print_rv_trace();
+		if (client_head)
+			forward_rv_trace();
+		return;
+	case RVT_L1_HEADER:
+		if (!no_output || logF)
+			print_l1_trace();
+		if (client_head)
+			forward_nonrvt_pkt();
+		return;
+	case RVT_L23_HEADER:
+		if (!no_output || logF)
+			print_g23_trace();
+		if (client_head)
+			forward_nonrvt_pkt();
+		return;
+	case RVT_TM_HEADER:
+		if (!no_output || logF)
+			print_tm_output_raw();
+		if (client_head)
+			forward_nonrvt_pkt();
+		return;
+	case RVT_AT_HEADER:
+		if (!no_output || logF)
+			print_ati_output();
+		if (client_head)
+			forward_nonrvt_pkt();
+		return;
+	case RVT_EXTUI_HEADER:
+		if (rxpkt_len < 5 || !(rxpkt_len & 1))
+			goto unknown;
+		if (extlcd_program)
+			output_to_extlcd();
+		else
+			report_extui_packet();
+		return;
+	case RVT_TCH_HEADER:
+		if (!no_output || logF)
+			print_tch_output_raw();
+		if (client_head)
+			forward_nonrvt_pkt();
+		return;
+	case '*':
+		print_fc_lld_msg();
+		return;
+	default:
+	unknown:
+		print_unknown_packet();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/rvtdump.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,138 @@
+/*
+ * This program reads bytes from a serial port, parses them assuming
+ * TI's RVT MUX format, and prints every decoded packet.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include "../include/pktmux.h"
+
+extern int target_fd;
+extern char *baudrate_name;
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+char *logfname;
+FILE *logF;
+time_t logtime;
+int background;
+int no_output;	/* for output.c */
+
+main(argc, argv)
+	char **argv;
+{
+	extern char *optarg;
+	extern int optind;
+	int c;
+	fd_set fds;
+
+	while ((c = getopt(argc, argv, "bB:d:l:")) != EOF)
+		switch (c) {
+		case 'b':
+			background++;
+			no_output++;	/* for output.c */
+			continue;
+		case 'B':
+			baudrate_name = optarg;
+			continue;
+		case 'd':
+			target_fd = atoi(optarg);
+			continue;
+		case 'l':
+			logfname = optarg;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: %s [options] ttyport\n", argv[0]);
+			exit(1);
+		}
+	if (background && !logfname) {
+		fprintf(stderr, "%s: -b is meaningless without -l\n", argv[0]);
+		exit(1);
+	}
+	if (target_fd <= 0) {
+		if (argc - optind != 1)
+			goto usage;
+		open_target_serial(argv[optind]);
+	}
+
+	set_serial_nonblock(0);
+	setlinebuf(stdout);
+	if (logfname) {
+		logF = fopen(logfname, "w");
+		if (!logF) {
+			perror(logfname);
+			exit(1);
+		}
+		setlinebuf(logF);
+		fprintf(logF, "*** Log of decoded RVT output ***\n");
+	}
+	if (background) {
+		c = fork();
+		if (c < 0) {
+			perror("fork");
+			exit(1);
+		}
+		if (c) {
+			printf("rvtdump forked into background (pid %d)\n", c);
+			exit(0);
+		}
+	}
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		c = select(target_fd+1, &fds, 0, 0, 0);
+		time(&logtime);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(target_fd, &fds))
+			process_serial_rx();
+	}
+}
+
+handle_rx_packet()
+{
+	switch (rxpkt[0]) {
+	case RVT_RV_HEADER:
+		if (rxpkt_len < 6)
+			goto unknown;
+		print_rv_trace();
+		return;
+	case RVT_L1_HEADER:
+		print_l1_trace();
+		return;
+	case RVT_L23_HEADER:
+		print_g23_trace();
+		return;
+	case RVT_TM_HEADER:
+		print_tm_output_raw();
+		return;
+	case RVT_AT_HEADER:
+		print_ati_output();
+		return;
+	case RVT_EXTUI_HEADER:
+		if (rxpkt_len < 5 || !(rxpkt_len & 1))
+			goto unknown;
+		report_extui_packet();
+		return;
+	case RVT_TCH_HEADER:
+		print_tch_output_raw();
+		return;
+	case '*':
+		print_fc_lld_msg();
+		return;
+	default:
+	unknown:
+		print_unknown_packet();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/lowlevel/tfc139.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,311 @@
+/*
+ * This program facilitates the recovery of those Compal/Motorola phones
+ * whose bootloaders have been maliciously locked down.  It connects
+ * to a running Mot C1xx firmware through the RVTMUX interface provided
+ * by the latter and uses the Test Mode memory write command (which
+ * these firmwares implement just like TI's reference fw) to inject
+ * some shellcode and to transfer control to it by overwriting a
+ * function return address on the stack.  The injected shellcode then
+ * enables the Calypso boot ROM and jumps to it, allowing fc-loadtool
+ * to take over from there.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+
+extern int target_fd;
+extern char *baudrate_name;
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+char *logfname;
+FILE *logF;
+time_t logtime;
+int no_output;	/* for output.c */
+
+int wakeup_after_sec = 1;
+
+/* see ../../target-utils/tf-breakin/payload.S for the source */
+static u_char shellcode[114] = {
+	0x78, 0x47, 0xC0, 0x46, 0xD3, 0xF0, 0x21, 0xE3,
+	0x50, 0x10, 0x9F, 0xE5, 0xF5, 0x00, 0xA0, 0xE3,
+	0xB2, 0x00, 0xC1, 0xE1, 0xA0, 0x00, 0xA0, 0xE3,
+	0xB2, 0x00, 0xC1, 0xE1, 0x40, 0x60, 0x9F, 0xE5,
+	0x05, 0x00, 0xD6, 0xE5, 0x20, 0x00, 0x10, 0xE3,
+	0xFC, 0xFF, 0xFF, 0x0A, 0x38, 0x10, 0x8F, 0xE2,
+	0x06, 0x20, 0xA0, 0xE3, 0x01, 0x00, 0xD1, 0xE4,
+	0x00, 0x00, 0xC6, 0xE5, 0x01, 0x20, 0x52, 0xE2,
+	0xFB, 0xFF, 0xFF, 0x1A, 0x05, 0x00, 0xD6, 0xE5,
+	0x40, 0x00, 0x10, 0xE3, 0xFC, 0xFF, 0xFF, 0x0A,
+	0x10, 0x10, 0x9F, 0xE5, 0x01, 0x2C, 0xA0, 0xE3,
+	0xB0, 0x20, 0xC1, 0xE1, 0x00, 0xF0, 0xA0, 0xE3,
+	0x02, 0xF8, 0xFF, 0xFF, 0x00, 0x58, 0xFF, 0xFF,
+	0x10, 0xFB, 0xFF, 0xFF, 0x02, 0x02, 0x02, 0x4F,
+	0x4B, 0x02
+};
+
+static unsigned shellcode_load_addr;
+static unsigned stack_smash_addr;
+static int thumb_entry = 1;
+
+static u_char stack_smash_payload[4];
+static int breakin_in_progress;
+
+static char *target_tty_port;
+
+static void
+send_compal_memwrite(addr, payload, payload_len)
+	unsigned addr;
+	u_char *payload;
+{
+	u_char pkt[MAX_PKT_TO_TARGET];
+	int i, csum, csum_offset;
+
+	pkt[0] = RVT_TM_HEADER;
+	pkt[1] = 0x40;		/* old TM3 MEM_WRITE command */
+	pkt[2] = addr;
+	pkt[3] = addr >> 8;
+	pkt[4] = addr >> 16;
+	pkt[5] = addr >> 24;
+	bcopy(payload, pkt + 6, payload_len);
+	csum_offset = payload_len + 6;
+	csum = 0;
+	for (i = 1; i < csum_offset; i++)
+		csum ^= pkt[i];
+	pkt[i] = csum;
+	send_pkt_to_target(pkt, i + 1);
+}
+
+static void
+initiate_breakin()
+{
+	char msgbuf[80];
+	unsigned jump_addr;
+
+	sprintf(msgbuf,
+	      "Using shellcode load addr 0x%x, stack smash starting addr 0x%x",
+		shellcode_load_addr, stack_smash_addr);
+	output_line(msgbuf);
+	jump_addr = shellcode_load_addr;
+	if (thumb_entry)
+		jump_addr += 1;
+	else
+		jump_addr += 4;
+	stack_smash_payload[0] = jump_addr;
+	stack_smash_payload[1] = jump_addr >> 8;
+	stack_smash_payload[2] = jump_addr >> 16;
+	stack_smash_payload[3] = jump_addr >> 24;
+	output_line("Sending shellcode RAM write");
+	send_compal_memwrite(shellcode_load_addr, shellcode, sizeof shellcode);
+	breakin_in_progress = 1;
+}
+
+static void
+send_memcheck_query()
+{
+	u_char sendpkt[25];
+
+	output_line("Sending GPF MEMCHECK query");
+	/* fill out the packet */
+	sendpkt[0] = RVT_L23_HEADER;
+	sendpkt[1] = 0xB7;	/* system prim */
+	sendpkt[2] = 20;
+	sendpkt[3] = 0;
+	/* send zeros for the timestamp */
+	sendpkt[4] = 0;
+	sendpkt[5] = 0;
+	sendpkt[6] = 0;
+	sendpkt[7] = 0;
+	/* fixed string with all fields */
+	strcpy(sendpkt + 8, "PCO L1  MEMCHECK");
+	/* send it! */
+	send_pkt_to_target(sendpkt, 24);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	extern char *optarg;
+	extern int optind;
+	int c;
+	fd_set fds;
+
+	baudrate_name = "57600";	/* what C139 firmware uses */
+	while ((c = getopt(argc, argv, "a:AB:l:ms:w:")) != EOF)
+		switch (c) {
+		case 'a':
+			shellcode_load_addr = strtoul(optarg, 0, 16);
+			continue;
+		case 'B':
+			baudrate_name = optarg;
+			continue;
+		case 'l':
+			logfname = optarg;
+			continue;
+		case 'm':
+			/* mimic mot931c.exe */
+			shellcode_load_addr = 0x800000;
+			stack_smash_addr = 0x837C54;
+			/* FALL THRU */
+		case 'A':
+			thumb_entry = 0;
+			continue;
+		case 's':
+			stack_smash_addr = strtoul(optarg, 0, 16);
+			continue;
+		case 'w':
+			wakeup_after_sec = strtoul(optarg, 0, 0);
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: %s [options] ttyport\n", argv[0]);
+			exit(1);
+		}
+	if (argc - optind != 1)
+		goto usage;
+	if (stack_smash_addr && !shellcode_load_addr) {
+		fprintf(stderr, "usage error: -a option required with -s\n");
+		exit(1);
+	}
+	open_target_serial(argv[optind]);
+	target_tty_port = argv[optind];
+
+	set_serial_nonblock(0);
+	setlinebuf(stdout);
+	if (logfname) {
+		logF = fopen(logfname, "w");
+		if (!logF) {
+			perror(logfname);
+			exit(1);
+		}
+		setlinebuf(logF);
+		fprintf(logF, "*** Log of TFC139 break-in session ***\n");
+	}
+	time(&logtime);
+	if (stack_smash_addr)
+		initiate_breakin();
+	else
+		send_memcheck_query();
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		c = select(target_fd+1, &fds, 0, 0, 0);
+		time(&logtime);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(target_fd, &fds))
+			process_serial_rx();
+	}
+}
+
+static void
+handle_tm_response()
+{
+	char msgbuf[80];
+
+	if (!breakin_in_progress) {
+		output_line("TM response unexpected at this time");
+		return;
+	}
+	if (rxpkt_len != 4 || rxpkt[1] != 0x40 || rxpkt[2] || rxpkt[3] != 0x40){
+		output_line("TM response differs from expected");
+		return;
+	}
+	sprintf(msgbuf, "Sending stack smash write at 0x%x", stack_smash_addr);
+	output_line(msgbuf);
+	send_compal_memwrite(stack_smash_addr, stack_smash_payload, 4);
+	stack_smash_addr += 4;
+}
+
+static void
+analyze_gpf_packet()
+{
+	unsigned stackbase, untouched;
+	static char format[] =
+	  "Name:L1 Stat:%*s Count:%*s Prio:%*s Stack:%x Size:%*s Untouched:%u";
+	char msgbuf[80];
+
+	if (rxpkt_len < 17 || rxpkt_len > 128)
+		return;
+	/* it needs to be a trace packet */
+	if ((rxpkt[1] & 0xF0) != 0xA0)
+		return;
+	/* check the length */
+	if (rxpkt[2] + 4 != rxpkt_len)
+		return;
+	if (rxpkt[3])
+		return;
+	/* skip timestamp, check src and dest */
+	if (strncmp(rxpkt + 8, "SYSTPCO ", 8))
+		return;
+	/* terminating NUL for sscanf */
+	rxpkt[rxpkt_len] = '\0';
+	if (sscanf(rxpkt + 16, format, &stackbase, &untouched) != 2)
+		return;
+	/* success! */
+	sprintf(msgbuf,
+		"Parsed L1 stack location: base=0x%x, untouched=%u (0x%x)",
+		stackbase, untouched, untouched);
+	output_line(msgbuf);
+	if (stackbase & 3) {
+		output_line("Error: stack base address is not word-aligned");
+		exit(1);
+	}
+	untouched &= ~3;
+	if (!shellcode_load_addr) {
+		if (untouched < sizeof shellcode) {
+			output_line("Error: not enough room for shellcode");
+			exit(1);
+		}
+		shellcode_load_addr = stackbase;
+	}
+	stack_smash_addr = stackbase + untouched;
+	initiate_breakin();
+}
+
+handle_rx_packet()
+{
+	if (rxpkt_len == 2 && rxpkt[0] == 'O' && rxpkt[1] == 'K') {
+		output_line(
+		"Success: target should now be in boot ROM download wait");
+		printf("You can now run fc-loadtool -h compal -c none %s\n",
+			target_tty_port);
+		exit(0);
+	}
+	switch (rxpkt[0]) {
+	case RVT_RV_HEADER:
+		if (rxpkt_len < 6)
+			goto unknown;
+		print_rv_trace();
+		return;
+	case RVT_L1_HEADER:
+		print_l1_trace();
+		return;
+	case RVT_L23_HEADER:
+		print_g23_trace();
+		if (!breakin_in_progress)
+			analyze_gpf_packet();
+		return;
+	case RVT_TM_HEADER:
+		print_tm_output_raw();
+		handle_tm_response();
+		return;
+	default:
+	unknown:
+		print_unknown_packet();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,23 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	rvtdump
+XPROGS=	etmsend
+INSTBIN=/usr/local/bin
+
+RVTDUMP_OBJS=	log.o openport.o packetrx.o packettx.o rvtdump.o rvtdump_tx.o \
+		trdump.o
+
+all:	${PROGS} ${XPROGS}
+
+rvtdump:	${RVTDUMP_OBJS}
+	${CC} ${CFLAGS} -o $@ ${RVTDUMP_OBJS}
+
+etmsend:	etmsend.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+install:	${PROGS}
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROGS} ${XPROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/etmsend.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,79 @@
+/*
+ * This program is a hack that sends a hand-crafted ETM packet
+ * to the UNIX-local dgram socket established by rvtdump with -s option.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pktmux.h"
+#include "txpkt.h"
+
+char sockpath[] = "/tmp/rvt_send_socket";
+
+u_char packet[MAX_PKT_TO_TARGET];
+int payload_len;
+
+main(argc, argv)
+	char **argv;
+{
+	int i, c, s;
+	struct sockaddr_un local;
+	unsigned int namelen;
+
+	if (argc < 2) {
+		fprintf(stderr, "usage: %s hexbytes...\n", argv[0]);
+		exit(1);
+	}
+	payload_len = argc - 1;
+	if (payload_len > MAX_PKT_TO_TARGET-2) {
+		fprintf(stderr,
+			"%s: too many bytes (packet length limit exceeded)\n",
+			argv[0]);
+		exit(1);
+	}
+
+	packet[0] = RVT_TM_HEADER;
+	for (i = 1; i <= payload_len; i++)
+		packet[i] = strtoul(argv[i], 0, 16);
+	c = 0;
+	for (i = 1; i <= payload_len; i++)
+		c ^= packet[i];
+	packet[payload_len+1] = c;
+
+	s = socket(AF_UNIX, SOCK_DGRAM, 0);
+	if (s < 0) {
+		perror("socket(AF_UNIX, SOCK_DGRAM, 0)");
+		exit(1);
+	}
+
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, sockpath, sizeof(local.sun_path));
+	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	namelen = SUN_LEN(&local);
+#else
+	namelen = strlen(local.sun_path) +
+		  offsetof(struct sockaddr_un, sun_path);
+#endif
+
+	i = sendto(s, packet, payload_len+2, 0,
+			(struct sockaddr *) &local, namelen);
+	if (i < 0) {
+		perror("sendto");
+		exit(1);
+	}
+
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/log.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,50 @@
+/*
+ * This module implements the logging function
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+extern char pr_item[];
+
+extern FILE *logF;
+extern time_t logtime;
+
+static struct tm last_tm;
+
+log_item()
+{
+	struct tm *curtm;
+
+	curtm = gmtime(&logtime);
+	if (curtm->tm_year != last_tm.tm_year ||
+	    curtm->tm_mon != last_tm.tm_mon ||
+	    curtm->tm_mday != last_tm.tm_mday)
+		fprintf(logF, "%d-%02d-%02d (gmtime):\n", curtm->tm_year + 1900,
+			curtm->tm_mon+1, curtm->tm_mday);
+	fprintf(logF, "[%02d:%02d:%02d] %s\n", curtm->tm_hour, curtm->tm_min,
+		curtm->tm_sec, pr_item);
+	bcopy(curtm, &last_tm, sizeof(struct tm));
+}
+
+log_sent_packet(pkt, pktlen)
+	u_char *pkt;
+{
+	int i;
+	char *dp;
+
+	dp = pr_item;
+	strcpy(dp, "Sent");
+	dp += 4;
+	for (i = 0; i < pktlen; i++) {
+		sprintf(dp, " %02X", pkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	print_item();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/openport.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,82 @@
+/*
+ * This module takes care of opening the serial port and setting the
+ * proper "raw" termios modes, including the baud rate.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+int target_fd;
+char *baudrate_name = "115200";
+
+static struct baudrate {
+	char	*name;
+	speed_t	termios_code;
+} baud_rate_table[] = {
+	{"19200",	B19200},
+	{"38400",	B38400},
+	{"57600",	B57600},
+	{"115200",	B115200},
+	/* non-standard high baud rates "remapped" by CP2102 usb2serial IC */
+	{"203125",	B230400},
+	{"406250",	B460800},
+	{"812500",	B921600},
+	/* table search terminator */
+	{NULL,		B0}
+};
+
+static speed_t
+get_baud_rate()
+{
+	struct baudrate *br;
+
+	for (br = baud_rate_table; br->name; br++)
+		if (!strcmp(br->name, baudrate_name))
+			break;
+	if (!br->name) {
+		fprintf(stderr, "baud rate \"%s\" unknown/unsupported\n",
+			baudrate_name);
+		exit(1);
+	}
+	return br->termios_code;
+}
+
+open_target_serial(ttydev)
+	char *ttydev;
+{
+	struct termios target_termios;
+	speed_t br_code;
+
+	br_code = get_baud_rate();
+	target_fd = open(ttydev, O_RDWR|O_NONBLOCK);
+	if (target_fd < 0) {
+		perror(ttydev);
+		exit(1);
+	}
+	target_termios.c_iflag = IGNBRK;
+	target_termios.c_oflag = 0;
+	target_termios.c_cflag = CLOCAL|HUPCL|CREAD|CS8;
+	target_termios.c_lflag = 0;
+	target_termios.c_cc[VMIN] = 1;
+	target_termios.c_cc[VTIME] = 0;
+	cfsetispeed(&target_termios, br_code);
+	cfsetospeed(&target_termios, br_code);
+	if (tcsetattr(target_fd, TCSAFLUSH, &target_termios) < 0) {
+		perror("initial tcsetattr on target");
+		exit(1);
+	}
+	return 0;
+}
+
+set_serial_nonblock(state)
+	int state;
+{
+	ioctl(target_fd, FIONBIO, &state);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/packetrx.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,88 @@
+/*
+ * This module handles the lowest level of serial packet Rx
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pktmux.h"
+
+extern int target_fd;
+
+#define	MAXPKT	512
+u_char rxpkt[MAXPKT];
+size_t rxpkt_len;
+
+extern char pr_item[];
+
+static int in_pkt, dle_state, toobig;
+
+static void
+process_inbyte(inb)
+{
+	if (!in_pkt) {
+		if (inb != STX || dle_state) {
+			rxpkt_len++;
+			dle_state = (inb == DLE);
+			return;
+		}
+		if (rxpkt_len) {
+			sprintf(pr_item,
+				"Warning: Rx %u byte%s outside of a packet",
+				(unsigned)rxpkt_len, rxpkt_len != 1 ? "s" : "");
+			print_item();
+			rxpkt_len = 0;
+		}
+		in_pkt = 1;
+		toobig = 0;
+		return;
+	}
+	if (dle_state) {
+		dle_state = 0;
+		if (inb != STX && inb != DLE) {
+			sprintf(pr_item,
+				"Rx framing error: %02X after DLE\n", inb);
+			print_item();
+			in_pkt = 0;
+			rxpkt_len = 0;
+			return;
+		}
+		goto data;
+	}
+	if (inb == DLE) {
+		dle_state = 1;
+		return;
+	} else if (inb == STX) {
+		if (!rxpkt_len)
+			return;
+		in_pkt = 0;
+		handle_rx_packet();
+		rxpkt_len = 0;
+		return;
+	}
+data:	if (rxpkt_len >= MAXPKT) {
+		if (!toobig) {
+			sprintf(pr_item, "Error: Rx packet too big!\n");
+			print_item();
+			toobig = 1;
+		}
+		return;
+	}
+	rxpkt[rxpkt_len++] = inb;
+}
+
+void
+process_serial_rx()
+{
+	u_char rdbuf[512];
+	int cc, i;
+
+	cc = read(target_fd, rdbuf, sizeof rdbuf);
+	if (cc <= 0) {
+		perror("Error/EOF reading from target");
+		exit(1);
+	}
+	for (i = 0; i < cc; i++)
+		process_inbyte(rdbuf[i]);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/packettx.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,32 @@
+/*
+ * This module handles the lowest level of serial packet Tx
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "pktmux.h"
+#include "txpkt.h"
+
+extern int target_fd;
+
+send_pkt_to_target(pkt, pktlen)
+	u_char *pkt;
+{
+	u_char buf[MAX_PKT_TO_TARGET*2+2];
+	u_char *cp, *dp, *endp;
+	int c;
+
+	endp = pkt + pktlen;
+	dp = buf;
+	*dp++ = STX;
+	for (cp = pkt; cp < endp; cp++) {
+		c = *cp;
+		if (c == STX || c == DLE)
+			*dp++ = DLE;
+		*dp++ = c;
+	}
+	*dp++ = STX;
+	write(target_fd, buf, dp - buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/pktmux.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,16 @@
+/*
+ * Definitions for the RVT MUX over-the-wire protocol
+ */
+
+#define	STX	0x02
+#define	DLE	0x10
+
+#define RVT_RV_HEADER        0x11
+#define RVT_L1_HEADER        0x12
+#define RVT_L23_HEADER       0x13
+#define RVT_TM_HEADER        0x14
+#define RVT_RNET_HEADER      0x15
+#define RVT_PROF_HEADER      0x16
+#define RVT_GTTBACK_HEADER   0x17
+#define RVT_OTHER_HEADER     0x18
+#define RVT_INVALID_HEADER   0xFF
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/rvtdump.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,123 @@
+/*
+ * This program reads bytes from a serial port, parses them assuming
+ * TI's RVT MUX format, and prints every decoded packet.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+
+extern int target_fd;
+extern char *baudrate_name;
+
+extern char pr_item[];
+extern int sendsock_fd;
+
+char *logfname;
+FILE *logF;
+time_t logtime;
+int background, sendsock_enable;
+
+main(argc, argv)
+	char **argv;
+{
+	extern char *optarg;
+	extern int optind;
+	int c, maxfd;
+	fd_set fds;
+
+	while ((c = getopt(argc, argv, "bB:d:l:s")) != EOF)
+		switch (c) {
+		case 'b':
+			background++;
+			continue;
+		case 'B':
+			baudrate_name = optarg;
+			continue;
+		case 'd':
+			target_fd = atoi(optarg);
+			continue;
+		case 'l':
+			logfname = optarg;
+			continue;
+		case 's':
+			sendsock_enable++;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+			"usage: %s [options] ttyport\n", argv[0]);
+			exit(1);
+		}
+	if (background && !logfname) {
+		fprintf(stderr, "%s: -b is meaningless without -l\n", argv[0]);
+		exit(1);
+	}
+	if (target_fd <= 0) {
+		if (argc - optind != 1)
+			goto usage;
+		open_target_serial(argv[optind]);
+	}
+
+	set_serial_nonblock(0);
+	setlinebuf(stdout);
+	if (logfname) {
+		logF = fopen(logfname, "w");
+		if (!logF) {
+			perror(logfname);
+			exit(1);
+		}
+		fprintf(logF, "*** Log of decoded RVT output ***\n");
+		setlinebuf(logF);
+	}
+	if (sendsock_enable)
+		create_send_socket();
+	if (background) {
+		c = fork();
+		if (c < 0) {
+			perror("fork");
+			exit(1);
+		}
+		if (c) {
+			printf("rvtdump forked into background (pid %d)\n", c);
+			exit(0);
+		}
+	}
+	maxfd = target_fd;
+	if (sendsock_fd > maxfd)
+		maxfd = sendsock_fd;
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(target_fd, &fds);
+		if (sendsock_enable)
+			FD_SET(sendsock_fd, &fds);
+		c = select(maxfd+1, &fds, 0, 0, 0);
+		time(&logtime);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(target_fd, &fds))
+			process_serial_rx();
+		if (FD_ISSET(sendsock_fd, &fds))
+			handle_sendsock();
+	}
+}
+
+handle_rx_packet()
+{
+	print_rx_packet();
+}
+
+print_item()
+{
+	if (!background)
+		printf("%s\n", pr_item);
+	if (logF)
+		log_item();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/rvtdump_tx.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,70 @@
+/*
+ * This module contains the implementation of the Tx extension to rvtdump
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "txpkt.h"
+
+static char sockpath[] = "/tmp/rvt_send_socket";
+
+int sendsock_fd;
+u_char sendsock_pkt[MAX_PKT_TO_TARGET];
+int sendsock_pktlen;
+
+create_send_socket()
+{
+	/* local socket binding voodoo copied from osmocon */
+	struct sockaddr_un local;
+	unsigned int namelen;
+	int rc;
+
+	sendsock_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+	if (sendsock_fd < 0) {
+		perror("socket(AF_UNIX, SOCK_DGRAM, 0)");
+		exit(1);
+	}
+
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, sockpath, sizeof(local.sun_path));
+	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+	unlink(local.sun_path);
+
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	namelen = SUN_LEN(&local);
+#else
+	namelen = strlen(local.sun_path) +
+		  offsetof(struct sockaddr_un, sun_path);
+#endif
+
+	rc = bind(sendsock_fd, (struct sockaddr *) &local, namelen);
+	if (rc != 0) {
+		perror("bind on local dgram socket");
+		exit(1);
+	}
+
+	return(0);
+}
+
+handle_sendsock()
+{
+	sendsock_pktlen = recv(sendsock_fd, sendsock_pkt, MAX_PKT_TO_TARGET, 0);
+	if (sendsock_pktlen <= 0) {
+		fprintf(stderr, "return value from recv on socket: %d\n",
+			sendsock_pktlen);
+		exit(1);
+	}
+	send_pkt_to_target(sendsock_pkt, sendsock_pktlen);
+	log_sent_packet(sendsock_pkt, sendsock_pktlen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/trdump.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,169 @@
+/*
+ * This module implements the basic dump of any incoming packets
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include "pktmux.h"
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+
+char pr_item[4096];
+
+void
+print_rv_trace()
+{
+	int i, c;
+	char *dp;
+
+	dp = pr_item;
+	strcpy(dp, "RV ");
+	dp += 3;
+	/* the SWE static ID is sent MSB first */
+	for (i = 1; i <= 4; i++) {
+		sprintf(dp, "%02X", rxpkt[i]);
+		dp += 2;
+	}
+	/* severity level */
+	sprintf(dp, " %d ", rxpkt[5]);
+	dp = index(dp, '\0');
+	for (i = 6; i < rxpkt_len; i++) {
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	*dp = '\0';
+	print_item();
+}
+
+void
+print_l1_trace()
+{
+	int i, c;
+	char *dp;
+
+	dp = pr_item;
+	strcpy(dp, "L1: ");
+	dp += 4;
+	for (i = 1; i < rxpkt_len; i++) {
+		if ((i+1 < rxpkt_len) &&
+		    (rxpkt[i] == '\r' && rxpkt[i+1] == '\n' ||
+		     rxpkt[i] == '\n' && rxpkt[i+1] == '\r')) {
+			*dp = '\0';
+			print_item();
+			if (i+2 == rxpkt_len)
+				return;
+			dp = pr_item;
+			*dp++ = '+';
+			*dp++ = ' ';
+			i++;
+			continue;
+		}
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*dp++ = 'M';
+			*dp++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*dp++ = '^';
+			*dp++ = c + '@';
+		} else if (c == 0x7F) {
+			*dp++ = '^';
+			*dp++ = '?';
+		} else
+			*dp++ = c;
+	}
+	/* will get here only if no newline sequence at the end */
+	*dp = '\0';
+	print_item();
+}
+
+void
+print_g23_trace()
+{
+	int i;
+	char *dp;
+
+	dp = pr_item;
+	strcpy(dp, "G23:");
+	dp += 4;
+	for (i = 1; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	print_item();
+}
+
+void
+print_etm_output_raw()
+{
+	int i;
+	char *dp;
+
+	dp = pr_item;
+	strcpy(dp, "ETM:");
+	dp += 4;
+	for (i = 1; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	print_item();
+}
+
+void
+print_unknown_packet()
+{
+	int i;
+	char *dp;
+
+	dp = pr_item;
+	strcpy(dp, "UNK:");
+	dp += 4;
+	for (i = 0; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	print_item();
+}
+
+void
+print_rx_packet()
+{
+	switch (rxpkt[0]) {
+	case RVT_RV_HEADER:
+		if (rxpkt_len < 6)
+			goto unknown;
+		print_rv_trace();
+		return;
+	case RVT_L1_HEADER:
+		print_l1_trace();
+		return;
+	case RVT_L23_HEADER:
+		print_g23_trace();
+		return;
+	case RVT_TM_HEADER:
+		print_etm_output_raw();
+		return;
+	default:
+	unknown:
+		print_unknown_packet();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/before-rvinterf/txpkt.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,15 @@
+/*
+ * For sizing our buffers etc in rvinterf, we need a limit on the size of
+ * message packets we can send to the target.  As it happens, the packet
+ * Rx code in RVT on the target side also has a limit (quite naturally,
+ * as it needs to use a static buffer to reassemble incoming packets as
+ * they arrive at the UART in unpredictable interrupt-sized chunks), so
+ * we set our limit to match that in RVT.
+ */
+
+#define	MAX_PKT_TO_TARGET	255
+
+/*
+ * The above limit definition counts all bytes between the opening and
+ * closing STX flags, but not DLEs inserted for binary transparency.
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/format_g23.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,241 @@
+/*
+ * This module implements the decoding of G23 trace packets into
+ * human-readable form - or more precisely, traces, system primitives
+ * and other packets that can be emitted through GPF on targets.
+ * The decoding is based on my (Space Falcon's) understanding
+ * of the packet format, which is in turn based on the GPF sources
+ * available to us, now integrated under gsm-fw/gpf.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include "../include/pktmux.h"
+#include "../include/limits.h"
+
+extern u_char rxpkt[];
+extern size_t rxpkt_len;
+extern char fmtbuf[];
+
+static char *fmtbuf_ptr;
+
+static int
+basic_checks()
+{
+	int i, c;
+
+	if (rxpkt_len < 17)
+		return(0);
+	/* check version bits in the header byte */
+	if ((rxpkt[1] & 0xC0) != 0x80)
+		return(0);
+	/* check the length */
+	c = rxpkt[2] | rxpkt[3] << 8;
+	if (c + 4 != rxpkt_len)
+		return(0);
+	/* ensure that the "from" and "to" are printable ASCII */
+	for (i = 8; i < 16; i++) {
+		c = rxpkt[i];
+		if (c < ' ' || c > '~')
+			return(0);
+	}
+	/* basic checks pass */
+	return(1);
+}
+
+static int
+psprim_extra_checks()
+{
+	int i, c;
+
+	if (rxpkt_len < 24)
+		return(0);
+	/* "original rcvr" field needs to be printable ASCII */
+	for (i = 16; i < 20; i++) {
+		c = rxpkt[i];
+		if (c < ' ' || c > '~')
+			return(0);
+	}
+	/* checks pass */
+	return(1);
+}
+
+static void
+print_malformed()
+{
+	int i;
+	char *dp;
+
+	dp = fmtbuf;
+	strcpy(dp, "G23 UNK:");
+	dp += 8;
+	for (i = 1; i < rxpkt_len; i++) {
+		sprintf(dp, " %02X", rxpkt[i]);
+		dp += 3;
+	}
+	*dp = '\0';
+	output_line(fmtbuf);
+}
+
+static int
+entity_name_well_formed(p)
+	char *p;
+{
+	int i, len;
+
+	if (!isupper(p[0]))
+		return(0);
+	for (i = 0; i < 4; i++)
+		if (!isalnum(p[i]))
+			break;
+	len = i;
+	for (; i < 4; i++)
+		if (p[i] != ' ')
+			return(0);
+	return(len);
+}
+
+static void
+print_entity_name(raw)
+	char *raw;
+{
+	int len;
+
+	len = entity_name_well_formed(raw);
+	if (len)
+		sprintf(fmtbuf_ptr, "%.*s", len, raw);
+	else
+		sprintf(fmtbuf_ptr, "\"%.4s\"", raw);
+	fmtbuf_ptr = index(fmtbuf_ptr, '\0');
+}
+
+static void
+print_common_hdr(typestr)
+	char *typestr;
+{
+	sprintf(fmtbuf, "G23 %s id=%02X ts=%02X%02X%02X%02X ", typestr,
+		rxpkt[1], rxpkt[7], rxpkt[6], rxpkt[5], rxpkt[4]);
+	fmtbuf_ptr = index(fmtbuf, '\0');
+	print_entity_name(rxpkt + 8);
+	*fmtbuf_ptr++ = '-';
+	*fmtbuf_ptr++ = '>';
+	print_entity_name(rxpkt + 12);
+	*fmtbuf_ptr++ = ' ';
+}
+
+static void
+format_text(start_off)
+{
+	int i, c;
+
+	*fmtbuf_ptr++ = '\"';
+	for (i = start_off; i < rxpkt_len; i++) {
+		c = rxpkt[i];
+		if (c & 0x80) {
+			*fmtbuf_ptr++ = 'M';
+			*fmtbuf_ptr++ = '-';
+			c &= 0x7F;
+		}
+		if (c < 0x20) {
+			*fmtbuf_ptr++ = '^';
+			*fmtbuf_ptr++ = c + '@';
+		} else if (c == 0x7F) {
+			*fmtbuf_ptr++ = '^';
+			*fmtbuf_ptr++ = '?';
+		} else
+			*fmtbuf_ptr++ = c;
+	}
+	*fmtbuf_ptr++ = '\"';
+	*fmtbuf_ptr = '\0';
+	output_line(fmtbuf);
+}
+
+static void
+format_compressed_trace(start_off)
+{
+	int i;
+
+	i = start_off + 1;
+	sprintf(fmtbuf_ptr, "%d", rxpkt[i+1] << 8 | rxpkt[i]);
+	fmtbuf_ptr = index(fmtbuf_ptr, '\0');
+	i += 4;
+	for (; i < rxpkt_len; i++) {
+		sprintf(fmtbuf_ptr, " %02X", rxpkt[i]);
+		fmtbuf_ptr += 3;
+	}
+	*fmtbuf_ptr = '\0';
+	output_line(fmtbuf);
+}
+
+static void
+format_trace()
+{
+	int i;
+
+	i = 16;
+	if (rxpkt[i] < 0x20) {
+		sprintf(fmtbuf_ptr, "tc=%02X ", rxpkt[i]);
+		fmtbuf_ptr += 6;
+		i++;
+	}
+	if (rxpkt_len - i >= 5 && rxpkt[i] == '%' &&
+	    !rxpkt[i+3] && !rxpkt[i+4])
+		format_compressed_trace(i);
+	else
+		format_text(i);
+}
+
+static void
+format_psprim()
+{
+	int i;
+
+	/* original destination */
+	*fmtbuf_ptr++ = '(';
+	print_entity_name(rxpkt + 16);
+	*fmtbuf_ptr++ = ')';
+	/* opcode */
+	sprintf(fmtbuf_ptr, " %02X%02X%02X%02X",
+		rxpkt[23], rxpkt[22], rxpkt[21], rxpkt[20]);
+	fmtbuf_ptr += 9;
+	for (i = 24; i < rxpkt_len; i++) {
+		sprintf(fmtbuf_ptr, " %02X", rxpkt[i]);
+		fmtbuf_ptr += 3;
+	}
+	*fmtbuf_ptr = '\0';
+	output_line(fmtbuf);
+}
+
+void
+print_g23_trace()
+{
+	if (!basic_checks()) {
+		print_malformed();
+		return;
+	}
+	/* dispatch by type */
+	switch (rxpkt[1] & 0x30) {
+	case 0x10:
+		/* PS primitive */
+		if (psprim_extra_checks()) {
+			print_common_hdr("PSprim");
+			format_psprim();
+		} else
+			print_malformed();
+		return;
+	case 0x20:
+		/* trace */
+		print_common_hdr("trace");
+		format_trace();
+		return;
+	case 0x30:
+		/* system primitive */
+		print_common_hdr("sysprim");
+		format_text(16);
+		return;
+	default:
+		print_malformed();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/g23sh/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+CC=	gcc
+CFLAGS=	-O2 -I../include
+PROG=	g23sh
+OBJS=	init.o main.o pktsort.o sendsp.o usercmd.o
+LIBS=	../libasync/libasync.a ../libg23/libg23.a
+INSTBIN=/usr/local/bin
+
+all:	${PROG}
+
+${PROG}: ${OBJS} ${LIBS}
+	${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS}
+
+install:	${PROG}
+	mkdir -p ${INSTBIN}
+	install -c ${PROG} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROG}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/g23sh/init.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,27 @@
+/*
+ * This module contains the initialization code for g23sh.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "localsock.h"
+
+extern int sock;
+
+init()
+{
+	static u_char want_rvt_lost[9] = {CLI2RVI_WANT_RVTRACE,
+					  0xFF, 0xFF, 0xFF, 0xFF,
+					  0x00, 0x00, 0x00, 0x00};
+	static u_char want_l23_mux[2] = {CLI2RVI_WANT_MUXPROTO, RVT_L23_HEADER};
+
+	if (!sock)
+		connect_local_socket();
+	localsock_prep_for_length_rx();
+	send_init_command(want_rvt_lost, 9);
+	send_init_command(want_l23_mux, 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/g23sh/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,86 @@
+/*
+ * This module contains the main() function for g23sh.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+char *socket_pathname = "/tmp/rvinterf_socket";
+int ttyhacks, dflag;
+
+int sock;
+
+extern char *rvinterf_Bopt, *rvinterf_lopt, *rvinterf_wopt;
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c;
+	fd_set fds;
+
+	while ((c = getopt(argc, argv, "B:dl:s:w:")) != EOF)
+		switch (c) {
+		case 'B':
+			rvinterf_Bopt = optarg;
+			continue;
+		case 'd':
+			dflag++;
+			continue;
+		case 'l':
+			rvinterf_lopt = optarg;
+			continue;
+		case 's':
+			socket_pathname = optarg;
+			continue;
+		case 'w':
+			rvinterf_wopt = optarg;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: %s [options] [ttyport]\n", argv[0]);
+			exit(1);
+		}
+	switch (argc - optind) {
+	case 0:
+		if (rvinterf_Bopt || rvinterf_lopt || rvinterf_wopt) {
+			fprintf(stderr,
+      "%s: -B, -l and -w options are meaningful only when launching rvinterf\n",
+				argv[0]);
+			exit(1);
+		}
+		break;
+	case 1:
+		launch_rvinterf(argv[optind]);
+		break;
+	default:
+		goto usage;
+	}
+
+	ttyhacks = isatty(0) && !dflag;
+	init();
+	tty_init();
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(0, &fds);
+		FD_SET(sock, &fds);
+		c = select(sock+1, &fds, 0, 0, 0);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			tty_cleanup();
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(0, &fds))
+			handle_tty_input();
+		if (FD_ISSET(sock, &fds))
+			handle_rvinterf_input();
+		fflush(stdout);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/g23sh/pktsort.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,67 @@
+/*
+ * Here we sort out incoming packets from the target relayed via rvinterf.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "localsock.h"
+#include "localtypes.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+static void
+process_rvt()
+{
+	u32 useid;
+
+	if (rvi_msg_len < 7) {
+		tty_cleanup();
+		fprintf(stderr, "Error: rvinterf sent us an invalid RVT msg\n");
+		exit(1);
+	}
+	useid = rvi_msg[2] << 24 | rvi_msg[3] << 16 | rvi_msg[4] << 8
+		| rvi_msg[5];
+	switch (useid) {
+	case 0:
+		handle_useid_0();
+		return;
+	default:
+		tty_cleanup();
+		fprintf(stderr, "unexpected fwd of USEID %08X from rvinterf\n",
+			useid);
+		exit(1);
+	}
+}
+
+void
+g23_packet_rx()
+{
+	char fmtbuf[MAX_PKT_FROM_TARGET*8];	/* size it generously */
+
+	format_g23_packet(rvi_msg + 1, rvi_msg_len - 1, fmtbuf);
+	async_msg_output(fmtbuf);
+}
+
+void
+process_pkt_from_target()
+{
+	switch (rvi_msg[1]) {
+	case RVT_RV_HEADER:
+		process_rvt();
+		return;
+	case RVT_L23_HEADER:
+		g23_packet_rx();
+		return;
+	default:
+		tty_cleanup();
+		fprintf(stderr, "unexpected fwd of MUX %02X from rvinterf\n",
+			rvi_msg[1]);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/g23sh/sendsp.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,53 @@
+/*
+ * g23sh system primitive sending command
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+
+void
+cmd_sendsp(argc, argv)
+	char **argv;
+{
+	char *stackdest, *primarg;
+	unsigned intlen;
+	u_char sendpkt[MAX_PKT_TO_TARGET+1];
+	unsigned pktlen;
+
+	stackdest = argv[1];
+	primarg = argv[2];
+	if (strlen(stackdest) > 4) {
+		printf(
+		  "error: stack destination arg may not exceed 4 characters\n");
+		return;
+	}
+	intlen = 12 + strlen(primarg);
+	pktlen = intlen + 4;
+	if (pktlen > MAX_PKT_TO_TARGET) {
+		printf("error: max pkt to target limit exceeded\n");
+		return;
+	}
+	/* fill out the packet */
+	sendpkt[0] = RVT_L23_HEADER;
+	sendpkt[1] = 0xB7;	/* system prim */
+	sendpkt[2] = intlen;
+	sendpkt[3] = intlen >> 8;
+	/* send zeros for the timestamp */
+	sendpkt[4] = 0;
+	sendpkt[5] = 0;
+	sendpkt[6] = 0;
+	sendpkt[7] = 0;
+	/* as far as TI's sw is concerned, we are PCO */
+	sendpkt[8] = 'P';
+	sendpkt[9] = 'C';
+	sendpkt[10] = 'O';
+	sendpkt[11] = ' ';
+	sprintf(sendpkt + 12, "%-4s%s", stackdest, primarg);
+	/* send it! */
+	send_pkt_to_target(sendpkt, pktlen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/g23sh/usercmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,90 @@
+/*
+ * This module implements g23sh user command dispatch.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char usercmd[];
+
+extern void cmd_sendsp();
+
+void
+cmd_exit()
+{
+	tty_cleanup();
+	exit(0);
+}
+
+static struct cmdtab {
+	char *cmd;
+	int minargs;
+	int maxargs;
+	void (*func)();
+} cmdtab[] = {
+	{"exit", 0, 0, cmd_exit},
+	{"quit", 0, 0, cmd_exit},
+	{"sp", 2, 2, cmd_sendsp},
+	{0, 0, 0, 0}
+};
+
+void
+dispatch_user_cmd()
+{
+	char *argv[10];
+	char *cp, **ap;
+	struct cmdtab *tp;
+
+	for (cp = usercmd; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#')
+		return;
+	argv[0] = cp;
+	while (*cp && !isspace(*cp))
+		cp++;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		printf("error: no such command\n");
+		return;
+	}
+	for (ap = argv + 1; ; ) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp || *cp == '#')
+			break;
+		if (ap - argv - 1 >= tp->maxargs) {
+			printf("error: too many arguments\n");
+			return;
+		}
+		if (*cp == '"') {
+			*ap++ = ++cp;
+			while (*cp && *cp != '"')
+				cp++;
+			if (*cp != '"') {
+				printf("error: unterminated quoted string\n");
+				return;
+			}
+			*cp++ = '\0';
+		} else {
+			*ap++ = cp;
+			while (*cp && !isspace(*cp))
+				cp++;
+			if (*cp)
+				*cp++ = '\0';
+		}
+	}
+	if (ap - argv - 1 < tp->minargs) {
+		printf("error: too few arguments\n");
+		return;
+	}
+	*ap = 0;
+	tp->func(ap - argv, argv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/sendsp/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+CC=	gcc
+CFLAGS=	-O2 -I../include
+PROGS=	fc-sendsp
+INSTBIN=/usr/local/bin
+
+SENDSP_OBJS=	rvifsend.o sendsp.o
+
+all:	${PROGS}
+
+fc-sendsp:	${SENDSP_OBJS}
+	${CC} ${CFLAGS} -o $@ ${SENDSP_OBJS}
+
+install:	${PROGS}
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/sendsp/rvifsend.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,66 @@
+/*
+ * This module is currently linked by fc-sendsp, and may be used by
+ * other similar hack-utilities in the future.  Here we connect to
+ * rvinterf, send one packet to the target, and call it done.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "localsock.h"
+
+extern char *socket_pathname;
+
+send_pkt_to_target(pkt, pktlen)
+	u_char *pkt;
+	unsigned pktlen;
+{
+	/* local socket binding voodoo copied from osmocon */
+	struct sockaddr_un local;
+	unsigned int namelen;
+	int sock, rc;
+	u_char hdrbuf[3];
+	int len1;
+
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock < 0) {
+		perror("socket(AF_UNIX, SOCK_STREAM, 0)");
+		exit(1);
+	}
+
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, socket_pathname, sizeof(local.sun_path));
+	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	namelen = SUN_LEN(&local);
+#else
+	namelen = strlen(local.sun_path) +
+		  offsetof(struct sockaddr_un, sun_path) + 1;
+#endif
+
+	rc = connect(sock, (struct sockaddr *) &local, namelen);
+	if (rc != 0) {
+		perror(socket_pathname);
+		exit(1);
+	}
+
+	len1 = pktlen + 1;
+	hdrbuf[0] = len1 >> 8;
+	hdrbuf[1] = len1 & 0xFF;
+	hdrbuf[2] = CLI2RVI_PKT_TO_TARGET;
+	write(sock, hdrbuf, 3);
+	write(sock, pkt, pktlen);
+	close(sock);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/old/sendsp/sendsp.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,71 @@
+/*
+ * This hack-utility sends a GPF System Primitive given on the command line
+ * to a target GSM device via rvinterf.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+
+char *socket_pathname = "/tmp/rvinterf_socket";
+
+main(argc, argv)
+	char **argv;
+{
+	char *stackdest, *primarg;
+	unsigned intlen;
+	u_char sendpkt[MAX_PKT_TO_TARGET];
+	unsigned pktlen;
+
+	if (argc < 3) {
+usage:		fprintf(stderr, "usage: %s [-s socket] stackdest primarg\n",
+			argv[0]);
+		exit(1);
+	}
+	if (strcmp(argv[1], "-s")) {
+		if (argc != 3)
+			goto usage;
+		stackdest = argv[1];
+		primarg = argv[2];
+	} else {
+		if (argc != 5)
+			goto usage;
+		socket_pathname = argv[2];
+		stackdest = argv[3];
+		primarg = argv[4];
+	}
+	if (strlen(stackdest) > 4) {
+		fprintf(stderr,
+		"error: stack destination arg may not exceed 4 characters\n");
+		exit(1);
+	}
+	intlen = 12 + strlen(primarg) + 1;
+	pktlen = intlen + 4;
+	if (pktlen > MAX_PKT_TO_TARGET) {
+		fprintf(stderr, "error: max pkt to target limit exceeded\n");
+		exit(1);
+	}
+	/* fill out the packet */
+	sendpkt[0] = RVT_L23_HEADER;
+	sendpkt[1] = 0xB7;	/* system prim */
+	sendpkt[2] = intlen;
+	sendpkt[3] = intlen >> 8;
+	/* send zeros for the timestamp */
+	sendpkt[4] = 0;
+	sendpkt[5] = 0;
+	sendpkt[6] = 0;
+	sendpkt[7] = 0;
+	/* as far as TI's sw is concerned, we are PCO */
+	sendpkt[8] = 'P';
+	sendpkt[9] = 'C';
+	sendpkt[10] = 'O';
+	sendpkt[11] = ' ';
+	sprintf(sendpkt + 12, "%-4s%s", stackdest, primarg);
+	/* send it! */
+	send_pkt_to_target(sendpkt, pktlen);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+CC=	gcc
+CFLAGS=	-O2 -I../include
+PROGS=	fc-tmsh
+INSTBIN=/usr/local/bin
+LIBS=	../libasync/libasync.a
+
+TMSH_OBJS=	abb.o etmbasic.o ffs2.o ffs2resp.o ffs2wr.o init.o main.o \
+		misc.o omr.o pktsort.o tmcore.o usercmd.o
+
+all:	${PROGS}
+
+fc-tmsh:	${TMSH_OBJS} ${LIBS}
+	${CC} ${CFLAGS} -o $@ ${TMSH_OBJS} ${LIBS}
+
+install:	${PROGS}
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f *.o *.out *errs ${PROGS}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/abb.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,98 @@
+/*
+ * In this module we are going to implement commands dealing with the ABB.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "etm.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+void
+cmd_abbr(argc, argv)
+	char **argv;
+{
+	u32 page, reg;
+	u_char cmdpkt[5];
+
+	page = strtoul(argv[1], 0, 0);
+	reg = strtoul(argv[2], 0, 0);
+	if (page > 1 || reg > 31) {
+		printf("error: argument(s) out of range\n");
+		return;
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_CODEC_RD;
+	cmdpkt[3] = page << 5 | reg;
+	send_etm_cmd(cmdpkt, 3);
+}
+
+void
+abbr_response()
+{
+	unsigned pg, reg, val;
+	char buf[80];
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("abbr error");
+		return;
+	}
+	if (rvi_msg_len != 9) {
+		print_etm_pkt_raw("abbr malformed resp");
+		return;
+	}
+	pg = rvi_msg[5] >> 5;
+	reg = rvi_msg[5] & 0x1F;
+	val = rvi_msg[6] | rvi_msg[7] << 8;
+	sprintf(buf, "abbr %u %u: %03X", pg, reg, val);
+	async_msg_output(buf);
+}
+
+void
+cmd_abbw(argc, argv)
+	char **argv;
+{
+	u32 page, reg, val;
+	u_char cmdpkt[7];
+
+	page = strtoul(argv[1], 0, 0);
+	reg = strtoul(argv[2], 0, 0);
+	val = strtoul(argv[3], 0, 16);
+	if (page > 1 || reg > 31 || val > 0x3FF) {
+		printf("error: argument(s) out of range\n");
+		return;
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_CODEC_WR;
+	cmdpkt[3] = page << 5 | reg;
+	cmdpkt[4] = val;
+	cmdpkt[5] = val >> 8;
+	send_etm_cmd(cmdpkt, 5);
+}
+
+void
+abbw_response()
+{
+	unsigned pg, reg;
+	char buf[80];
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("abbw error");
+		return;
+	}
+	if (rvi_msg_len != 7) {
+		print_etm_pkt_raw("abbw malformed resp");
+		return;
+	}
+	pg = rvi_msg[5] >> 5;
+	reg = rvi_msg[5] & 0x1F;
+	sprintf(buf, "abbw %u %u OK", pg, reg);
+	async_msg_output(buf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/etmbasic.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,107 @@
+/*
+ * Basic ETM interaction
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "etm.h"
+#include "tm3.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+void
+print_etm_pkt_raw(err)
+	char *err;
+{
+	char buf[MAX_PKT_FROM_TARGET*3+80], *dp;
+	int i;
+
+	sprintf(buf, "%s:", err);
+	dp = index(buf, '\0');
+	for (i = 2; i < rvi_msg_len; i++) {
+		sprintf(dp, " %02X", rvi_msg[i]);
+		dp += 3;
+	}
+	async_msg_output(buf);
+}
+
+void
+etm_packet_rx()
+{
+	int i, c;
+
+	if (rvi_msg_len < 4) {
+runt:		print_etm_pkt_raw("TM runt");
+		return;
+	}
+	c = 0;
+	for (i = 2; i < rvi_msg_len; i++)
+		c ^= rvi_msg[i];
+	if (c) {
+		print_etm_pkt_raw("BAD CKSUM");
+		return;
+	}
+	switch (rvi_msg[2]) {
+	case ETM_CORE:
+		if (rvi_msg_len < 6)
+			goto runt;
+		tmcore_msg_rx();
+		return;
+	case ETM_FFS1:
+		print_etm_pkt_raw("FFS1");
+		return;
+	case ETM_FFS2:
+		if (rvi_msg_len < 5)
+			goto runt;
+		handle_ffs2_response();
+		return;
+	/* TM3 */
+	case MEM_READ:
+		if (rvi_msg_len < 5)
+			goto runt;
+		handle_omr_response();
+		return;
+	default:
+		print_etm_pkt_raw("TM unknown");
+	}
+}
+
+void
+cmd_etmpkt(argc, argv)
+	char **argv;
+{
+	u_char pkt[MAX_PKT_TO_TARGET];
+	int di, c, b;
+	char **ap;
+
+	pkt[0] = RVT_TM_HEADER;
+	di = 1;
+	c = 0;
+	for (ap = argv + 1; *ap; ap++) {
+		b = strtoul(*ap, 0, 16);
+		pkt[di++] = b;
+		c ^= b;
+	}
+	pkt[di++] = c;
+	send_pkt_to_target(pkt, di);
+}
+
+void
+send_etm_cmd(buf, len)
+	u_char *buf;
+{
+	int i, c;
+
+	buf[0] = RVT_TM_HEADER;
+	c = 0;
+	for (i = 1; i <= len; i++)
+		c ^= buf[i];
+	buf[i] = c;
+	send_pkt_to_target(buf, len + 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/ffs2.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,349 @@
+/*
+ * In this module we are going to implement TMFFS2 functionality.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "limits.h"
+#include "localtypes.h"
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+
+void
+cmd_ffs2_close(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[5];
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_CLOSE;
+	cmdpkt[3] = strtoul(argv[1], 0, 0);
+	send_etm_cmd(cmdpkt, 3);
+}
+
+void
+cmd_ffs2_delete(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_REMOVE;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_format(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: argument exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_FORMAT;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	/* magic is 0x2BAD, 16-bit little-endian */
+	*dp++ = 0xAD;
+	*dp++ = 0x2B;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_mkdir(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_MKDIR;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_open(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_OPEN;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	*dp++ = strtoul(argv[2], 0, 16);
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_opendir(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_OPENDIR;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_preformat()
+{
+	u_char cmdpkt[6];
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_PREFORMAT;
+	/* magic is 0xDEAD, 16-bit little-endian */
+	cmdpkt[3] = 0xAD;
+	cmdpkt[4] = 0xDE;
+	send_etm_cmd(cmdpkt, 4);
+}
+
+void
+cmd_ffs2_query(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[5];
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_QUERY;
+	cmdpkt[3] = strtoul(argv[1], 0, 0);
+	send_etm_cmd(cmdpkt, 3);
+}
+
+void
+cmd_ffs2_readfd(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[6];
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_READ;
+	cmdpkt[3] = strtoul(argv[1], 0, 0);
+	cmdpkt[4] = strtoul(argv[2], 0, 0);
+	send_etm_cmd(cmdpkt, 4);
+}
+
+void
+cmd_ffs2_readfile(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_FILE_READ;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	*dp++ = strtoul(argv[2], 0, 0);
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_stat(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_STAT;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_version()
+{
+	u_char cmdpkt[4];
+
+	cmdpkt[1] = ETM_FFS2;
+	cmdpkt[2] = TMFFS_VERSION;
+	send_etm_cmd(cmdpkt, 2);
+}
+
+void
+cmd_ffs2_wrfile(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen, slen2;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	slen2 = strlen(argv[2]);
+	if (slen2 > 64) {
+		printf("error: write test data argument is too long\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_FILE_WRITE;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	*dp++ = slen2;		/* no NUL-termination on test data */
+	strcpy(dp, argv[2]);
+	dp += slen2;
+	*dp++ = FFS_O_CREATE | FFS_O_TRUNC;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_writefd(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen2;
+
+	slen2 = strlen(argv[2]);
+	if (slen2 > 64) {
+		printf("error: write test data argument is too long\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_WRITE;
+	*dp++ = strtoul(argv[1], 0, 0);
+	*dp++ = slen2;		/* no NUL-termination on test data */
+	strcpy(dp, argv[2]);
+	dp += slen2;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_ffs2_xlstat(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+	int slen;
+
+	slen = strlen(argv[1]);
+	if (slen >= TMFFS_STRING_SIZE) {
+		printf("error: pathname arg exceeds string length limit\n");
+		return;
+	}
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_XLSTAT;
+	*dp++ = slen + 1;
+	strcpy(dp, argv[1]);
+	dp += slen + 1;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+static struct cmdtab {
+	char *cmd;
+	int minargs;
+	int maxargs;
+	void (*func)();
+} ffs2_cmds[] = {
+	{"close", 1, 1, cmd_ffs2_close},
+	{"delete", 1, 1, cmd_ffs2_delete},
+	{"format", 1, 1, cmd_ffs2_format},
+	{"mkdir", 1, 1, cmd_ffs2_mkdir},
+	{"open", 2, 2, cmd_ffs2_open},
+	{"opendir", 1, 1, cmd_ffs2_opendir},
+	{"preformat", 0, 0, cmd_ffs2_preformat},
+	{"query", 1, 1, cmd_ffs2_query},
+	{"readfd", 2, 2, cmd_ffs2_readfd},
+	{"readfile", 2, 2, cmd_ffs2_readfile},
+	{"stat", 1, 1, cmd_ffs2_stat},
+	{"version", 0, 0, cmd_ffs2_version},
+	{"wrfile", 2, 2, cmd_ffs2_wrfile},
+	{"writefd", 2, 2, cmd_ffs2_writefd},
+	{"xlstat", 1, 1, cmd_ffs2_xlstat},
+	{0, 0, 0, 0}
+};
+
+void
+cmd_ffs2(argc, argv)
+	char **argv;
+{
+	struct cmdtab *tp;
+	int extargs;
+
+	for (tp = ffs2_cmds; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[1]))
+			break;
+	if (!tp->func) {
+		printf("error: no such ffs2 command\n");
+		return;
+	}
+	extargs = argc - 2;
+	if (extargs > tp->maxargs) {
+		printf("error: too many arguments\n");
+		return;
+	}
+	if (extargs < tp->minargs) {
+		printf("error: too few arguments\n");
+		return;
+	}
+	tp->func(argc - 1, argv + 1);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/ffs2resp.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+/*
+ * Handling of TMFFS2 responses from ETM
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+void
+handle_ffs2_response()
+{
+	if (rvi_msg[3])
+		print_etm_pkt_raw("FFS2 error");
+	else if (rvi_msg_len == 5)
+		async_msg_output("FFS2 command successful");
+	else
+		print_etm_pkt_raw("FFS2 response");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/ffs2wr.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,111 @@
+/*
+ * In this module we are going to implement some high-level FFS write
+ * operations, using the TMFFS2 protocol.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "limits.h"
+#include "localtypes.h"
+#include "etm.h"
+#include "ffs.h"
+#include "tmffs2.h"
+
+void
+send_file_write(filename, data, size)
+	char *filename;
+	u_char *data;
+{
+	u_char cmdpkt[MAX_PKT_TO_TARGET], *dp;
+
+	dp = cmdpkt + 1;
+	*dp++ = ETM_FFS2;
+	*dp++ = TMFFS_FILE_WRITE;
+	*dp++ = strlen(filename) + 1;
+	strcpy(dp, filename);
+	dp += strlen(filename) + 1;
+	*dp++ = size;		/* data size in bytes */
+	bcopy(data, dp, size);
+	dp += size;
+	*dp++ = FFS_O_CREATE | FFS_O_TRUNC;
+	send_etm_cmd(cmdpkt, dp - cmdpkt - 1);
+}
+
+void
+cmd_set_imeisv(argc, argv)
+	char **argv;
+{
+	char *filename, *cp, digits[16];
+	u_char bytes[8];
+	int pcm_order, i;
+
+	if (!strcmp(argv[1], "pcm")) {
+		filename = "/pcm/IMEI";
+		pcm_order = 1;
+	} else if (!strcmp(argv[1], "fc")) {
+		filename = "/etc/IMEISV";
+		pcm_order = 0;
+	} else {
+		printf(
+	"error: IMEISV storage type argument must be \"pcm\" or \"fc\"\n");
+		return;
+	}
+	cp = argv[2];
+	if (!isdigit(*cp)) {
+inv:		printf("error: 2nd argument must have 16 decimal digits\n");
+		return;
+	}
+	for (i = 0; i < 16; i++) {
+		if (ispunct(*cp))
+			cp++;
+		if (!isdigit(*cp))
+			goto inv;
+		digits[i] = *cp++ - '0';
+	}
+	if (*cp)
+		goto inv;
+	for (i = 0; i < 8; i++)
+		bytes[i] = pcm_order ? digits[i*2+1] << 4 | digits[i*2]
+				     : digits[i*2] << 4 | digits[i*2+1];
+	printf("Writing \"%02X %02X %02X %02X %02X %02X %02X %02X\" into %s\n",
+		bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5],
+		bytes[6], bytes[7], filename);
+	send_file_write(filename, bytes, 8);
+}
+
+void
+cmd_set_pcm_string(argc, argv)
+	char **argv;
+{
+	char filename[16];
+
+	if (strcmp(argv[1], "CGMI") && strcmp(argv[1], "CGMM") &&
+	    strcmp(argv[1], "CGMR") && strcmp(argv[1], "CGSN")) {
+	      printf("error: \"%s\" is not a recognized PCM string file name\n",
+			argv[1]);
+		return;
+	}
+	sprintf(filename, "/pcm/%s", argv[1]);
+	if (strlen(argv[2]) > 20) {
+		printf("error: %s string may not exceed 20 characters\n",
+			filename);
+		return;
+	}
+	send_file_write(filename, argv[2], strlen(argv[2]));
+}
+
+void
+cmd_set_rfcap(argc, argv)
+	char **argv;
+{
+	u_char bytes[16];
+	int i;
+
+	for (i = 0; i < 16; i++)
+		bytes[i] = strtoul(argv[i+1], 0, 16);
+	send_file_write("/gsm/com/rfcap", bytes, 16);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/init.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,31 @@
+/*
+ * This module contains the initialization code for fc-tmsh.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "localsock.h"
+
+extern int sock;
+
+init()
+{
+	static u_char want_rvt_lost[9] = {CLI2RVI_WANT_RVTRACE,
+					  0xFF, 0xFF, 0xFF, 0xFF,
+					  0x00, 0x00, 0x00, 0x00};
+	static u_char want_rvt_etm[9]  = {CLI2RVI_WANT_RVTRACE,
+					  0xFF, 0xFF, 0xFF, 0xFF,
+					  0x00, 0x1E, 0x00, 0x04};
+	static u_char want_etm_mux[2] = {CLI2RVI_WANT_MUXPROTO, RVT_TM_HEADER};
+
+	if (!sock)
+		connect_local_socket();
+	localsock_prep_for_length_rx();
+	send_init_command(want_rvt_lost, 9);
+	send_init_command(want_rvt_etm, 9);
+	send_init_command(want_etm_mux, 2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,86 @@
+/*
+ * This module contains the main() function for fc-tmsh.
+ */
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+char *socket_pathname = "/tmp/rvinterf_socket";
+int ttyhacks, dflag;
+
+int sock;
+
+extern char *rvinterf_Bopt, *rvinterf_lopt, *rvinterf_wopt;
+
+main(argc, argv)
+	char **argv;
+{
+	extern int optind;
+	extern char *optarg;
+	int c;
+	fd_set fds;
+
+	while ((c = getopt(argc, argv, "B:dl:s:w:")) != EOF)
+		switch (c) {
+		case 'B':
+			rvinterf_Bopt = optarg;
+			continue;
+		case 'd':
+			dflag++;
+			continue;
+		case 'l':
+			rvinterf_lopt = optarg;
+			continue;
+		case 's':
+			socket_pathname = optarg;
+			continue;
+		case 'w':
+			rvinterf_wopt = optarg;
+			continue;
+		case '?':
+		default:
+usage:			fprintf(stderr,
+				"usage: %s [options] [ttyport]\n", argv[0]);
+			exit(1);
+		}
+	switch (argc - optind) {
+	case 0:
+		if (rvinterf_Bopt || rvinterf_lopt || rvinterf_wopt) {
+			fprintf(stderr,
+      "%s: -B, -l and -w options are meaningful only when launching rvinterf\n",
+				argv[0]);
+			exit(1);
+		}
+		break;
+	case 1:
+		launch_rvinterf(argv[optind]);
+		break;
+	default:
+		goto usage;
+	}
+
+	ttyhacks = isatty(0) && !dflag;
+	init();
+	tty_init();
+	for (;;) {
+		FD_ZERO(&fds);
+		FD_SET(0, &fds);
+		FD_SET(sock, &fds);
+		c = select(sock+1, &fds, 0, 0, 0);
+		if (c < 0) {
+			if (errno == EINTR)
+				continue;
+			tty_cleanup();
+			perror("select");
+			exit(1);
+		}
+		if (FD_ISSET(0, &fds))
+			handle_tty_input();
+		if (FD_ISSET(sock, &fds))
+			handle_rvinterf_input();
+		fflush(stdout);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/misc.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,24 @@
+/*
+ * Commands which don't belong anywhere else
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "etm.h"
+#include "tmffs1.h"
+
+void
+cmd_check_ffs1(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[5];
+
+	cmdpkt[1] = ETM_FFS1;
+	cmdpkt[2] = FPI_TMFFS_VERSION;
+	cmdpkt[3] = FPI_END;
+	send_etm_cmd(cmdpkt, 3);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/omr.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,94 @@
+/*
+ * Old-style memory read command
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "tm3.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+static void
+memdump_line(off, inbuf, len)
+	u_char *inbuf;
+{
+	char outbuf[80], *dp;
+	int i, c;
+
+	sprintf(outbuf, "omr %02X:  ", off);
+	dp = index(outbuf, '\0');
+	for (i = 0; i < 16; i++) {
+		if (i < len)
+			sprintf(dp, "%02X ", inbuf[i]);
+		else
+			strcpy(dp, "   ");
+		dp += 3;
+		if (i == 7 || i == 15)
+			*dp++ = ' ';
+	}
+	for (i = 0; i < len; i++) {
+		c = inbuf[i];
+		if (c < ' ' || c > '~')
+			c = '.';
+		*dp++ = c;
+	}
+	*dp = '\0';
+	async_msg_output(outbuf);
+}
+
+void
+handle_omr_response()
+{
+	int off, len;
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("TM3 memread error");
+		return;
+	}
+	if (rvi_msg_len < 10) {
+bad:		print_etm_pkt_raw("omr bad resp");
+		return;
+	}
+	if (rvi_msg[5] || rvi_msg[6] || rvi_msg[7])
+		goto bad;
+	if (rvi_msg_len != rvi_msg[4] + 9)
+		goto bad;
+	for (off = 0; off < rvi_msg[4]; off += len) {
+		len = rvi_msg[4] - off;
+		if (len > 16)
+			len = 16;
+		memdump_line(off, rvi_msg + 8 + off, len);
+	}
+}
+
+void
+cmd_omr(argc, argv)
+	char **argv;
+{
+	u32 addr, size;
+	u_char cmdpkt[11];
+
+	addr = strtoul(argv[1], 0, 16);
+	size = strtoul(argv[2], 0, 16);
+	if (size < 1 || size > TM3_MEMREAD_MAX) {
+		printf("error: count argument outside valid range\n");
+		return;
+	}
+	cmdpkt[1] = MEM_READ;
+	cmdpkt[2] = addr;
+	cmdpkt[3] = addr >> 8;
+	cmdpkt[4] = addr >> 16;
+	cmdpkt[5] = addr >> 24;
+	cmdpkt[6] = size;
+	cmdpkt[7] = 0;
+	cmdpkt[8] = 0;
+	cmdpkt[9] = 0;
+	send_etm_cmd(cmdpkt, 9);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/pktsort.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,72 @@
+/*
+ * Here we sort out incoming packets from the target relayed via rvinterf.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "localsock.h"
+#include "localtypes.h"
+#include "etm.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+static void
+print_etm_trace()
+{
+	char buf[MAX_PKT_FROM_TARGET*4];
+
+	strcpy(buf, "ETM Tr: ");
+	safe_print_trace(rvi_msg + 7, rvi_msg_len - 7, buf + 8);
+	async_msg_output(buf);
+}
+
+static void
+process_rvt()
+{
+	u32 useid;
+
+	if (rvi_msg_len < 7) {
+		tty_cleanup();
+		fprintf(stderr, "Error: rvinterf sent us an invalid RVT msg\n");
+		exit(1);
+	}
+	useid = rvi_msg[2] << 24 | rvi_msg[3] << 16 | rvi_msg[4] << 8
+		| rvi_msg[5];
+	switch (useid) {
+	case 0:
+		handle_useid_0();
+		return;
+	case ETM_USE_ID:
+		print_etm_trace();
+		return;
+	default:
+		tty_cleanup();
+		fprintf(stderr, "unexpected fwd of USEID %08X from rvinterf\n",
+			useid);
+		exit(1);
+	}
+}
+
+void
+process_pkt_from_target()
+{
+	switch (rvi_msg[1]) {
+	case RVT_RV_HEADER:
+		process_rvt();
+		return;
+	case RVT_TM_HEADER:
+		etm_packet_rx();
+		return;
+	default:
+		tty_cleanup();
+		fprintf(stderr, "unexpected fwd of MUX %02X from rvinterf\n",
+			rvi_msg[1]);
+		exit(1);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/tmcore.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,448 @@
+/*
+ * In this module we are going to implement commands which send requests
+ * to ETM_CORE and the handling of responses from that target module.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include "pktmux.h"
+#include "limits.h"
+#include "localtypes.h"
+#include "etm.h"
+
+extern u_char rvi_msg[];
+extern int rvi_msg_len;
+
+static void
+rw8_response()
+{
+	char buf[MAX_PKT_FROM_TARGET*3+80], *dp;
+	int num, i;
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("rw8 error");
+		return;
+	}
+	num = rvi_msg_len - 7;
+	if (!num) {
+		async_msg_output("w8 OK");
+		return;
+	}
+	strcpy(buf, "r8:");
+	dp = buf + 3;
+	for (i = 0; i < num; i++) {
+		sprintf(dp, " %02X", rvi_msg[i+6]);
+		dp += 3;
+	}
+	async_msg_output(buf);
+}
+
+static void
+rw16_response()
+{
+	char buf[MAX_PKT_FROM_TARGET*3+80], *dp;
+	int num, i, d, off;
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("rw16 error");
+		return;
+	}
+	num = rvi_msg_len - 7;
+	if (!num) {
+		async_msg_output("w16 OK");
+		return;
+	}
+	if (num & 1) {
+		print_etm_pkt_raw("rw16 malformed resp");
+		return;
+	}
+	num >>= 1;
+	strcpy(buf, "r16:");
+	dp = buf + 4;
+	off = 6;
+	for (i = 0; i < num; i++) {
+		d = rvi_msg[off] | rvi_msg[off+1] << 8;
+		off += 2;
+		sprintf(dp, " %04X", d);
+		dp += 5;
+	}
+	async_msg_output(buf);
+}
+
+static void
+rw32_response()
+{
+	char buf[MAX_PKT_FROM_TARGET*3+80], *dp;
+	int num, i, d, off;
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("rw32 error");
+		return;
+	}
+	num = rvi_msg_len - 7;
+	if (!num) {
+		async_msg_output("w32 OK");
+		return;
+	}
+	if (num & 3) {
+		print_etm_pkt_raw("rw32 malformed resp");
+		return;
+	}
+	num >>= 2;
+	strcpy(buf, "r32:");
+	dp = buf + 4;
+	off = 6;
+	for (i = 0; i < num; i++) {
+		d = rvi_msg[off] | rvi_msg[off+1] << 8 | rvi_msg[off+2] << 16
+			| rvi_msg[off+3] << 24;
+		off += 4;
+		sprintf(dp, " %08X", d);
+		dp += 9;
+	}
+	async_msg_output(buf);
+}
+
+static void
+dieid_response()
+{
+	char buf[MAX_PKT_FROM_TARGET*3+80], *dp;
+	int num, i;
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("dieid error");
+		return;
+	}
+	num = rvi_msg_len - 6;
+	strcpy(buf, "dieid resp:");
+	dp = buf + 11;
+	for (i = 0; i < num; i++) {
+		sprintf(dp, " %02X", rvi_msg[i+5]);
+		dp += 3;
+	}
+	async_msg_output(buf);
+}
+
+static void
+echo_response()
+{
+	if (rvi_msg[3])
+		print_etm_pkt_raw("echo error");
+	else
+		print_etm_pkt_raw("echo resp");
+}
+
+static void
+version_response()
+{
+	char buf[80];
+
+	if (rvi_msg[3]) {
+		print_etm_pkt_raw("version error");
+		return;
+	}
+	if (rvi_msg_len != 10) {
+		print_etm_pkt_raw("version malformed resp");
+		return;
+	}
+	sprintf(buf, "version resp: %02X%02X%02X%02X", rvi_msg[8], rvi_msg[7],
+		rvi_msg[6], rvi_msg[5]);
+	async_msg_output(buf);
+}
+
+void
+tmcore_msg_rx()
+{
+	switch (rvi_msg[4]) {
+	case TMCORE_OPC_MEM:
+		if (rvi_msg_len < 7)
+			goto unknown;
+		switch (rvi_msg[5]) {
+		case 0x00:
+		case 0x04:
+			rw32_response();
+			return;
+		case 0x01:
+			rw8_response();
+			return;
+		case 0x02:
+			rw16_response();
+			return;
+		default:
+			goto unknown;
+		}
+	case TMCORE_OPC_ECHO:
+		echo_response();
+		return;
+	case TMCORE_OPC_VERSION:
+		version_response();
+		return;
+	case TMCORE_OPC_CODEC_RD:
+		abbr_response();
+		return;
+	case TMCORE_OPC_CODEC_WR:
+		abbw_response();
+		return;
+	case TMCORE_OPC_DIEID:
+		dieid_response();
+		return;
+	default:
+	unknown:
+		print_etm_pkt_raw("ETM_CORE resp");
+	}
+}
+
+void
+cmd_r8(argc, argv)
+	char **argv;
+{
+	u32 addr;
+	int count;
+	u_char cmdpkt[10];
+
+	addr = strtoul(argv[1], 0, 16);
+	if (argv[2])
+		count = strtoul(argv[2], 0, 0);
+	else
+		count = 1;
+	if (count < 1 || count > 253) {
+		printf("error: count argument outside valid range\n");
+		return;
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x01;
+	cmdpkt[4] = count;
+	cmdpkt[5] = addr;
+	cmdpkt[6] = addr >> 8;
+	cmdpkt[7] = addr >> 16;
+	cmdpkt[8] = addr >> 24;
+	send_etm_cmd(cmdpkt, 8);
+}
+
+void
+cmd_r16(argc, argv)
+	char **argv;
+{
+	u32 addr;
+	int count;
+	u_char cmdpkt[10];
+
+	addr = strtoul(argv[1], 0, 16);
+	if (argv[2])
+		count = strtoul(argv[2], 0, 0);
+	else
+		count = 1;
+	if (addr & 1) {
+		printf("error: address not aligned\n");
+		return;
+	}
+	if (count < 1 || count > 126) {
+		printf("error: count argument outside valid range\n");
+		return;
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x02;
+	cmdpkt[4] = count;
+	cmdpkt[5] = addr;
+	cmdpkt[6] = addr >> 8;
+	cmdpkt[7] = addr >> 16;
+	cmdpkt[8] = addr >> 24;
+	send_etm_cmd(cmdpkt, 8);
+}
+
+void
+cmd_r32(argc, argv)
+	char **argv;
+{
+	u32 addr;
+	int count;
+	u_char cmdpkt[10];
+
+	addr = strtoul(argv[1], 0, 16);
+	if (argv[2])
+		count = strtoul(argv[2], 0, 0);
+	else
+		count = 1;
+	if (addr & 3) {
+		printf("error: address not aligned\n");
+		return;
+	}
+	if (count < 1 || count > 63) {
+		printf("error: count argument outside valid range\n");
+		return;
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x04;
+	cmdpkt[4] = count;
+	cmdpkt[5] = addr;
+	cmdpkt[6] = addr >> 8;
+	cmdpkt[7] = addr >> 16;
+	cmdpkt[8] = addr >> 24;
+	send_etm_cmd(cmdpkt, 8);
+}
+
+void
+cmd_w8(argc, argv)
+	char **argv;
+{
+	u32 addr, v;
+	u_char cmdpkt[MAX_PKT_TO_TARGET];
+	int di;
+	char **ap;
+
+	addr = strtoul(argv[1], 0, 16);
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x11;
+	cmdpkt[4] = argc - 2;
+	cmdpkt[5] = addr;
+	cmdpkt[6] = addr >> 8;
+	cmdpkt[7] = addr >> 16;
+	cmdpkt[8] = addr >> 24;
+	di = 9;
+	for (ap = argv + 2; *ap; ap++) {
+		v = strtoul(*ap, 0, 16);
+		cmdpkt[di++] = v;
+	}
+	send_etm_cmd(cmdpkt, di - 1);
+}
+
+void
+cmd_w16(argc, argv)
+	char **argv;
+{
+	u32 addr, v;
+	u_char cmdpkt[MAX_PKT_TO_TARGET];
+	int di;
+	char **ap;
+
+	addr = strtoul(argv[1], 0, 16);
+	if (addr & 1) {
+		printf("error: address not aligned\n");
+		return;
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x12;
+	cmdpkt[4] = argc - 2;
+	cmdpkt[5] = addr;
+	cmdpkt[6] = addr >> 8;
+	cmdpkt[7] = addr >> 16;
+	cmdpkt[8] = addr >> 24;
+	di = 9;
+	for (ap = argv + 2; *ap; ap++) {
+		v = strtoul(*ap, 0, 16);
+		cmdpkt[di++] = v;
+		cmdpkt[di++] = v >> 8;
+	}
+	send_etm_cmd(cmdpkt, di - 1);
+}
+
+void
+cmd_w32(argc, argv)
+	char **argv;
+{
+	u32 addr, v;
+	u_char cmdpkt[MAX_PKT_TO_TARGET];
+	int di;
+	char **ap;
+
+	addr = strtoul(argv[1], 0, 16);
+	if (addr & 3) {
+		printf("error: address not aligned\n");
+		return;
+	}
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_MEM;
+	cmdpkt[3] = 0x14;
+	cmdpkt[4] = argc - 2;
+	cmdpkt[5] = addr;
+	cmdpkt[6] = addr >> 8;
+	cmdpkt[7] = addr >> 16;
+	cmdpkt[8] = addr >> 24;
+	di = 9;
+	for (ap = argv + 2; *ap; ap++) {
+		v = strtoul(*ap, 0, 16);
+		cmdpkt[di++] = v;
+		cmdpkt[di++] = v >> 8;
+		cmdpkt[di++] = v >> 16;
+		cmdpkt[di++] = v >> 24;
+	}
+	send_etm_cmd(cmdpkt, di - 1);
+}
+
+void
+cmd_dieid(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[4];
+
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_DIEID;
+	send_etm_cmd(cmdpkt, 2);
+}
+
+void
+cmd_ping(argc, argv)
+	char **argv;
+{
+	int delay, size;
+	u_char cmdpkt[8];
+
+	if (argc > 1) {
+		delay = strtoul(argv[1], 0, 0);
+		if (delay > 65535) {
+			printf("error: ping delay argument too big\n");
+			return;
+		}
+	} else
+		delay = 0;
+	if (argc > 2) {
+		size = strtoul(argv[2], 0, 0);
+		if (size > 240) {
+			printf("error: ping size argument too big\n");
+			return;
+		}
+	} else
+		size = 1;
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_ECHO;
+	cmdpkt[3] = delay;
+	cmdpkt[4] = delay >> 8;
+	cmdpkt[5] = size;
+	cmdpkt[6] = size >> 8;
+	send_etm_cmd(cmdpkt, 6);
+}
+
+void
+cmd_tgtreset(argc, argv)
+	char **argv;
+{
+	u_char cmdpkt[4];
+
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_RESET;
+	send_etm_cmd(cmdpkt, 2);
+}
+
+void
+cmd_version(argc, argv)
+	char **argv;
+{
+	u32 arg;
+	u_char cmdpkt[8];
+
+	arg = strtoul(argv[1], 0, 16);
+	cmdpkt[1] = ETM_CORE;
+	cmdpkt[2] = TMCORE_OPC_VERSION;
+	cmdpkt[3] = arg;
+	cmdpkt[4] = arg >> 8;
+	cmdpkt[5] = arg >> 16;
+	cmdpkt[6] = arg >> 24;
+	send_etm_cmd(cmdpkt, 6);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rvinterf/tmsh/usercmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,126 @@
+/*
+ * This module implements fc-tmsh user command dispatch.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+extern char usercmd[];
+
+extern void cmd_abbr();
+extern void cmd_abbw();
+extern void cmd_check_ffs1();
+extern void cmd_dieid();
+extern void cmd_etmpkt();
+extern void cmd_ffs2();
+extern void cmd_omr();
+extern void cmd_ping();
+extern void cmd_r8();
+extern void cmd_r16();
+extern void cmd_r32();
+extern void cmd_set_imeisv();
+extern void cmd_set_pcm_string();
+extern void cmd_set_rfcap();
+extern void cmd_tgtreset();
+extern void cmd_version();
+extern void cmd_w8();
+extern void cmd_w16();
+extern void cmd_w32();
+
+void
+cmd_exit()
+{
+	tty_cleanup();
+	exit(0);
+}
+
+static struct cmdtab {
+	char *cmd;
+	int minargs;
+	int maxargs;
+	void (*func)();
+} cmdtab[] = {
+	{"abbr", 2, 2, cmd_abbr},
+	{"abbw", 3, 3, cmd_abbw},
+	{"check-ffs1", 0, 0, cmd_check_ffs1},
+	{"dieid", 0, 0, cmd_dieid},
+	{"etmpkt", 1, 253, cmd_etmpkt},
+	{"exit", 0, 0, cmd_exit},
+	{"ffs2", 1, 3, cmd_ffs2},
+	{"omr", 2, 2, cmd_omr},
+	{"ping", 0, 2, cmd_ping},
+	{"quit", 0, 0, cmd_exit},
+	{"r8", 1, 2, cmd_r8},
+	{"r16", 1, 2, cmd_r16},
+	{"r32", 1, 2, cmd_r32},
+	{"set-imeisv", 2, 2, cmd_set_imeisv},
+	{"set-pcm-string", 2, 2, cmd_set_pcm_string},
+	{"set-rfcap", 16, 16, cmd_set_rfcap},
+	{"tgtreset", 0, 0, cmd_tgtreset},
+	{"version", 1, 1, cmd_version},
+	{"w8", 2, 246, cmd_w8},
+	{"w16", 2, 123, cmd_w16},
+	{"w32", 2, 62, cmd_w32},
+	{0, 0, 0, 0}
+};
+
+void
+dispatch_user_cmd()
+{
+	char *argv[257];
+	char *cp, **ap;
+	struct cmdtab *tp;
+
+	for (cp = usercmd; isspace(*cp); cp++)
+		;
+	if (!*cp || *cp == '#')
+		return;
+	argv[0] = cp;
+	while (*cp && !isspace(*cp))
+		cp++;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, argv[0]))
+			break;
+	if (!tp->func) {
+		printf("error: no such command\n");
+		return;
+	}
+	for (ap = argv + 1; ; ) {
+		while (isspace(*cp))
+			cp++;
+		if (!*cp || *cp == '#')
+			break;
+		if (ap - argv - 1 >= tp->maxargs) {
+			printf("error: too many arguments\n");
+			return;
+		}
+		if (*cp == '"') {
+			*ap++ = ++cp;
+			while (*cp && *cp != '"')
+				cp++;
+			if (*cp != '"') {
+				printf("error: unterminated quoted string\n");
+				return;
+			}
+			*cp++ = '\0';
+		} else {
+			*ap++ = cp;
+			while (*cp && !isspace(*cp))
+				cp++;
+			if (*cp)
+				*cp++ = '\0';
+		}
+	}
+	if (ap - argv - 1 < tp->minargs) {
+		printf("error: too few arguments\n");
+		return;
+	}
+	*ap = 0;
+	tp->func(ap - argv, argv);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,30 @@
+FOR_LOADTOOLS=	compalstage loadagent
+ALLPROGS=	${FOR_LOADTOOLS} c139explore c139-lldbg helloapp pirexplore \
+		tf-breakin
+LIBS=		libbase libcommon libload libprintf libtiffs
+SUBDIR=		${ALLPROGS} ${LIBS}
+
+default:	${FOR_LOADTOOLS}
+all:		${ALLPROGS}
+
+c139explore:	libbase libcommon libprintf
+c139-lldbg:	libbase libcommon libprintf
+helloapp:	libbase libcommon libprintf
+loadagent:	libbase libcommon libload libprintf
+pirexplore:	libbase libcommon libprintf libtiffs
+
+${SUBDIR}: FRC
+	cd $@; ${MAKE} ${MFLAGS}
+
+install: FRC
+	for i in ${FOR_LOADTOOLS}; do (cd $$i; ${MAKE} ${MFLAGS} install); done
+
+clean: FRC
+	rm -f a.out core errs
+	for i in ${SUBDIR}; do \
+		if [ -d $$i ]; then \
+			(cd $$i; ${MAKE} ${MFLAGS} clean) \
+		fi \
+	done
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,34 @@
+The following target utilities or code components are currently buildable in
+this target-utils tree:
+
+compalstage	For Compal phones only: a little piece of code that is fed to
+		the original fw's bootloader via the serial download protocol
+		provided by the latter; it re-enables the Calypso chip boot ROM
+		and jumps to it, allowing our loadagent to be loaded in the
+		same way as on freedom-enabled devices.
+
+helloapp	Template/skeleton for building programs like loadagent and
+		pirexplore.
+
+loadagent	Loadagent is built to be loaded and run out of the Calypso
+		internal (on-chip) RAM, and does not depend on any hardware
+		outside of the Calypso chip itself - thus it should run
+		unchanged on all Calypso targets.  It expects to be loaded by
+		the Calypso ROM bootloader in the UART download mode, and it
+		reads a RAM variable left behind by the ROM code that indicates
+		which UART has been used to perform that download - it then
+		uses that same UART to communicate with the host, presenting an
+		interactive command prompt.  You can run loadagent "raw" by
+		loading loadagent.srec with fc-iram, but normally it is used
+		"behind the scenes" by fc-loadtool and fc-xram.
+
+pirexplore	For Pirelli DP-L10 target only: this program is built in the
+		same manner as loadagent (also runs out of IRAM, expects to be
+		loaded with fc-iram, and presents an interactive command prompt
+		on the autodetected UART), but it automatically performs some
+		hardware (board level) initialization specific to the Pirelli,
+		and offers additional commands for exploring the hardware
+		features of this device.
+
+tf-breakin	Here we build the payload for the tfc139 hack-utility; see
+		../rvinterf/lowlevel/tfc139.c for the ugly details.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,27 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+LD=	arm-elf-ld
+OBJCOPY=arm-elf-objcopy
+
+PROG=	lldbg
+OBJS=	entry.o cmdtab.o entryinfo.o main.o mygetchar.o uartbase.o
+LIBS=	../libcommon/libcommon.a ../libprintf/libprintf.a ../libbase/libbase.a
+LDS=	lldbg.lds
+
+TC_LIBS=`${CC} -print-file-name=libc.a` \
+	`${CC} -print-file-name=libgcc.a`
+
+all:	${PROG}.bin
+
+${PROG}.elf:	${OBJS} ${LIBS} ${LDS}
+	${LD} -N -T ${LDS} -o $@ ${OBJS} ${LIBS} \
+		--start-group ${TC_LIBS} --end-group
+
+${PROG}.bin:	${PROG}.elf
+	${OBJCOPY} -O binary $< $@
+
+clean:
+	rm -f *.o *errs *core *.elf *.bin *.srec crt0.S
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/cmdtab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,23 @@
+#include "cmdtab.h"
+
+extern void cmd_entryinfo();
+extern void cmd_r8();
+extern void cmd_r16();
+extern void cmd_r32();
+extern void cmd_w8();
+extern void cmd_w16();
+extern void cmd_w32();
+
+extern void cmd_memdump_human();
+
+const struct cmdtab cmdtab[] = {
+	{"dump", cmd_memdump_human},
+	{"entry", cmd_entryinfo},
+	{"r8", cmd_r8},
+	{"r16", cmd_r16},
+	{"r32", cmd_r32},
+	{"w8", cmd_w8},
+	{"w16", cmd_w16},
+	{"w32", cmd_w32},
+	{0, 0}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/entry.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+	.text
+	.globl	_entry
+_entry:
+	.code	16
+	bx	pc
+	nop
+
+	.code	32
+	stmfd	sp!, {r0-r12,lr}
+	mrs	r0, CPSR
+	mov	r1, sp
+	/* supervisor mode, disable all interrupts */
+	msr	CPSR_c, #0xd3
+	ldr	sp, =stack_bottom
+	/* save entry SP and CPSR */
+	ldr	r2, =lldbg_entry_cpsr
+	str	r0, [r2]
+	ldr	r2, =lldbg_entry_sp
+	str	r1, [r2]
+	b	main
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/entryinfo.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+#include "types.h"
+
+u32 lldbg_entry_cpsr;
+u32 lldbg_entry_sp;
+
+void
+cmd_entryinfo()
+{
+	printf("CPSR on entry: %08X\n", lldbg_entry_cpsr);
+	printf("SP on entry after register save: %08X\n", lldbg_entry_sp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/lldbg.lds	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,42 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_entry)
+SECTIONS
+{
+    /* code */
+    . = 0x3B0000;
+    .text : {
+        /* regular code */
+        *(.text*)
+        /* gcc voodoo */
+        *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
+        . = ALIGN(4);
+    }
+
+    /* read-only data */
+    . = ALIGN(4);
+    .rodata : {
+        *(.rodata*)
+    }
+
+    /* initialized data */
+    . = ALIGN(4);
+    .data : {
+        *(.data)
+    }
+    PROVIDE(edata = .);
+
+    /* uninitialized data */
+    .bss 0x83C000 (NOLOAD) : {
+        . = ALIGN(4);
+        __bss_start = .;
+        *(.bss)
+    }
+    . = ALIGN(4);
+    __bss_end = .;
+    /* end of image */
+    _end = .;
+    PROVIDE(end = .);
+}
+
+stack_bottom = 0x83FFFC;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,9 @@
+main()
+{
+	printf("\2\2\2*Standalone Low Level Debugger entered\2");
+	for (;;) {
+		putchar('>');
+		if (command_entry())
+			command_dispatch();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/mygetchar.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+/*
+ * The interactive command entry (editing) function in libcommon
+ * will call mygetchar() for its character input.  It is supposed
+ * to be a blocking wait for input, but in some programs other
+ * processing can be done while waiting - for example, check for
+ * keypad presses as well.  This is the basic version which waits
+ * for serial input and nothing else.
+ */
+
+extern int serial_in_poll();
+
+int
+mygetchar()
+{
+	register int c;
+
+	do
+		c = serial_in_poll();
+	while (c < 0);
+	return c;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139-lldbg/uartbase.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,5 @@
+	.section	.rodata
+	.balign	4
+	.globl	uart_base
+uart_base:
+	.word	0xFFFF5800
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,30 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+LD=	arm-elf-ld
+OBJCOPY=arm-elf-objcopy
+
+PROG=	c139explore
+OBJS=	crt0.o backlight.o cmdtab.o lcd.o main.o mygetchar.o uartbase.o uwire.o
+LIBS=	../libcommon/libcommon.a ../libprintf/libprintf.a ../libbase/libbase.a
+LDS=	../env/compalram.lds
+
+TC_LIBS=`${CC} -print-file-name=libc.a` \
+	`${CC} -print-file-name=libgcc.a`
+
+all:	${PROG}.bin
+
+crt0.S:	../env/crt0.S
+	ln -s $< .
+
+${PROG}.elf:	${OBJS} ${LIBS} ${LDS}
+	${LD} -N -T ${LDS} -o $@ ${OBJS} ${LIBS} \
+		--start-group ${TC_LIBS} --end-group
+
+${PROG}.bin:	${PROG}.elf
+	${OBJCOPY} -O binary $< $@
+
+clean:
+	rm -f *.o *errs *core *.elf *.bin *.srec crt0.S
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/backlight.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,42 @@
+#include <sys/types.h>
+#include <string.h>
+#include "types.h"
+#include "abbdefs.h"
+
+#define	GPIO_OUT_REG		(*(volatile u16 *) 0xfffe4802)
+#define	BACKLIGHT_GPIO_MASK	0x0002
+
+#define	AUXLED_KPBL_OFF	0x000
+#define	AUXLED_KPBL_ON	0x002
+
+void
+cmd_dbl(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (!strcmp(argv[0], "on"))
+		GPIO_OUT_REG |= BACKLIGHT_GPIO_MASK;
+	else if (!strcmp(argv[0], "off"))
+		GPIO_OUT_REG &= ~BACKLIGHT_GPIO_MASK;
+	else
+		printf("ERROR: \"on\" or \"off\" argument expected\n");
+}
+
+void
+cmd_kpbl(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (!strcmp(argv[0], "on"))
+		abb_reg_write(AUXLED, AUXLED_KPBL_ON);
+	else if (!strcmp(argv[0], "off"))
+		abb_reg_write(AUXLED, AUXLED_KPBL_OFF);
+	else
+		printf("ERROR: \"on\" or \"off\" argument expected\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/cmdtab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,47 @@
+#include "cmdtab.h"
+
+extern void cmd_abbr();
+extern void cmd_abbw();
+extern void cmd_dbl();
+extern void cmd_hbars();
+extern void cmd_jump();
+extern void cmd_kpbl();
+extern void cmd_lcdcmd();
+extern void cmd_lcdfill();
+extern void cmd_lcdinit();
+extern void cmd_lcdpix();
+extern void cmd_r8();
+extern void cmd_r16();
+extern void cmd_r32();
+extern void cmd_vbars();
+extern void cmd_w8();
+extern void cmd_w16();
+extern void cmd_w32();
+
+extern void abb_init();
+extern void abb_power_off();
+extern void cmd_memdump_human();
+
+const struct cmdtab cmdtab[] = {
+	{"abbinit", abb_init},
+	{"abbr", cmd_abbr},
+	{"abbw", cmd_abbw},
+	{"dbl", cmd_dbl},
+	{"dump", cmd_memdump_human},
+	{"hbars", cmd_hbars},
+	{"jump", cmd_jump},
+	{"kpbl", cmd_kpbl},
+	{"lcdcmd", cmd_lcdcmd},
+	{"lcdfill", cmd_lcdfill},
+	{"lcdinit", cmd_lcdinit},
+	{"lcdpix", cmd_lcdpix},
+	{"poweroff", abb_power_off},
+	{"r8", cmd_r8},
+	{"r16", cmd_r16},
+	{"r32", cmd_r32},
+	{"vbars", cmd_vbars},
+	{"w8", cmd_w8},
+	{"w16", cmd_w16},
+	{"w32", cmd_w32},
+	{0, 0}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/lcd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,174 @@
+#include <sys/types.h>
+#include "types.h"
+
+static void
+send_cmd_data(cmdbyte, databyte)
+{
+	send_via_uwire(cmdbyte);
+	send_via_uwire(databyte | 0x100);
+}
+
+void
+cmd_lcdcmd(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long cmd, data;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 2, &cmd) < 0) {
+		printf("ERROR: arg1 must be a valid 8-bit hex value\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 2, &data) < 0) {
+		printf("ERROR: arg2 must be a valid 8-bit hex value\n");
+		return;
+	}
+	send_cmd_data(cmd, data);
+}
+
+static void
+send_pixel_value(pix)
+{
+	send_via_uwire((pix >> 8) | 0x100);
+	send_via_uwire((pix & 0xFF) | 0x100);
+}
+
+void
+cmd_lcdpix(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long pixval;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 4, &pixval) < 0) {
+		printf("ERROR: arg1 must be a valid 16-bit hex value\n");
+		return;
+	}
+	send_pixel_value(pixval);
+}
+
+void
+cmd_lcdinit()
+{
+	/* from OsmocomBB */
+	send_cmd_data(0x3F, 0x01);
+	send_cmd_data(0x20, 0x03);
+	send_cmd_data(0x31, 0x03);
+}
+
+static void
+set_lcd_addr_region(xstart, xend, ystart, yend)
+{
+	send_cmd_data(0x10, xstart);
+	send_cmd_data(0x11, ystart);
+	send_cmd_data(0x12, xend);
+	send_cmd_data(0x13, yend);
+	send_cmd_data(0x14, xstart);
+	send_cmd_data(0x15, ystart);
+}
+
+void
+cmd_lcdfill(argbulk)
+	char *argbulk;
+{
+	int argc;
+	char *argv[6];
+	u_long pixval;
+	int xstart, xend, ystart, yend;
+	int npix;
+
+	if (parse_args(argbulk, 1, 5, argv, &argc) < 0)
+		return;
+	if (parse_hexarg(argv[0], 4, &pixval) < 0) {
+		printf("ERROR: arg1 must be a valid 16-bit hex value\n");
+		return;
+	}
+	switch (argc) {
+	case 1:
+		xstart = ystart = 0;
+		xend = 95;
+		yend = 63;
+		break;
+	case 5:
+		xstart = atoi(argv[1]);
+		if (xstart < 0 || xstart > 95) {
+range_err:		printf("ERROR: coordinate arg out of range\n");
+			return;
+		}
+		xend = atoi(argv[2]);
+		if (xend < 0 || xend > 95)
+			goto range_err;
+		ystart = atoi(argv[3]);
+		if (ystart < 0 || ystart > 63)
+			goto range_err;
+		yend = atoi(argv[4]);
+		if (yend < 0 || yend > 63)
+			goto range_err;
+		if (xend < xstart || yend < ystart) {
+			printf("ERROR: negative range\n");
+			return;
+		}
+		break;
+	default:
+		printf("ERROR: wrong number of arguments\n");
+		return;
+	}
+	set_lcd_addr_region(xstart, xend, ystart, yend);
+	npix = (xend + 1 - xstart) * (yend + 1 - ystart);
+	while (npix--)
+		send_pixel_value(pixval);
+}
+
+void
+cmd_hbars()
+{
+	int i, j, k, p;
+
+	/*
+	 * The result of this command should be 8 horizontal bars
+	 * in the natural RGB order.
+	 */
+	set_lcd_addr_region(16, 79, 0, 63);
+	for (i = 0; i < 8; i++) {
+		for (j = 0; j < 8; j++) {
+			p = 0;
+			if (i & 4)
+				p |= 0xF800;
+			if (i & 2)
+				p |= 0x07E0;
+			if (i & 1)
+				p |= 0x001F;
+			for (k = 0; k < 64; k++)
+				send_pixel_value(p);
+		}
+	}
+}
+
+void
+cmd_vbars()
+{
+	int i, j, k, p;
+
+	/*
+	 * The result of this command should be 8 vertical bars
+	 * in the natural RGB order.
+	 */
+	set_lcd_addr_region(16, 79, 0, 63);
+	for (i = 0; i < 64; i++) {
+		for (j = 0; j < 8; j++) {
+			p = 0;
+			if (j & 4)
+				p |= 0xF800;
+			if (j & 2)
+				p |= 0x07E0;
+			if (j & 1)
+				p |= 0x001F;
+			for (k = 0; k < 8; k++)
+				send_pixel_value(p);
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+#include "types.h"
+
+main()
+{
+	/* delay kludge workaround for defect in fc-compalram */
+	osmo_delay_ms(30);
+	printf("C139 hardware exploration utility running\n");
+	/* GPIO init */
+	*(volatile u16 *)0xfffe4802 = 0x0002;
+	*(volatile u16 *)0xfffe4804 = 0xFFF5;
+	/* take peripherals out of reset */
+	*(volatile u16 *)0xfffffd04 = 0xFFF3;
+	abb_init();
+	uwire_init();
+	for (;;) {
+		putchar('=');
+		if (command_entry())
+			command_dispatch();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/mygetchar.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+/*
+ * The interactive command entry (editing) function in libcommon
+ * will call mygetchar() for its character input.  It is supposed
+ * to be a blocking wait for input, but in some programs other
+ * processing can be done while waiting - for example, check for
+ * keypad presses as well.  This is the basic version which waits
+ * for serial input and nothing else.
+ */
+
+extern int serial_in_poll();
+
+int
+mygetchar()
+{
+	register int c;
+
+	do
+		c = serial_in_poll();
+	while (c < 0);
+	return c;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/uartbase.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,5 @@
+	.section	.rodata
+	.balign	4
+	.globl	uart_base
+uart_base:
+	.word	0xFFFF5800
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/c139explore/uwire.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,103 @@
+/* Driver for uWire Master Controller inside TI Calypso */
+/* lifted from OsmocomBB and ported to FreeCalypso target-utils environment */
+
+/* (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "types.h"
+
+struct uwire_regs {
+	u16	reg_data;
+	u16	reg_csr;
+	u16	reg_sr1;
+	u16	reg_sr2;
+	u16	reg_sr3;
+};
+
+#define	UWIRE_REGS	(*(volatile struct uwire_regs *) 0xFFFE4000)
+
+#define UWIRE_CSR_BITS_RD(n)	(((n) & 0x1f) << 0)
+#define UWIRE_CSR_BITS_WR(n)	(((n) & 0x1f) << 5)
+#define UWIRE_CSR_IDX(n)	(((n) & 3) << 10)
+#define UWIRE_CSR_CS_CMD	(1 << 12)
+#define UWIRE_CSR_START		(1 << 13)
+#define UWIRE_CSR_CSRB		(1 << 14)
+#define UWIRE_CSR_RDRB		(1 << 15)
+
+#define UWIRE_CSn_EDGE_RD	(1 << 0)	/* 1=falling 0=rising */
+#define UWIRE_CSn_EDGE_WR	(1 << 1)	/* 1=falling 0=rising */
+#define UWIRE_CSn_CS_LVL	(1 << 2)
+#define UWIRE_CSn_FRQ_DIV2	(0 << 3)
+#define UWIRE_CSn_FRQ_DIV4	(1 << 3)
+#define UWIRE_CSn_FRQ_DIV8	(2 << 3)
+#define UWIRE_CSn_CKH
+
+#define UWIRE_CSn_SHIFT(n)	(((n) & 1) ? 6 : 0)
+#define UWIRE_CSn_REG(n)	(((n) & 2) ? REG_SR2 : REG_SR1)
+
+#define UWIRE_SR3_CLK_EN	(1 << 0)
+#define UWIRE_SR3_CLK_DIV2	(0 << 1)
+#define UWIRE_SR3_CLK_DIV4	(1 << 1)
+#define UWIRE_SR3_CLK_DIV7	(2 << 1)
+#define UWIRE_SR3_CLK_DIV10	(3 << 1)
+
+static inline void _uwire_wait(int mask, int val)
+{
+	while ((UWIRE_REGS.reg_csr & mask) != val);
+}
+
+/*
+ * Let's try changing the chip select logic from OsmocomBB way
+ * to the way seen in TI's R2D source.
+ */
+
+void uwire_init(void)
+{
+	UWIRE_REGS.reg_sr3 = UWIRE_SR3_CLK_EN | UWIRE_SR3_CLK_DIV2;
+	UWIRE_REGS.reg_sr1 = UWIRE_CSn_FRQ_DIV2;
+#if 0
+	UWIRE_REGS.reg_sr1 = UWIRE_CSn_CS_LVL | UWIRE_CSn_FRQ_DIV2;
+	UWIRE_REGS.reg_csr = UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD;
+	_uwire_wait(UWIRE_CSR_CSRB, 0);
+#endif
+}
+
+send_via_uwire(word)
+	unsigned word;
+{
+#if 0
+	/* select the chip */
+	UWIRE_REGS.reg_csr = UWIRE_CSR_IDX(0) | UWIRE_CSR_CS_CMD;
+	_uwire_wait(UWIRE_CSR_CSRB, 0);
+#endif
+
+	UWIRE_REGS.reg_data = word << 7;
+	UWIRE_REGS.reg_csr = UWIRE_CSR_BITS_WR(9) | UWIRE_CSR_START
+				| UWIRE_CSR_CS_CMD;
+	_uwire_wait(UWIRE_CSR_CSRB, 0);
+
+	/* unselect the chip */
+	UWIRE_REGS.reg_csr = UWIRE_CSR_IDX(0) | 0;
+#if 0
+	_uwire_wait(UWIRE_CSR_CSRB, 0);
+#endif
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/compalstage/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,30 @@
+CC=	arm-elf-gcc
+OBJCOPY=arm-elf-objcopy
+
+TARGETS=compalstage-plain.bin compalstage-thumb.bin compalstage-1003.bin
+INSTDIR=/usr/local/share/freecalypso
+
+all:	${TARGETS}
+
+.SUFFIXES: .o .bin
+
+.o.bin:
+	${OBJCOPY} -O binary $< $@
+
+compalstage-1003.o:	compalstage.S
+	${CC} -DPAD_TO_1003 -c -o $@ $<
+
+compalstage-plain.o:	compalstage.S
+	${CC} -c -o $@ $<
+
+compalstage-thumb.o:	compalstage.S
+	${CC} -DTHUMB_ENTRY -c -o $@ $<
+
+install:
+	mkdir -p ${INSTDIR}
+	install -c ${TARGETS} ${INSTDIR}
+
+clean:
+	rm -f *.o *errs *core *.bin
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/compalstage/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,33 @@
+FreeCalypso loadtools have been designed from the beginning to work through
+the Calypso chip's own boot ROM.  This approach works great for Openmoko and
+Pirelli targets, but Compal phones unfortunately have this Calypso boot ROM
+disabled at the board level.  To run our own code in these phones instead of
+booting the regular firmware, we need to go through Compal's own boot code.
+The latter allows loading code into IRAM and jumping to it, but not in the
+same way as how we do it through the Calypso boot ROM.
+
+One could argue that the "proper" way to support these Compal phones would be
+to build a different version of our loadagent that is designed to be loaded
+through Compal's boot code instead of the Calypso boot ROM, and then redesign
+our fc-loadtool and fc-xram utilities to work with different loadagents loaded
+in different ways on different target devices.  But I don't feel like doing
+that - too invasive to the once-clean design of loadtools.
+
+Hence I am adopting a different solution that works in the same way as
+OsmocomBB's "chain loading": the IRAM image that is fed to Compal's boot code
+is not our real loadagent, but a tiny piece of code that enables the Calypso
+boot ROM and jumps to it.  All loadtools host programs will include this
+optional "Compal stage" at the beginning, enabled for targets that need it,
+but will then always fall into the Calypso boot ROM IRAM download path.
+
+The approach I'm adopting is doubly inefficient for Mot C139/140 phones whose
+bootloader effectively requires that the downloaded image be ~15 KiB long
+even when we only need to download 32 bytes.  But hey, our ultimate goal is to
+produce our own Calypso phones, rather than hack those made by diabolical
+manufacturers who do not Respect Your Freedom; running our own gsm-fw on the
+Mot C139 is only a transitional step, so making fc-loadtool/fc-xram entry
+slower by a second or two is probably an acceptable price for keeping the
+code clean for the boot-ROM-enabled free world.
+
+In this directory we build the several different versions of compalstage
+needed for different C1xx models.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/compalstage/compalstage.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,27 @@
+	.text
+	.org	0
+
+#if THUMB_ENTRY
+	.code	16
+	bx	pc
+	nop
+#endif
+	.code	32
+
+/* delay loop like OsmocomBB does */
+	mov	r1, #0xa0000
+1:	subs	r1, r1, #1
+	bne	1b
+/* enable the Calypso boot ROM */
+	ldr	r1, reg_addr
+	mov	r2, #0x0100
+	strh	r2, [r1]
+/* jump to it! */
+	mov	pc, #0
+reg_addr:
+	.word	0xFFFFFB10
+
+#if PAD_TO_1003
+	.org	0x3be0
+	.ascii	"1003"
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/env/compalram.lds	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,47 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_entry)
+SECTIONS
+{
+    /* code */
+    . = 0x800100;
+    .text : {
+        /* regular code */
+        *(.text*)
+        /* gcc voodoo */
+        *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
+        . = ALIGN(4);
+    }
+
+    /* read-only data */
+    . = ALIGN(4);
+    .rodata : {
+        *(.rodata*)
+    }
+
+    /* initialized data */
+    . = ALIGN(4);
+    .data : {
+        *(.data)
+    }
+    PROVIDE(edata = .);
+
+    /* magic signature for C139/140 bootloader */
+    .magic 0x803ce0 : {
+	LONG(0x33303031)
+    }
+
+    /* uninitialized data */
+    .bss (NOLOAD) : {
+        . = ALIGN(4);
+        __bss_start = .;
+        *(.bss)
+    }
+    . = ALIGN(4);
+    __bss_end = .;
+    /* end of image */
+    _end = .;
+    PROVIDE(end = .);
+}
+
+stack_bottom = 0x83FFFC;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/env/crt0.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,19 @@
+#include "halt.h"
+
+	.text
+	.code	32
+	.global	_entry
+_entry:
+	ldr	sp, =stack_bottom
+@ zero bss
+	ldr	r0, =__bss_start
+	ldr	r2, =__bss_end
+	sub	r1, r2, r0
+	bl	bzero
+@ C code entry
+	bl	main
+	mov	r0, #HALTCODE_MAINEXITED
+	.global	_exit
+_exit:	nop
+1:	nop
+	b	1b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/env/iram.lds	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,50 @@
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_entry)
+SECTIONS
+{
+    /* code */
+    . = Base_addr;
+    .text : {
+        /* regular code */
+        *(.text*)
+        /* gcc voodoo */
+        *(.glue_7t) *(.glue_7) *(.vfp11_veneer) *(.v4_bx)
+        . = ALIGN(4);
+    }
+
+    /* read-only data */
+    . = ALIGN(4);
+    .rodata : {
+        *(.rodata*)
+    }
+
+    /* initialized data */
+    . = ALIGN(4);
+    .data : {
+        *(.data)
+    }
+    PROVIDE(edata = .);
+
+    /* uninitialized data */
+    .bss (NOLOAD) : {
+        . = ALIGN(4);
+        __bss_start = .;
+        *(.bss)
+    }
+    . = ALIGN(4);
+    __bss_end = .;
+    /* end of image */
+    _end = .;
+    PROVIDE(end = .);
+}
+
+/*
+ * stack_bottom will be set via the --defsym option to ld.
+ * Some programs have minimal IRAM requirements, so it would make more
+ * sense to set stack_bottom to 0x83FFFC, don't use the upper half of
+ * IRAM for anything, and make that program portable to Calypso Lite
+ * devices.  But for some other programs we might have some use for
+ * the larger IRAM of our full Calypso devices, in which case we would
+ * want to set stack_bottom to 0x87FFFC instead.
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/helloapp/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,31 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+LD=	arm-elf-ld
+OBJCOPY=arm-elf-objcopy
+
+PROG=	helloapp
+OBJS=	crt0.o cmdtab.o main.o mygetchar.o
+LIBS=	../libcommon/libcommon.a ../libprintf/libprintf.a ../libbase/libbase.a
+LDS=	../env/iram.lds
+
+TC_LIBS=`${CC} -print-file-name=libc.a` \
+	`${CC} -print-file-name=libgcc.a`
+
+all:	${PROG}.srec
+
+crt0.S:	../env/crt0.S
+	ln -s $< .
+
+${PROG}.elf:	${OBJS} ${LIBS} ${LDS}
+	${LD} -N --defsym Base_addr=0x800750 --defsym stack_bottom=0x83FFFC \
+		-T ${LDS} -o $@ ${OBJS} ${LIBS} \
+		--start-group ${TC_LIBS} --end-group
+
+${PROG}.srec:	${PROG}.elf
+	${OBJCOPY} -O srec --srec-forceS3 --srec-len=30 $< $@
+
+clean:
+	rm -f *.o *errs *core *.elf *.bin *.srec crt0.S
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/helloapp/cmdtab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+#include "cmdtab.h"
+
+extern void cmd_r8();
+extern void cmd_r16();
+extern void cmd_r32();
+extern void cmd_w8();
+extern void cmd_w16();
+extern void cmd_w32();
+
+const struct cmdtab cmdtab[] = {
+	{"r8", cmd_r8},
+	{"r16", cmd_r16},
+	{"r32", cmd_r32},
+	{"w8", cmd_w8},
+	{"w16", cmd_w16},
+	{"w32", cmd_w32},
+	{0, 0}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/helloapp/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+main()
+{
+	uart_select_init();
+	printf("Hello-world demo app running\n");
+	print_boot_rom_info();
+	for (;;) {
+		putchar('=');
+		if (command_entry())
+			command_dispatch();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/helloapp/mygetchar.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+/*
+ * The interactive command entry (editing) function in libcommon
+ * will call mygetchar() for its character input.  It is supposed
+ * to be a blocking wait for input, but in some programs other
+ * processing can be done while waiting - for example, check for
+ * keypad presses as well.  This is the basic version which waits
+ * for serial input and nothing else.
+ */
+
+extern int serial_in_poll();
+
+int
+mygetchar()
+{
+	register int c;
+
+	do
+		c = serial_in_poll();
+	while (c < 0);
+	return c;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/include/abbdefs.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,155 @@
+/* lifted from OsmocomBB */
+
+#ifndef _TWL3025_H
+#define _TWL3025_H
+
+#define PAGE(n)		(n << 7)
+enum twl3025_reg {
+	VRPCCFG		= PAGE(1) | 30,
+	VRPCDEV		= PAGE(0) | 30,
+	VRPCMSK		= PAGE(1) | 31,
+	VRPCMSKABB	= PAGE(1) | 29,
+	VRPCSTS		= PAGE(0) | 31,
+	/* Monitoring ADC Registers */
+	MADCTRL		= PAGE(0) | 13,
+	MADCSTAT	= PAGE(0) | 24,
+	VBATREG		= PAGE(0) | 15,
+	VCHGREG		= PAGE(0) | 16,
+	ICHGREG		= PAGE(0) | 17,
+	VBKPREG		= PAGE(0) | 18,
+	ADIN1REG	= PAGE(0) | 19,
+	ADIN2REG	= PAGE(0) | 20,
+	ADIN3REG	= PAGE(0) | 21,
+	ADIN4REG	= PAGE(0) | 22,
+	/* Clock Generator Registers */
+	TOGBR1		= PAGE(0) | 4,
+	TOGBR2		= PAGE(0) | 5,
+	PWDNRG		= PAGE(1) | 9,
+	TAPCTRL		= PAGE(1) | 19,
+	TAPREG		= PAGE(1) | 20,
+	/* Automatic Frequency Control (AFC) Registers */
+	AUXAFC1		= PAGE(0) | 7,
+	AUXAFC2		= PAGE(0) | 8,
+	AFCCTLADD	= PAGE(1) | 21,
+	AFCOUT		= PAGE(1) | 22,
+	/* Automatic Power Control (APC) Registers */
+	APCDEL1		= PAGE(0) | 2,
+	APCDEL2		= PAGE(1) | 26,
+	AUXAPC		= PAGE(0) | 9,
+	APCRAM		= PAGE(0) | 10,
+	APCOFF		= PAGE(0) | 11,
+	APCOUT		= PAGE(1) | 12,
+	/* Auxiliary DAC Control Register */
+	AUXDAC		= PAGE(0) | 12,
+	/* SimCard Control Register */
+	VRPCSIM		= PAGE(1) | 23,
+	/* LED Driver Register */
+	AUXLED		= PAGE(1) | 24,
+	/* Battery Charger Interface (BCI) Registers */
+	CHGREG		= PAGE(0) | 25,
+	BCICTL1		= PAGE(0) | 28,
+	BCICTL2		= PAGE(0) | 29,
+	BCICONF		= PAGE(1) | 13,
+	/* Interrupt and Bus Control (IBIC) Registers */
+	ITMASK		= PAGE(0) | 28,
+	ITSTATREG	= PAGE(0) | 27,	/* both pages! */
+	PAGEREG		= PAGE(0) | 1, 	/* both pages! */
+	/* Baseband Codec (BBC) Registers */
+	BULIOFF		= PAGE(1) | 2,
+	BULQOFF		= PAGE(1) | 3,
+	BULIDAC		= PAGE(1) | 5,
+	BULQDAC		= PAGE(1) | 4,
+	BULGCAL		= PAGE(1) | 14,
+	BULDATA1	= PAGE(0) | 3,	/* 16 words */
+	BBCTRL		= PAGE(1) | 6,
+	/* Voiceband Codec (VBC) Registers */
+	VBCTRL1		= PAGE(1) | 8,
+	VBCTRL2		= PAGE(1) | 11,
+	VBPOP		= PAGE(1) | 10,
+	VBUCTRL		= PAGE(1) | 7,
+	VBDCTRL		= PAGE(0) | 6,
+};
+#define BULDATA2	BULDATA1
+
+/* available ADC inputs on IOTA */
+enum twl3025_dac_inputs {/* === Signal ============================= */
+	MADC_VBAT=0,	/* battery voltage / 4                      */
+	MADC_VCHG=1,	/* charger voltage / 5                      */
+	MADC_ICHG=2,	/* I-sense amp or CHGREG DAC output         */
+	MADC_VBKP=3,	/* backup battery voltage / 4               */
+	MADC_ADIN1=4,	/* VADCID, sense battery type, not used     */
+	MADC_ADIN2=5,	/* Temperature sensor in Battery            */
+	MADC_ADIN3=6,	/* Mode_detect: sense 2.5mm jack insertion  */
+	MADC_ADIN4=7,	/* RITA: TEMP_SEN                           */
+	MADC_NUM_CHANNELS=8
+};
+
+enum madcstat_reg_bits { /* monitoring ADC status register */
+	ADCBUSY = 0x01  /* if set, a conversion is currently going on */
+};
+
+/* BCICTL1 register bits */
+enum bcictl1_reg_bits {
+	MESBAT	= 1<<0,	/* connect resistive divider for bat voltage */
+	DACNBUF	= 1<<1,	/* bypass DAC buffer */
+	THSENS0	= 1<<3,	/* thermal sensor bias current (ADIN2), bit 0 */
+	THSENS1	= 1<<4,	/* "" bit 1 */
+	THSENS2	= 1<<5,	/* "" bit 2 */
+	THEN	= 1<<6,	/* enable thermal sensor bias current (ADIN1) */ 
+	TYPEN	= 1<<7	/* enable bias current for battery type reading */
+};
+
+/* BCICTL1 register bits */
+enum bcictl2_reg_bits {
+	CHEN	= 1<<0,	/* enable charger */
+	CHIV	= 1<<1,	/* 1=constant current, 0=constant voltage */
+	CHBPASSPA=1<<2,	/* full charging of the battery during pulse radio */
+	CLIB	= 1<<3,	/* calibrate I-to-V amp (short input pins) */
+	CHDISPA	= 1<<4,	/* disabel charging during pulse radio (???) */
+	LEDC	= 1<<5,	/* enable LED during charge */
+	CGAIN4	= 1<<6,	/* if set, I-to-V amp gain is reduced from 10 to 4 */
+	PREOFF	= 1<<7	/* disable battery precharge */
+};
+
+enum vrpcsts_reg_bits {
+	ONBSTS = 1<<0,	/* button push switched on the mobile */
+	ONRSTS = 1<<1,	/* RPWON terminal switched on the mobile */
+	ITWSTS = 1<<2,	/* ITWAKEUP terminal switched on the mobile */
+	CHGSTS = 1<<3,	/* plugging in charger has switched on the mobile */
+	ONREFLT= 1<<4,	/* state of PWON terminal after debouncing */
+	ONMRFLT= 1<<5,	/* state of RPWON terminal after debouncing */
+	CHGPRES= 1<<6	/* charger is connected */
+};
+
+enum togbr2_bits {
+	TOGBR2_KEEPR	= (1 << 0),	/* Clear KEEPON bit */
+	TOGBR2_KEEPS	= (1 << 1),	/* Set KEEPON bit */
+	TOGBR2_ACTR	= (1 << 2),	/* Dectivate MCLK */
+	TOGBR2_ACTS	= (1 << 3),	/* Activate MCLK */
+	TOGBR2_IBUFPTR1	= (1 << 4),	/* Initialize pointer of burst buffer 1 */
+	TOGBR2_IBUFPTR2	= (1 << 5),	/* Initialize pointer of burst buffer 2 */
+	TOGBR2_IAPCPTR	= (1 << 6),	/* Initialize pointer of APC RAM */
+};
+
+/* How a RAMP value is encoded */
+#define ABB_RAMP_VAL(up, down)	( ((down & 0x1F) << 5) | (up & 0x1F) )
+
+enum twl3025_unit {
+	TWL3025_UNIT_AFC,
+	TWL3025_UNIT_MAD,
+	TWL3025_UNIT_ADA,
+	TWL3025_UNIT_VDL,
+	TWL3025_UNIT_VUL,
+};
+
+enum twl3025_tsp_bits {
+	BULON		= 0x80,
+	BULCAL		= 0x40,
+	BULENA		= 0x20,
+	BDLON		= 0x10,
+	BDLCAL		= 0x08,
+	BDLENA		= 0x04,
+	STARTADC	= 0x02,
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/include/cmdtab.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,6 @@
+/* this structure is used for interactive command dispatch */
+
+struct cmdtab {
+	char	*cmd;
+	void	(*func)();
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/include/halt.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+/*
+ * In some error cases in our loadagent code we have no better course
+ * of action available than to halt in a tight loop.  We define _exit()
+ * to do the latter.  We have defined some codes for the argument value
+ * that goes into R0; if you manage to hook up JTAG and get it to work,
+ * you might be able to see what went wrong.
+ */
+
+#define	HALTCODE_MAINEXITED	0x40
+#define	HALTCODE_INVALIDUART	0x41
+#define	HALTCODE_BOOTROMVER	0x42
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/include/ns16550.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,101 @@
+#ifndef __NS16550_H
+#define	__NS16550_H
+
+/* NS16550 registers */
+#define	NS16550_RBR	0
+#define	NS16550_THR	0
+#define	NS16550_IER	1
+#define	NS16550_IIR	2
+#define	NS16550_FCR	2
+#define	NS16550_LCR	3
+#define	NS16550_MCR	4
+#define	NS16550_LSR	5
+#define	NS16550_MSR	6
+#define	NS16550_SCR	7
+#define	NS16550_DLL	0
+#define	NS16550_DLM	1
+
+#ifndef __ASSEMBLER__
+#include "types.h"
+
+struct ns16550_regs {
+	u8	datareg;
+	u8	ier;
+	u8	iir_fcr;
+	u8	lcr;
+	u8	mcr;
+	u8	lsr;
+	u8	msr;
+	u8	scr;
+};
+#endif
+
+/* IER bits */
+#define	NS16550_IER_EDSSI	0x08
+#define	NS16550_IER_ELSI	0x04
+#define	NS16550_IER_ETBEI	0x02
+#define	NS16550_IER_ERBFI	0x01
+
+/* IIR bits */
+#define	NS16550_IIR_FIFOEN	0xC0
+#define	NS16550_IIR_INTID	0x0E
+#define	NS16550_IIR_INT_RLS	0x06
+#define	NS16550_IIR_INT_RDA	0x04
+#define	NS16550_IIR_INT_CTO	0x0C
+#define	NS16550_IIR_INT_THRE	0x02
+#define	NS16550_IIR_INT_MODEM	0x00
+#define	NS16550_IIR_INTPEND	0x01
+
+/* FCR bits */
+
+#define	NS16550_FCR_RXTR	0xC0
+#define	NS16550_FCR_RXTR_1	0x00
+#define	NS16550_FCR_RXTR_4	0x40
+#define	NS16550_FCR_RXTR_8	0x80
+#define	NS16550_FCR_RXTR_14	0xC0
+#define	NS16550_FCR_DMAMODE	0x08
+#define	NS16550_FCR_TXRST	0x04
+#define	NS16550_FCR_RXRST	0x02
+#define	NS16550_FCR_FIFOEN	0x01
+
+/* LCR bits */
+#define	NS16550_LCR_DLAB	0x80
+#define	NS16550_LCR_BREAK	0x40
+#define	NS16550_LCR_STICK	0x20
+#define	NS16550_LCR_EPS		0x10
+#define	NS16550_LCR_PEN		0x08
+#define	NS16550_LCR_STB		0x04
+#define	NS16550_LCR_WLS		0x03
+#define	NS16550_LCR_WLS_5	0x00
+#define	NS16550_LCR_WLS_6	0x01
+#define	NS16550_LCR_WLS_7	0x02
+#define	NS16550_LCR_WLS_8	0x03
+
+/* MCR bits */
+#define	NS16550_MCR_LOOP	0x10
+#define	NS16550_MCR_OUT2	0x08
+#define	NS16550_MCR_OUT1	0x04
+#define	NS16550_MCR_RTS		0x02
+#define	NS16550_MCR_DTR		0x01
+
+/* LSR bits */
+#define	NS16550_LSR_ERR		0x80
+#define	NS16550_LSR_TEMP	0x40
+#define	NS16550_LSR_THRE	0x20
+#define	NS16550_LSR_BI		0x10
+#define	NS16550_LSR_FE		0x08
+#define	NS16550_LSR_PE		0x04
+#define	NS16550_LSR_OE		0x02
+#define	NS16550_LSR_DR		0x01
+
+/* MSR bits */
+#define	NS16550_MSR_DCD		0x80
+#define	NS16550_MSR_RI		0x40
+#define	NS16550_MSR_DSR		0x20
+#define	NS16550_MSR_CTS		0x10
+#define	NS16550_MSR_DDCD	0x08
+#define	NS16550_MSR_TERI	0x04
+#define	NS16550_MSR_DDSR	0x02
+#define	NS16550_MSR_DCTS	0x01
+
+#endif	/* __NS16550_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/include/romvars.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,36 @@
+/*
+ * Our loadagent will always be loaded into Calypso targets by the on-chip
+ * boot ROM operating in the UART download mode.  The lowest IRAM address
+ * at which we can load our code is 0x800750; somewhat lower at 0x800518
+ * the boot ROM downloader has a few variables which may have been intended
+ * to be private to the boot ROM, but which are useful to us.  For example,
+ * by looking at these variables, we can see which of the two UARTs was
+ * used to feed our code to the boot ROM, and use the same UART for
+ * subsequent communication - without building multiple versions of our
+ * loadagent or resorting to other ugliness.
+ *
+ * This header file defines the layout of the IRAM structure in question,
+ * based on the disassembly of the boot ROM.
+ */
+
+#ifndef __ROMVARS_H
+#define	__ROMVARS_H
+
+#include "types.h"
+
+struct boot_rom_vars {
+	u8	baud_rate_code;
+	u8	pad1[3];
+	u32	uart_timeout;
+	u8	uart_id;
+	u8	pll_config;
+	u16	cs_ws_config;
+	u8	clktcxo_13mhz;
+	u8	rhea_cntl;
+	u16	chksum_cmd;
+	u16	chksum_accum;
+	u16	pad2;
+	u32	branch_addr;
+};
+
+#endif	/* include guard */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/include/rtc.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,34 @@
+/* Calypso RTC registers */
+
+#ifndef __CALYPSO_RTC_H
+#define	__CALYPSO_RTC_H
+
+#include "types.h"
+
+#define	RTC_REGS_BASE	0xFFFE1800
+
+struct rtctime {
+	u8	seconds;
+	u8	minutes;
+	u8	hours;
+	u8	day_of_month;
+	u8	month;
+	u8	year;
+	u8	day_of_week;
+	u8	pad;
+};
+
+struct rtcregs {
+	struct rtctime	rtc_cur;
+	struct rtctime	rtc_alarm;
+	u8		rtc_ctrl_reg;
+	u8		rtc_status_reg;
+	u8		rtc_int_reg;
+	u8		rtc_comp_lsb_reg;
+	u8		rtc_comp_msb_reg;
+	u8		rtc_res_prog_reg;
+};
+
+#define	RTC_REGS	(*(volatile struct rtcregs *) RTC_REGS_BASE)
+
+#endif	/* include guard */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/include/types.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+/*
+ * I personally like the u8/u16/u32 types, but I don't see them
+ * being defined in any of the headers provided by newlib.
+ * So we'll define them ourselves.
+ */
+
+#ifndef	__OUR_OWN_TYPES_H
+#define	__OUR_OWN_TYPES_H
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+
+typedef signed char s8;
+typedef signed short s16;
+typedef signed int s32;
+
+#endif	/* include guard */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libbase/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,16 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+AR=	arm-elf-ar
+RANLIB=	arm-elf-ranlib
+
+OBJS=	abbdrv.o osmodelay.o serio.o spidrv.o
+
+all:	libbase.a
+
+libbase.a:	${OBJS}
+	${AR} cru $@ ${OBJS}
+	${RANLIB} $@
+
+clean:
+	rm -f *.[oa] *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libbase/abbdrv.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,102 @@
+/* Driver for Analog Baseband Circuit (TWL3025) */
+/* lifted from OsmocomBB and ported to FreeCalypso target-utils environment */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "types.h"
+#include "abbdefs.h"
+
+/* TWL3025 */
+#define REG_PAGE(n)	((n) >> 7)
+#define REG_ADDR(n)	((n) & 0x1f)
+
+#define TWL3025_DEV_IDX		0	/* On the SPI bus */
+#define TWL3025_TSP_DEV_IDX	0	/* On the TSP bus */
+
+int abb_state_initdone, abb_state_page;
+
+void
+abb_reg_write(reg, data)
+{
+	u16 tx;
+
+	if (reg != PAGEREG && REG_PAGE(reg) != abb_state_page)
+		abb_select_page(REG_PAGE(reg));
+
+	tx = ((data & 0x3ff) << 6) | (REG_ADDR(reg) << 1);
+
+	spi_xfer(TWL3025_DEV_IDX, 16, &tx, 0);
+}
+
+u16
+abb_reg_read(reg)
+{
+	u16 tx, rx;
+
+	if (REG_PAGE(reg) != abb_state_page)
+		abb_select_page(REG_PAGE(reg));
+
+	tx = (REG_ADDR(reg) << 1) | 1;
+
+	/* A read cycle contains two SPI transfers */
+	spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx);
+	osmo_delay_ms(1);
+	spi_xfer(TWL3025_DEV_IDX, 16, &tx, &rx);
+
+	rx >>= 6;
+
+	return rx;
+}
+
+/* Switch the register page of the TWL3025 */
+abb_select_page(page)
+{
+	if (page == 0)
+		abb_reg_write(PAGEREG, 1 << 0);
+	else
+		abb_reg_write(PAGEREG, 1 << 1);
+	abb_state_page = page;
+	return(0);
+}
+
+abb_init()
+{
+	if (abb_state_initdone)
+		return(0);
+	spi_init();
+	abb_select_page(0);
+	/* CLK13M enable */
+	abb_reg_write(TOGBR2, TOGBR2_ACTS);
+	osmo_delay_ms(1);
+	/* for whatever reason we need to do this twice */
+	abb_reg_write(TOGBR2, TOGBR2_ACTS);
+	osmo_delay_ms(1);
+	abb_state_initdone = 1;
+	return(1);
+}
+
+void
+abb_power_off()
+{
+	abb_init();
+	serial_flush();
+	abb_reg_write(VRPCDEV, 0x01);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libbase/osmodelay.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,30 @@
+/*
+ * This assembly module provides a replica of OsmocomBB's bogo-millisecond
+ * delay_ms() function.  It is literally a copy of what OsmocomBB's delay_ms()
+ * compiles to with their gcc version and their optimization settings, as seen
+ * by doing arm-elf-objdump on their lib/delay.o.
+ *
+ * This hack is intended for those cases where we have to copy OsmocomBB's
+ * black magic voodoo operations with no ability to understand what is actually
+ * needed, such as SPCA552E initialization on the Pirelli DP-L10.
+ */
+
+	.text
+	.code	32
+	.globl	osmo_delay_ms
+osmo_delay_ms:
+	mov     r3, #0
+	sub     sp, sp, #4
+	str     r3, [sp]
+	ldr     r3, =1300
+	mul     r3, r0, r3
+	b       2f
+1:	ldr     r2, [sp]
+	ldr     r2, [sp]
+	add     r2, r2, #1
+	str     r2, [sp]
+2:	ldr     r2, [sp]
+	cmp     r2, r3
+	bcc     1b
+	add     sp, sp, #4
+	bx      lr
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libbase/serio.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,36 @@
+#include "ns16550.h"
+
+@ this module implements the elementary serial I/O operations
+
+	.comm	uart_base,4,4
+
+	.text
+	.code	32
+	.global	serial_out
+serial_out:
+	ldr	r1, =uart_base
+	ldr	r2, [r1]
+1:	ldrb	r3, [r2, #NS16550_LSR]
+	tst	r3, #NS16550_LSR_THRE
+	beq	1b
+	strb	r0, [r2, #NS16550_THR]
+	bx	lr
+
+	.global	serial_in_poll
+serial_in_poll:
+	ldr	r1, =uart_base
+	ldr	r2, [r1]
+	ldrb	r3, [r2, #NS16550_LSR]
+	tst	r3, #NS16550_LSR_DR
+	ldrneb	r0, [r2, #NS16550_RBR]
+	mvneq	r0, #0
+	bx	lr
+
+	.global	serial_flush
+serial_flush:
+	ldr	r1, =uart_base
+	ldr	r2, [r1]
+1:	ldrb	r3, [r2, #NS16550_LSR]
+	tst	r3, #NS16550_LSR_TEMP
+	beq	1b
+	bx	lr
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libbase/spidrv.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,139 @@
+/* Driver for SPI Master Controller inside TI Calypso */
+/* lifted from OsmocomBB and ported to FreeCalypso target-utils environment */
+
+/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "types.h"
+
+#define	ASIC_CONF_REG	(*(volatile u16 *) 0xFFFEF008)
+
+struct spi_regs {
+	u16	reg_set1;
+	u16	reg_set2;
+	u16	reg_ctrl;
+	u16	reg_status;
+	u16	reg_tx_lsb;
+	u16	reg_tx_msb;
+	u16	reg_rx_lsb;
+	u16	reg_rx_msb;
+};
+
+#define	SPI_REGS	(*(volatile struct spi_regs *) 0xFFFE3000)
+
+#define BASE_ADDR_SPI	0xfffe3000
+#define SPI_REG(n)	(BASE_ADDR_SPI+(n))
+
+#define SPI_SET1_EN_CLK		(1 << 0)
+#define SPI_SET1_WR_IRQ_DIS	(1 << 4)
+#define SPI_SET1_RDWR_IRQ_DIS	(1 << 5)
+
+#define SPI_CTRL_RDWR		(1 << 0)
+#define SPI_CTRL_WR		(1 << 1)
+#define SPI_CTRL_NB_SHIFT	2
+#define SPI_CTRL_AD_SHIFT	7
+
+#define SPI_STATUS_RE		(1 << 0)	/* Read End */
+#define SPI_STATUS_WE		(1 << 1)	/* Write End */
+
+spi_init()
+{
+	static int initdone;
+
+	if (initdone)
+		return(0);
+	ASIC_CONF_REG |= 0x6000;
+	SPI_REGS.reg_set1 = SPI_SET1_EN_CLK | SPI_SET1_WR_IRQ_DIS |
+				SPI_SET1_RDWR_IRQ_DIS;
+	SPI_REGS.reg_set2 = 0x0001;
+	initdone = 1;
+	return(1);
+}
+
+spi_xfer(dev_idx, bitlen, dout, din)
+	void *dout, *din;
+{
+	int bytes_per_xfer;
+	u16 reg_status, reg_ctrl = 0;
+	u32 tmp;
+
+	if (bitlen <= 0)
+		return 0;
+
+	if (bitlen > 32)
+		return -1;
+
+	if (dev_idx > 4)
+		return -1;
+
+	bytes_per_xfer = bitlen / 8;
+	if (bitlen % 8)
+		bytes_per_xfer ++;
+
+	reg_ctrl |= (bitlen - 1) << SPI_CTRL_NB_SHIFT;
+	reg_ctrl |= (dev_idx & 0x7) << SPI_CTRL_AD_SHIFT;
+
+	if (bitlen <= 8) {
+		tmp = *(u8 *)dout;
+		tmp <<= 24 + (8-bitlen);	/* align to MSB */
+	} else if (bitlen <= 16) {
+		tmp = *(u16 *)dout;
+		tmp <<= 16 + (16-bitlen);	/* align to MSB */
+	} else {
+		tmp = *(u32 *)dout;
+		tmp <<= (32-bitlen);		/* align to MSB */
+	}
+
+	/* fill transmit registers */
+	SPI_REGS.reg_tx_msb = tmp >> 16;
+	SPI_REGS.reg_tx_lsb = tmp;
+
+	/* initiate transfer */
+	if (din)
+		reg_ctrl |= SPI_CTRL_RDWR;
+	else
+		reg_ctrl |= SPI_CTRL_WR;
+	SPI_REGS.reg_ctrl = reg_ctrl;
+
+	/* wait until the transfer is complete */
+	while (1) {
+		reg_status = SPI_REGS.reg_status;
+		if (din && (reg_status & SPI_STATUS_RE))
+			break;
+		else if (reg_status & SPI_STATUS_WE)
+			break;
+	}
+	/* FIXME: calibrate how much delay we really need (seven 13MHz cycles) */
+	osmo_delay_ms(1);
+
+	if (din) {
+		tmp = SPI_REGS.reg_rx_msb << 16;
+		tmp |= SPI_REGS.reg_rx_lsb;
+
+		if (bitlen <= 8)
+			*(u8 *)din = tmp & 0xff;
+		else if (bitlen <= 16)
+			*(u16 *)din = tmp & 0xffff;
+		else
+			*(u32 *)din = tmp;
+	}
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+AR=	arm-elf-ar
+RANLIB=	arm-elf-ranlib
+
+OBJS=	abbcmd.o cmdentry.o dispatch.o hexarg.o parseargs.o uartsel.o \
+	cmd_baud_switch.o cmd_dieid.o cmd_jump.o cmd_r8.o cmd_r16.o cmd_r32.o \
+	cmd_w8.o cmd_w16.o cmd_w32.o cmd_memdump_human.o cmd_memdump_machine.o
+
+all:	libcommon.a
+
+libcommon.a:	${OBJS}
+	${AR} cru $@ ${OBJS}
+	${RANLIB} $@
+
+clean:
+	rm -f *.[oa] *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/abbcmd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,51 @@
+/*
+ * abbr pg reg		-- read ABB register
+ * abbw pg reg val	-- write ABB register
+ */
+
+#include <sys/types.h>
+#include "types.h"
+#include "abbdefs.h"
+
+extern u16 abb_reg_read();
+extern void abb_reg_write();
+
+void
+cmd_abbr(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u32 pg, reg, val;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	pg = strtoul(argv[0], 0, 0);
+	reg = strtoul(argv[1], 0, 0);
+	if (pg > 1 || reg > 31) {
+		printf("ERROR: argument(s) out of range\n");
+		return;
+	}
+	abb_init();
+	val = abb_reg_read(PAGE(pg) | reg);
+	printf("%03X\n", val);
+}
+
+void
+cmd_abbw(argbulk)
+	char *argbulk;
+{
+	char *argv[4];
+	u32 pg, reg, val;
+
+	if (parse_args(argbulk, 3, 3, argv, 0) < 0)
+		return;
+	pg = strtoul(argv[0], 0, 0);
+	reg = strtoul(argv[1], 0, 0);
+	val = strtoul(argv[2], 0, 16);
+	if (pg > 1 || reg > 31 || val > 0x3FF) {
+		printf("ERROR: argument(s) out of range\n");
+		return;
+	}
+	abb_init();
+	abb_reg_write(PAGE(pg) | reg, val);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_baud_switch.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,112 @@
+/*
+ * Baud rate switching command
+ */
+
+#include <stdlib.h>
+#include "types.h"
+#include "ns16550.h"
+
+extern struct ns16550_regs *uart_base;
+extern int serial_in_poll();
+
+static const struct tab {
+	int baud;
+	int divisor;
+} rate_table[] = {
+	/*
+	 * First support the rates and divisors implemented by the
+	 * Calypso boot ROM.  Dividing 13 MHz by 7 gives an approximation
+	 * of 115200 (x16); the divisors used by the boot ROM code for
+	 * the slower baud rates are all 7x the usual PC value.
+	 */
+	{115200, 7},
+	{57600, 7 * 2},
+	{38400, 7 * 3},
+	{28800, 7 * 4},
+	{19200, 7 * 6},
+	/*
+	 * Going faster than ~115200 baud means using a divisor
+	 * less than 7, resulting in a non-standard baud rate.
+	 * The /1, /2 and /4 seem like reasonable choices.
+	 */
+	{812500, 1},
+	{406250, 2},
+	{203125, 4},
+	/* that's all we really need to support */
+	{0, 0}
+};
+
+/*
+ * The following helper function actually switches the UART
+ * baud rate divisor.  Call serial_flush() first.  It returns the
+ * old divisor value.
+ *
+ * Note the u8 type for both the new and old divisor values.
+ * All supported divisors are well below 255, so we don't bother
+ * with the upper byte.
+ */
+static u8
+actually_switch_baud(newdiv)
+	u8 newdiv;
+{
+	volatile struct ns16550_regs *regs;
+	u8 save_lcr, save_old_baud;
+
+	regs = uart_base;
+	save_lcr = regs->lcr;
+	regs->lcr = save_lcr | NS16550_LCR_DLAB;
+	save_old_baud = regs->datareg;
+	regs->datareg = newdiv;
+	regs->lcr = save_lcr;
+	return(save_old_baud);
+}
+
+void
+cmd_baud_switch(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	int baudarg;
+	struct tab *tp;
+	u8 save_old_baud;
+	int c;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	baudarg = atoi(argv[0]);
+	for (tp = rate_table; tp->baud; tp++)
+		if (tp->baud == baudarg)
+			break;
+	if (!tp->baud) {
+		printf("ERROR: invalid/unimplemented baud rate argument\n");
+		return;
+	}
+
+	/* do it */
+	serial_flush();
+	save_old_baud = actually_switch_baud(tp->divisor);
+
+	/*
+	 * After getting the echo of this command at the old baud rate
+	 * (see the serial flush call just before switching the divisor),
+	 * the line will go silent from the user's perspective.
+	 * The user should wait just a little bit, then send us a 0x55 ('U')
+	 * at the new baud rate - we should be in the below loop waiting
+	 * for this character by then.  Receiving that character
+	 * correctly (0x55 was chosen for the bit pattern - unlikely to
+	 * be received if the sender is sending at a wrong baud rate)
+	 * will cause us to conclude this command and return a new '='
+	 * prompt at the new baud rate.
+	 *
+	 * If we get something else, we assume that someone messed up,
+	 * switch back to the old baud rate, scribble an error message
+	 * and return.
+	 */
+	do
+		c = serial_in_poll();
+	while (c < 0);
+	if (c != 0x55) {
+	  actually_switch_baud(save_old_baud);
+	  printf("ERROR: no \'U\' received, switched back to old baud rate\n");
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_dieid.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,10 @@
+#include "types.h"
+
+void
+cmd_dieid()
+{
+	u32 addr;
+
+	for (addr = 0xFFFEF010; addr <= 0xFFFEF016; addr += 2)
+		printf("%08X: %04X\n", addr, *(volatile u16 *)addr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_jump.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,24 @@
+/*
+ * jump hexaddr -- transfer control with BX
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_jump(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	serial_flush();
+	asm volatile ("bx %0" : : "r" (addr));
+	__builtin_unreachable();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_memdump_human.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,50 @@
+/*
+ * This is a human-oriented memory dump command.  The dump is given in
+ * both hex and ASCII, with readable spacing.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_memdump_human(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long start, length;
+	u_long offset;
+	u_char intbuf[16];
+	int i, c;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &start) < 0) {
+		printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 8, &length) < 0) {
+	    printf("ERROR: arg2 must be a valid 32-bit hex value (length)\n");
+		return;
+	}
+	if (start & 0xF || length & 0xF) {
+	    printf("ERROR: implementation limit: 16-byte alignment required\n");
+		return;
+	}
+	for (offset = 0; offset < length; offset += 0x10) {
+		bcopy(start + offset, intbuf, 0x10);
+		printf("%08X: ", start + offset);
+		for (i = 0; i < 16; i++) {
+			printf("%02X ", intbuf[i]);
+			if ((i & 3) == 3)
+				putchar(' ');
+		}
+		for (i = 0; i < 16; i++) {
+			c = intbuf[i];
+			if (c >= ' ' && c <= '~')
+				putchar(c);
+			else
+				putchar('.');
+		}
+		putchar('\n');
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_memdump_machine.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,50 @@
+/*
+ * This is a machine-oriented memory dump command.  The output is in the
+ * form of S3 records.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_memdump_machine(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long start, length;
+	u_long addr;
+	u_char srbuf[0x86], cksum;
+	int i;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &start) < 0) {
+		printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 8, &length) < 0) {
+	    printf("ERROR: arg2 must be a valid 32-bit hex value (length)\n");
+		return;
+	}
+	if (start & 0x7F || length & 0x7F) {
+	   printf("ERROR: implementation limit: 128-byte alignment required\n");
+		return;
+	}
+	srbuf[0] = 0x85;
+	for (addr = start; addr < start + length; addr += 0x80) {
+		srbuf[1] = addr >> 24;
+		srbuf[2] = addr >> 16;
+		srbuf[3] = addr >> 8;
+		srbuf[4] = addr;
+		bcopy(addr, srbuf + 5, 0x80);
+		cksum = 0;
+		for (i = 0; i < 0x85; i++)
+			cksum += srbuf[i];
+		srbuf[i] = ~cksum;
+		putchar('S');
+		putchar('3');
+		for (i = 0; i < 0x86; i++)
+			printf("%02X", srbuf[i]);
+		putchar('\n');
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_r16.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,26 @@
+/*
+ * r16 hexaddr -- read a 16-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_r16(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 1) {
+		printf("ERROR: unaligned address\n");
+		return;
+	}
+	printf("%04X\n", *(volatile u16 *)addr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_r32.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,26 @@
+/*
+ * r32 hexaddr -- read a 32-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_r32(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 3) {
+		printf("ERROR: unaligned address\n");
+		return;
+	}
+	printf("%08X\n", *(volatile u32 *)addr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_r8.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,22 @@
+/*
+ * r8 hexaddr -- read an 8-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_r8(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	printf("%02X\n", *(volatile u8 *)addr);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_w16.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,30 @@
+/*
+ * w16 hexaddr xxxx -- write a 16-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_w16(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long addr, data;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 1) {
+		printf("ERROR: unaligned address\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 4, &data) < 0) {
+		printf("ERROR: arg2 must be a valid 16-bit hex value\n");
+		return;
+	}
+	*(volatile u16 *)addr = data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_w32.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,30 @@
+/*
+ * w32 hexaddr xxxxxxxx -- write a 32-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_w32(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long addr, data;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 3) {
+		printf("ERROR: unaligned address\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 8, &data) < 0) {
+		printf("ERROR: arg2 must be a valid 32-bit hex value\n");
+		return;
+	}
+	*(volatile u32 *)addr = data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmd_w8.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,26 @@
+/*
+ * w8 hexaddr xx -- write an 8-bit register or memory location
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_w8(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long addr, data;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 2, &data) < 0) {
+		printf("ERROR: arg2 must be a valid 8-bit hex value\n");
+		return;
+	}
+	*(volatile u8 *)addr = data;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/cmdentry.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,72 @@
+/*
+ * This module implements ASCII command entry via the serial port,
+ * with normal echo and minimal editing (rubout and kill).
+ *
+ * The command string buffer is bss-allocated here as well.  It is
+ * sized to allow a maximum-size S-record to be sent as a command,
+ * as that is how we expect flash loading and XRAM chain-loading
+ * to be done.
+ */
+
+#define	MAXCMD	527
+
+char command[MAXCMD+1];
+
+/*
+ * The command_entry() function takes no arguments, and begins by waiting
+ * for serial input - hence the prompt should be printed before calling it.
+ *
+ * This function returns when one of the following characters is received:
+ * CR - accepts the command
+ * ^C or ^U - cancels the command
+ *
+ * The return value is non-zero if a non-empty command was accepted with CR,
+ * or 0 if the user hit CR with no input or if the command was canceled
+ * with ^C or ^U.  In any case a CRLF is sent out the serial port
+ * to close the input echo line before this function returns.
+ */
+command_entry()
+{
+	int inlen, ch;
+
+	for (inlen = 0; ; ) {
+		ch = mygetchar();
+		if (ch >= ' ' && ch <= '~') {
+			if (inlen < MAXCMD) {
+				command[inlen++] = ch;
+				putchar(ch);
+			} else
+				/* putchar(7) */;
+			continue;
+		}
+		switch (ch) {
+		case '\r':
+		case '\n':
+			command[inlen] = '\0';
+			putchar('\n');
+			return(inlen);
+		case '\b':	/* BS */
+		case 0x7F:	/* DEL */
+			if (inlen) {
+				putchar('\b');
+				putchar(' ');
+				putchar('\b');
+				inlen--;
+			} else
+				/* putchar(7) */;
+			continue;
+		case 0x03:	/* ^C */
+			putchar('^');
+			putchar('C');
+			putchar('\n');
+			return(0);
+		case 0x15:	/* ^U */
+			putchar('^');
+			putchar('U');
+			putchar('\n');
+			return(0);
+		default:
+			/* putchar(7) */;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/dispatch.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,32 @@
+/*
+ * This module implements the dispatch of interactively entered
+ * commands to their respective implementation functions via cmdtab.
+ */
+
+#include "cmdtab.h"
+
+extern char command[];
+extern struct cmdtab cmdtab[];
+
+void
+command_dispatch()
+{
+	char *cp, *np;
+	struct cmdtab *tp;
+
+	for (cp = command; *cp == ' '; cp++)
+		;
+	if (!*cp)
+		return;
+	for (np = cp; *cp && *cp != ' '; cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	for (tp = cmdtab; tp->cmd; tp++)
+		if (!strcmp(tp->cmd, np))
+			break;
+	if (tp->func)
+		tp->func(cp);
+	else
+		printf("ERROR: unknown or unimplemented command\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/hexarg.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,29 @@
+/*
+ * Many commands take hex arguments.  This module contains the parse_hexarg()
+ * function, which is a wrapper around strtoul that performs some additional
+ * checks.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+parse_hexarg(arg, maxdigits, valp)
+	char *arg;
+	int maxdigits;
+	u_long *valp;
+{
+	char *cp = arg, *bp;
+	int len;
+
+	if (cp[0] == '0' && (cp[1] == 'x' || cp[1] == 'X'))
+		cp += 2;
+	for (bp = cp; *cp; cp++)
+		if (!isxdigit(*cp))
+			return(-1);
+	len = cp - bp;
+	if (len < 1 || len > maxdigits)
+		return(-1);
+	*valp = strtoul(arg, 0, 16);
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/parseargs.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,39 @@
+/*
+ * This module contains the parse_args() function, which parses the "rest"
+ * part of an entered command into an argc/argv-style argument array.
+ */
+
+parse_args(unparsed, minargs, maxargs, argv, argcp)
+	char *unparsed;
+	int minargs, maxargs;
+	char **argv;
+	int *argcp;
+{
+	int argc;
+	char *cp;
+
+	argc = 0;
+	for (cp = unparsed; ; ) {
+		while (*cp == ' ')
+			cp++;
+		if (!*cp)
+			break;
+		if (argc >= maxargs) {
+			printf("ERROR: too many arguments\n");
+			return(-1);
+		}
+		argv[argc++] = cp;
+		while (*cp && *cp != ' ')
+			cp++;
+		if (*cp)
+			*cp++ = '\0';
+	}
+	if (argc < minargs) {
+		printf("ERROR: too few arguments\n");
+		return(-1);
+	}
+	argv[argc] = 0;
+	if (argcp)
+		*argcp = argc;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libcommon/uartsel.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,54 @@
+/*
+ * uart_select_init() figures out which UART was used to load us
+ * through the boot ROM, and sets things up for us to use the same
+ * UART for our communication.
+ */
+
+#include "types.h"
+#include "romvars.h"
+#include "ns16550.h"
+#include "halt.h"
+
+extern struct ns16550_regs *uart_base;
+
+static u16 rom_version;
+static struct boot_rom_vars *rom_vars;
+static char *uart_name;
+
+uart_select_init()
+{
+	rom_version = *(u16 *)0x1FFE;
+
+	switch (rom_version) {
+	case 0x0200:
+		rom_vars = (struct boot_rom_vars *) 0x800504;
+		break;
+	case 0x0300:
+		rom_vars = (struct boot_rom_vars *) 0x800518;
+		break;
+	default:
+		_exit(HALTCODE_BOOTROMVER);
+	}
+
+	switch (rom_vars->uart_id) {
+	case 0:
+		uart_base = (struct ns16550_regs *) 0xFFFF5800;
+		uart_name = "MODEM";
+		break;
+	case 1:
+		uart_base = (struct ns16550_regs *) 0xFFFF5000;
+		uart_name = "IrDA";
+		break;
+	default:
+		_exit(HALTCODE_INVALIDUART);
+	}
+}
+
+print_boot_rom_info()
+{
+	printf("Loaded via boot ROM v%04X, UART %d (%s) at baud rate #%d\n",
+		rom_version, rom_vars->uart_id, uart_name,
+		rom_vars->baud_rate_code);
+	printf("CLKTCXO input autodetected to be %d MHz\n",
+		rom_vars->clktcxo_13mhz ? 13 : 26);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libload/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,17 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+AR=	arm-elf-ar
+RANLIB=	arm-elf-ranlib
+
+OBJS=	cmd_blankchk.o cmd_crc32.o cmd_memload.o \
+	amdflash.o hexstrings.o intelflash.o
+
+all:	libload.a
+
+libload.a:	${OBJS}
+	${AR} cru $@ ${OBJS}
+	${RANLIB} $@
+
+clean:
+	rm -f *.[oa] *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libload/amdflash.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,90 @@
+/*
+ * This module implements the AMFB and AMFW commands for programming
+ * AMD-style flash memories.  Syntax:
+ *
+ * AMFB <baseaddr>	-- sets the base address for subsequent AMFW commands
+ * AMFW <offset> <hexstring>	-- the actual flash write operation
+ *
+ * The flash memory is assumed to be 16 bits wide.  The hex string
+ * argument to the AMFW command is just data, with no header, address,
+ * length, checksum or other additions.  The number of hex digits in the
+ * string must be a multiple of 4, and the byte order is the same as
+ * that of TI's *.m0 files: we interpret the string as consisting of
+ * 16-bit words rather than bytes.
+ *
+ * The address to which each flash write is directed is the sum of the
+ * base given to AMFB and the offset given to AMFW.  The fixed offsets
+ * of 0xAAA and 0x554 (0x555 and 0x2AA in words) prescribed for the flash
+ * programming command sequence are also made from the base set with AMFB.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+static u32 base_addr;
+
+void
+cmd_AMFB(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 1) {
+		printf("ERROR: odd address\n");
+		return;
+	}
+	base_addr = addr;
+}
+
+void
+cmd_AMFW(argbulk)
+	char *argbulk;
+{
+	char *argv[3], *s;
+	u_long offset;
+	volatile u16 *flashptr;
+	u32 datum;	/* needs to be u32 for decode_hex_digits() */
+	int i;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &offset) < 0) {
+	    printf("ERROR: offset argument must a valid 32-bit hex value\n");
+		return;
+	}
+	if (offset & 1) {
+		printf("ERROR: odd offset argument\n");
+		return;
+	}
+	flashptr = (volatile u16 *)(base_addr + offset);
+	for (s = argv[1]; *s; flashptr++, s += 4) {
+		if (decode_hex_digits(s, 4, &datum) < 0) {
+			printf("ERROR: bad AMFW hex string argument\n");
+			return;
+		}
+		if (*flashptr != 0xFFFF) {
+			printf("ERROR: flash not blank at %08X\n",
+				(u_long) flashptr);
+			return;
+		}
+		*(volatile u16 *)(base_addr + 0xAAA) = 0xAA;
+		*(volatile u16 *)(base_addr + 0x554) = 0x55;
+		*(volatile u16 *)(base_addr + 0xAAA) = 0xA0;
+		*flashptr = datum;
+		for (i = 10000; i; i--)
+			if (*flashptr == datum)
+				break;
+		if (!i) {
+			printf("ERROR: flash write timeout at %08X\n",
+				(u_long) flashptr);
+			return;
+		}
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libload/cmd_blankchk.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,35 @@
+/*
+ * Flash blank check command
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_blankchk(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long start, length;
+	u_long addr;
+	int c;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &start) < 0) {
+		printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 8, &length) < 0) {
+	    printf("ERROR: arg2 must be a valid 32-bit hex value (length)\n");
+		return;
+	}
+	for (addr = start; addr < start + length; addr++) {
+		c = *(volatile u8 *)addr;
+		if (c != 0xFF) {
+			printf("Not blank: %02X at %08X\n", c, addr);
+			return;
+		}
+	}
+	printf("OK\n");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libload/cmd_crc32.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,100 @@
+/*
+ * The crc32 command is an integrity checking aid for flash dumps
+ * via loadtool.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+/* This CRC-32 table has been computed for the LSB-first direction */
+
+static u32 crc32tab[256] = {
+	0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
+	0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
+	0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+	0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
+	0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
+	0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+	0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
+	0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
+	0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+	0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
+	0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
+	0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+	0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
+	0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
+	0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+	0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
+	0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
+	0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+	0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
+	0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
+	0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+	0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
+	0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
+	0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+	0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
+	0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
+	0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+	0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
+	0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
+	0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+	0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
+	0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
+	0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+	0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
+	0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
+	0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+	0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
+	0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
+	0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+	0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
+	0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
+	0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+	0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
+	0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
+	0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+	0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
+	0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
+	0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+	0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
+	0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
+	0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+	0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
+	0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
+	0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+	0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
+	0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
+	0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+	0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
+	0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
+	0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+	0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
+	0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
+	0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+	0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D,
+};
+
+void
+cmd_crc32(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long start, length;
+	u_long addr, crc;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &start) < 0) {
+		printf("ERROR: arg1 must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 8, &length) < 0) {
+	    printf("ERROR: arg2 must be a valid 32-bit hex value (length)\n");
+		return;
+	}
+	crc = 0xFFFFFFFF;
+	for (addr = start; addr < start + length; addr++)
+		crc = crc32tab[crc & 0xFF ^ *(volatile u8 *)addr] ^ (crc >> 8);
+	printf("%08X\n", crc);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libload/cmd_memload.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,52 @@
+/*
+ * This module implements the ML (memory load) command, which will be
+ * used by fc-chainload.
+ *
+ * The sole argument to the ML command is the body of an S3 record
+ * with the initial "S3" characters stripped, i.e., starting with the
+ * "count" byte, followed by the address, data and checksum bytes
+ * exactly as in the original S3 record.
+ */
+
+#include "types.h"
+
+void
+cmd_memload(argbulk)
+	char *argbulk;
+{
+	char *argv[2], *s;
+	u8 srecbin[256], cksum;
+	int len, i, c;
+	u32 addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	s = argv[0];
+	if (decode_hex_digits(s, 2, &len) < 0) {
+inv:		printf("ERROR: ML argument is invalid\n");
+		return;
+	}
+	s += 2;
+	if (len < 6)
+		goto inv;
+	srecbin[0] = len;
+	for (i = 1; i <= len; i++) {
+		if (decode_hex_digits(s, 2, &c) < 0)
+			goto inv;
+		s += 2;
+		srecbin[i] = c;
+	}
+	cksum = 0;
+	for (i = 0; i <= len; i++)
+		cksum += srecbin[i];
+	if (cksum != 0xFF) {
+		printf("ERROR: bad ML S-record checksum\n");
+		return;
+	}
+	len -= 5;
+	addr =  ((u32)srecbin[1] << 24) |
+		((u32)srecbin[2] << 16) |
+		((u32)srecbin[3] << 8) |
+		 (u32)srecbin[4];
+	bcopy(srecbin + 5, addr, len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libload/hexstrings.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,33 @@
+/*
+ * The decode_hex_digits() function contained in this module
+ * will be used by the XRAM and flash loading commands
+ * which take SREC-like long hex string arguments.
+ */
+
+#include <ctype.h>
+#include "types.h"
+
+decode_hex_digits(str, ndigits, valp)
+	char *str;
+	int ndigits;
+	u32 *valp;
+{
+	u32 accum;
+	int i, c;
+
+	accum = 0;
+	for (i = 0; i < ndigits; i++) {
+		c = *str++;
+		if (!isxdigit(c))
+			return(-1);
+		accum <<= 4;
+		if (isdigit(c))
+			accum += c - '0';
+		else if (islower(c))
+			accum += c - 'a' + 10;
+		else
+			accum += c - 'A' + 10;
+	}
+	*valp = accum;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libload/intelflash.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,139 @@
+/*
+ * This module implements the INFB and INFW commands for programming
+ * Intel-style flash memories.  The syntax and operation are exactly
+ * the same as the AMD flash counterparts AMFB and AMFW.
+ *
+ * The intel-rewrite-sector command (erase+program with a minimum of
+ * vulnerability for brickable-boot Compal phones) is implemented
+ * here as well.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+static u32 base_addr;
+
+void
+cmd_INFB(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u_long addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &addr) < 0) {
+		printf("ERROR: argument must be a valid 32-bit hex address\n");
+		return;
+	}
+	if (addr & 1) {
+		printf("ERROR: odd address\n");
+		return;
+	}
+	base_addr = addr;
+}
+
+void
+cmd_INFW(argbulk)
+	char *argbulk;
+{
+	char *argv[3], *s;
+	u_long offset;
+	volatile u16 *flashptr;
+	u32 datum;	/* needs to be u32 for decode_hex_digits() */
+	u16 stat;
+	int i;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &offset) < 0) {
+	    printf("ERROR: offset argument must a valid 32-bit hex value\n");
+		return;
+	}
+	if (offset & 1) {
+		printf("ERROR: odd offset argument\n");
+		return;
+	}
+	flashptr = (volatile u16 *)(base_addr + offset);
+	for (s = argv[1]; *s; flashptr++, s += 4) {
+		if (decode_hex_digits(s, 4, &datum) < 0) {
+			printf("ERROR: bad INFW hex string argument\n");
+			return;
+		}
+		*flashptr = 0x40;
+		*flashptr = datum;
+		for (i = 10000; i; i--) {
+			stat = *flashptr;
+			if (stat & 0x80)
+				break;
+		}
+		if (!i) {
+			printf("ERROR: flash write timeout at %08X\n",
+				(u_long) flashptr);
+			return;
+		}
+		if (stat & 0x10) {
+			printf("ERROR: program operation failed at %08X\n",
+				(u_long) flashptr);
+			return;
+		}
+	}
+}
+
+void
+cmd_intel_rewrite_sector(argbulk)
+	char *argbulk;
+{
+	char *argv[4];
+	u_long srcaddr, dstaddr, len;
+	const u16 *srcptr;
+	volatile u16 *flashptr;
+	u16 stat;
+
+	if (parse_args(argbulk, 3, 3, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 8, &srcaddr) < 0) {
+invarg:		printf("ERROR: invalid argument(s)\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 8, &dstaddr) < 0)
+		goto invarg;
+	if (parse_hexarg(argv[2], 8, &len) < 0)
+		goto invarg;
+	if (srcaddr & 1 || dstaddr & 1 || len & 1) {
+		printf("ERROR: all 3 arguments must be even\n");
+		return;
+	}
+	srcptr = (const u16 *) srcaddr;
+	flashptr = (volatile u16 *) dstaddr;
+	/* unlock the flash sector first */
+	*flashptr = 0x60;
+	*flashptr = 0xD0;
+	/* clear SR */
+	*flashptr = 0x50;
+	/* erase */
+	*flashptr = 0x20;
+	*flashptr = 0xD0;
+	/* wait for erase completion */
+	for (;;) {
+		stat = *flashptr;
+		if (stat & 0x80)
+			break;
+	}
+	if (stat & 0x30) {
+		printf("ERROR: erase operation failed!\n");
+		return;
+	}
+	/* now program the new content */
+	for (; len; len -= 2) {
+		*flashptr = 0x40;
+		*flashptr = *srcptr++;
+		for (;;) {
+			stat = *flashptr;
+			if (stat & 0x80)
+				break;
+		}
+		flashptr++;
+	}
+	printf("Operation complete, final SR: %02X\n", stat & 0xFF);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,16 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+AR=	arm-elf-ar
+RANLIB=	arm-elf-ranlib
+
+OBJS=	doprnt.o printf.o putchar.o puts.o sprintf.o sprintf_putchar.o \
+	vprintf.o vsprintf.o
+
+all:	libprintf.a
+
+libprintf.a:	${OBJS}
+	${AR} cru $@ ${OBJS}
+	${RANLIB} $@
+
+clean:
+	rm -f *.[oa] *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/README	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,15 @@
+The present libprintf is a very light printf implementation that is well-suited
+for simple bare-metal programs like loadagent; in the present case it overrides
+the much heavier printf implementation in newlib.  Programs like the present
+loadagent only need printf in order to scribble on the serial console port,
+and the most sensible implementation is to have the "character output" function
+from the guts of printf point directly to the physical UART output routine, or
+a trivial wrapper that turns \n into \r\n.  In contrast, newlib's version would
+pull in the complete FILE table infrastructure and malloc etc - maybe OK for
+more complex embedded programs that use those facilities for other things under
+a bona fide RTOS, but it would be disgusting to pull that stuff in for a
+minimal program like ours.
+
+The present printf implementation has been used earlier by the same author
+(Michael Spacefalcon) in the StarMON family of PowerPC bootloaders, and in my
+MC68x302-based SDSL CPE devices (Hack-o-Rocket and OSDCU).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/doprnt.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,297 @@
+/* the guts of printf - this implementation came from 4.3BSD-Tahoe */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#define	PUTC(ch)	((*outfunc)((ch), outfunc_param))
+
+#define	ARG() \
+	_ulong = flags&LONGINT ? va_arg(argp, long) : va_arg(argp, int);
+
+#define	BUF	12
+
+#define	todigit(c)	((c) - '0')
+#define	tochar(n)	((n) + '0')
+
+#define	LONGINT		0x01		/* long integer */
+#define	LONGDBL		0x02		/* long double; unimplemented */
+#define	SHORTINT	0x04		/* short integer */
+#define	ALT		0x08		/* alternate form */
+#define	LADJUST		0x10		/* left adjustment */
+#define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
+#define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
+
+_doprnt(fmt0, argp, outfunc, outfunc_param)
+	u_char *fmt0;
+	va_list argp;
+	void (*outfunc)();
+	void *outfunc_param;
+{
+	register u_char *fmt;	/* format string */
+	register int ch;	/* character from fmt */
+	register int cnt;	/* return value accumulator */
+	register int n;		/* random handy integer */
+	register char *t;	/* buffer pointer */
+	u_long _ulong;		/* integer arguments %[diouxX] */
+	int base;		/* base for [diouxX] conversion */
+	int dprec;		/* decimal precision in [diouxX] */
+	int fieldsz;		/* field size expanded by sign, etc */
+	int flags;		/* flags as above */
+	int prec;		/* precision from format (%.3d), or -1 */
+	int realsz;		/* field size expanded by decimal precision */
+	int size;		/* size of converted field or string */
+	int width;		/* width from format (%8d), or 0 */
+	char sign;		/* sign prefix (' ', '+', '-', or \0) */
+	char softsign;		/* temporary negative sign for floats */
+	char *digs;		/* digits for [diouxX] conversion */
+	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
+
+	fmt = fmt0;
+	digs = "0123456789abcdef";
+	for (cnt = 0;; ++fmt) {
+		for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
+			PUTC(ch);
+		if (!ch)
+			return (cnt);
+
+		flags = 0; dprec = 0; width = 0;
+		prec = -1;
+		sign = '\0';
+
+rflag:		switch (*++fmt) {
+		case ' ':
+			/*
+			 * ``If the space and + flags both appear, the space
+			 * flag will be ignored.''
+			 *	-- ANSI X3J11
+			 */
+			if (!sign)
+				sign = ' ';
+			goto rflag;
+		case '#':
+			flags |= ALT;
+			goto rflag;
+		case '*':
+			/*
+			 * ``A negative field width argument is taken as a
+			 * - flag followed by a  positive field width.''
+			 *	-- ANSI X3J11
+			 * They don't exclude field widths read from args.
+			 */
+			if ((width = va_arg(argp, int)) >= 0)
+				goto rflag;
+			width = -width;
+			/* FALLTHROUGH */
+		case '-':
+			flags |= LADJUST;
+			goto rflag;
+		case '+':
+			sign = '+';
+			goto rflag;
+		case '.':
+			if (*++fmt == '*')
+				n = va_arg(argp, int);
+			else {
+				n = 0;
+				while (isascii(*fmt) && isdigit(*fmt))
+					n = 10 * n + todigit(*fmt++);
+				--fmt;
+			}
+			prec = n < 0 ? -1 : n;
+			goto rflag;
+		case '0':
+			/*
+			 * ``Note that 0 is taken as a flag, not as the
+			 * beginning of a field width.''
+			 *	-- ANSI X3J11
+			 */
+			flags |= ZEROPAD;
+			goto rflag;
+		case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			n = 0;
+			do {
+				n = 10 * n + todigit(*fmt);
+			} while (isascii(*++fmt) && isdigit(*fmt));
+			width = n;
+			--fmt;
+			goto rflag;
+		case 'L':
+			flags |= LONGDBL;
+			goto rflag;
+		case 'h':
+			flags |= SHORTINT;
+			goto rflag;
+		case 'l':
+			flags |= LONGINT;
+			goto rflag;
+		case 'c':
+			*(t = buf) = va_arg(argp, int);
+			size = 1;
+			sign = '\0';
+			goto pforw;
+		case 'D':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'd':
+		case 'i':
+			ARG();
+			if ((long)_ulong < 0) {
+				_ulong = -_ulong;
+				sign = '-';
+			}
+			base = 10;
+			goto number;
+		case 'n':
+			if (flags & LONGINT)
+				*va_arg(argp, long *) = cnt;
+			else if (flags & SHORTINT)
+				*va_arg(argp, short *) = cnt;
+			else
+				*va_arg(argp, int *) = cnt;
+			break;
+		case 'O':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'o':
+			ARG();
+			base = 8;
+			goto nosign;
+		case 'p':
+			/*
+			 * ``The argument shall be a pointer to void.  The
+			 * value of the pointer is converted to a sequence
+			 * of printable characters, in an implementation-
+			 * defined manner.''
+			 *	-- ANSI X3J11
+			 */
+			/* NOSTRICT */
+			_ulong = (u_long)va_arg(argp, void *);
+			base = 16;
+			goto nosign;
+		case 's':
+			if (!(t = va_arg(argp, char *)))
+				t = "(null)";
+			if (prec >= 0) {
+				/*
+				 * can't use strlen; can only look for the
+				 * NUL in the first `prec' characters, and
+				 * strlen() will go further.
+				 */
+				char *p;
+
+				for (p = t, size = 0; size < prec; p++, size++)
+					if (*p == '\0')
+						break;
+			} else
+				size = strlen(t);
+			sign = '\0';
+			goto pforw;
+		case 'U':
+			flags |= LONGINT;
+			/*FALLTHROUGH*/
+		case 'u':
+			ARG();
+			base = 10;
+			goto nosign;
+		case 'X':
+			digs = "0123456789ABCDEF";
+			/* FALLTHROUGH */
+		case 'x':
+			ARG();
+			base = 16;
+			/* leading 0x/X only if non-zero */
+			if (flags & ALT && _ulong != 0)
+				flags |= HEXPREFIX;
+
+			/* unsigned conversions */
+nosign:			sign = '\0';
+			/*
+			 * ``... diouXx conversions ... if a precision is
+			 * specified, the 0 flag will be ignored.''
+			 *	-- ANSI X3J11
+			 */
+number:			if ((dprec = prec) >= 0)
+				flags &= ~ZEROPAD;
+
+			/*
+			 * ``The result of converting a zero value with an
+			 * explicit precision of zero is no characters.''
+			 *	-- ANSI X3J11
+			 */
+			t = buf + BUF;
+			if (_ulong != 0 || prec != 0) {
+				do {
+					*--t = digs[_ulong % base];
+					_ulong /= base;
+				} while (_ulong);
+				digs = "0123456789abcdef";
+				if (flags & ALT && base == 8 && *t != '0')
+					*--t = '0'; /* octal leading 0 */
+			}
+			size = buf + BUF - t;
+
+pforw:
+			/*
+			 * All reasonable formats wind up here.  At this point,
+			 * `t' points to a string which (if not flags&LADJUST)
+			 * should be padded out to `width' places.  If
+			 * flags&ZEROPAD, it should first be prefixed by any
+			 * sign or other prefix; otherwise, it should be blank
+			 * padded before the prefix is emitted.  After any
+			 * left-hand padding and prefixing, emit zeroes
+			 * required by a decimal [diouxX] precision, then print
+			 * the string proper, then emit zeroes required by any
+			 * leftover floating precision; finally, if LADJUST,
+			 * pad with blanks.
+			 */
+
+			/*
+			 * compute actual size, so we know how much to pad
+			 * fieldsz excludes decimal prec; realsz includes it
+			 */
+			fieldsz = size;
+			if (sign)
+				fieldsz++;
+			if (flags & HEXPREFIX)
+				fieldsz += 2;
+			realsz = dprec > fieldsz ? dprec : fieldsz;
+
+			/* right-adjusting blank padding */
+			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
+				for (n = realsz; n < width; n++)
+					PUTC(' ');
+			/* prefix */
+			if (sign)
+				PUTC(sign);
+			if (flags & HEXPREFIX) {
+				PUTC('0');
+				PUTC((char)*fmt);
+			}
+			/* right-adjusting zero padding */
+			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
+				for (n = realsz; n < width; n++)
+					PUTC('0');
+			/* leading zeroes from decimal precision */
+			for (n = fieldsz; n < dprec; n++)
+				PUTC('0');
+
+			for (n = size; --n >= 0; )
+				PUTC(*t++);
+			/* left-adjusting padding (always blank) */
+			if (flags & LADJUST)
+				for (n = realsz; n < width; n++)
+					PUTC(' ');
+			/* finally, adjust cnt */
+			cnt += width > realsz ? width : realsz;
+			break;
+		case '\0':	/* "%?" prints ?, unless ? is NULL */
+			return (cnt);
+		default:
+			PUTC((char)*fmt);
+			cnt++;
+		}
+	}
+	/* NOTREACHED */
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/printf.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,15 @@
+#include <stdarg.h>
+
+extern void putchar();
+
+int
+printf(char *fmt, ...)
+{
+	va_list ap;
+	int len;
+
+	va_start(ap, fmt);
+	len = _doprnt(fmt, ap, &putchar);
+	va_end(ap);
+	return(len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/putchar.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,9 @@
+extern void serial_out();
+
+void
+putchar(ch)
+{
+	if (ch == '\n')
+		serial_out('\r');
+	serial_out(ch);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/puts.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,10 @@
+void
+puts(s)
+	char *s;
+{
+	int c;
+
+	while (c = *s++)
+		putchar(c);
+	putchar('\n');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/sprintf.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,18 @@
+#include <stdarg.h>
+
+extern void _sprintf_putchar();
+
+int
+sprintf(char *strdest, char *fmt, ...)
+{
+	va_list ap;
+	char *strptr;
+	int len;
+
+	strptr = strdest;
+	va_start(ap, fmt);
+	len = _doprnt(fmt, ap, &_sprintf_putchar, &strptr);
+	va_end(ap);
+	*strptr = '\0';
+	return(len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/sprintf_putchar.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,7 @@
+void
+_sprintf_putchar(ch, pp)
+	int ch;
+	char **pp;
+{
+	*(*pp)++ = ch;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/vprintf.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+#include <stdarg.h>
+
+extern void putchar();
+
+int
+vprintf(fmt, ap)
+	char *fmt;
+	va_list ap;
+{
+	return(_doprnt(fmt, ap, &putchar));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libprintf/vsprintf.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,17 @@
+#include <stdarg.h>
+
+extern void _sprintf_putchar();
+
+int
+vsprintf(str, fmt, ap)
+	va_list ap;
+	char *str, *fmt;
+{
+	char *strptr;
+	int len;
+
+	strptr = str;
+	len = _doprnt(fmt, ap, &_sprintf_putchar, &strptr);
+	*strptr = '\0';
+	return(len);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,16 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+AR=	arm-elf-ar
+RANLIB=	arm-elf-ranlib
+
+OBJS=	basicfind.o cmd_find.o findfile.o globals.o init.o rdinmem.o
+
+all:	libtiffs.a
+
+libtiffs.a:	${OBJS}
+	${AR} cru $@ ${OBJS}
+	${RANLIB} $@
+
+clean:
+	rm -f *.[oa] *errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/basicfind.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,65 @@
+#include <sys/types.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "macros.h"
+
+extern char *index();
+
+static
+find_named_child(start, seekname)
+	char *seekname;
+{
+	int ino;
+	struct inode *irec;
+
+	for (ino = start; ino != 0xFFFF; ino = irec->sibling) {
+		irec = mpffs_active_index + ino;
+		if (!irec->type)
+			continue;
+		if (!strcmp(inode_to_dataptr(irec), seekname))
+			return(ino);
+	}
+	return(0);
+}
+
+mpffs_pathname_to_inode(pathname)
+	char *pathname;
+{
+	int ino, stat;
+	struct inode *irec;
+	char *cur, *next;
+
+	stat = mpffs_init();
+	if (stat < 0)
+		return(stat);
+	cur = pathname;
+	if (*cur == '/')
+		cur++;
+	for (ino = mpffs_root_ino; cur; cur = next) {
+		if (!*cur)
+			break;
+		next = index(cur, '/');
+		if (next == cur) {
+		    printf("malformed pathname: multiple adjacent slashes\n");
+			return(-1);
+		}
+		if (next)
+			*next++ = '\0';
+		irec = mpffs_active_index + ino;
+		if (irec->type != OBJTYPE_DIR) {
+			printf("Error: non-terminal non-directory\n");
+			if (next)
+				next[-1] = '/';
+			return(-1);
+		}
+		ino = find_named_child(irec->descend, cur);
+		if (next)
+			next[-1] = '/';
+		if (!ino) {
+			printf("Error: pathname component not found\n");
+			return(-1);
+		}
+	}
+	return(ino);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/cmd_find.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,25 @@
+#include <sys/types.h>
+#include "types.h"
+
+void
+cmd_find(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	int stat, cont;
+	u8 *start;
+	size_t size;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	stat = mpffs_find_file(argv[0], &start, &size, &cont);
+	if (stat < 0)
+		return;
+	printf("chunk @%08X size %x\n", (u32)start, (u32)size);
+	while (cont) {
+		stat = mpffs_get_segment(cont, &start, &size, &cont);
+		if (stat < 0)
+			return;
+		printf("chunk @%08X size %x\n", (u32)start, (u32)size);
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/findfile.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,106 @@
+#include <sys/types.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "macros.h"
+
+static u8 *
+find_endofchunk(ino)
+{
+	struct inode *irec = mpffs_active_index + ino;
+	u8 *p;
+	int i;
+
+	p = inode_to_dataptr(irec) + irec->len;
+	for (i = 0; i < 16; i++) {
+		p--;
+		if (!*p)
+			return(p);
+		if (*p != 0xFF)
+			break;
+	}
+	printf("Error: inode #%x has no valid termination\n", ino);
+	return(p);	/* XXX */
+}
+
+mpffs_find_file(pathname, startret, sizeret, continue_ret)
+	char *pathname;
+	u8 **startret;
+	size_t *sizeret;
+	int *continue_ret;
+{
+	int ino, cont;
+	struct inode *irec;
+	u8 *start, *end;
+	int size;
+
+	ino = mpffs_pathname_to_inode(pathname);
+	if (ino <= 0)
+		return(-1);
+	irec = mpffs_active_index + ino;
+	if (irec->type != OBJTYPE_FILE) {
+		printf("Error: %s is not a regular file\n", pathname);
+		return(-1);
+	}
+	start = inode_to_dataptr(irec);
+	start += strlen(start) + 1;
+	end = find_endofchunk(ino);
+	size = end - start;
+	if (size < 0)
+		size = 0;
+	cont = irec->descend;
+	if (cont == 0xFFFF)
+		cont = 0;
+	if (startret)
+		*startret = start;
+	if (sizeret)
+		*sizeret = size;
+	if (continue_ret)
+		*continue_ret = cont;
+	return(0);
+}
+
+mpffs_get_segment(ino, startret, sizeret, continue_ret)
+	int ino;
+	u8 **startret;
+	size_t *sizeret;
+	int *continue_ret;
+{
+	int cont;
+	struct inode *irec;
+	u8 *start, *end;
+	int size;
+
+	for (;;) {
+		irec = mpffs_active_index + ino;
+		if (irec->type)
+			break;
+		if (irec->sibling == 0xFFFF) {
+		    printf("Error: segment inode #%d: deleted and no sibling\n",
+				ino);
+			return(-1);
+		}
+		ino = irec->sibling;
+	}
+	if (irec->type != OBJTYPE_SEGMENT) {
+		printf("Error: inode #%x is not a segment\n", ino);
+		return(-1);
+	}
+	start = inode_to_dataptr(irec);
+	end = find_endofchunk(ino);
+	size = end - start;
+	if (size <= 0) {
+		printf("Error: segment inode #%x: bad length\n", ino);
+		return(-1);
+	}
+	cont = irec->descend;
+	if (cont == 0xFFFF)
+		cont = 0;
+	if (startret)
+		*startret = start;
+	if (sizeret)
+		*sizeret = size;
+	if (continue_ret)
+		*continue_ret = cont;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/globals.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,8 @@
+/* global variables for the MPFFS reader code */
+
+#include "types.h"
+#include "struct.h"
+
+struct inode *mpffs_active_index;
+int mpffs_root_ino;
+int mpffs_init_done;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/globals.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,9 @@
+/* global variables for the MPFFS reader code - extern declarations */
+
+extern struct inode *mpffs_active_index;
+extern int mpffs_root_ino;
+extern int mpffs_init_done;
+
+extern const u32 mpffs_base_addr;
+extern const u32 mpffs_sector_size;
+extern const int mpffs_nsectors;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/init.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,74 @@
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "macros.h"
+
+static const u8 ffs_sector_signature[6] = {'F', 'f', 's', '#', 0x10, 0x02};
+static const u8 blank_flash_line[16] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+					0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+					0xFF, 0xFF, 0xFF, 0xFF};
+
+static
+find_indexblk()
+{
+	u32 sector_addr;
+	u8 *sector_ptr;
+	int i;
+
+	printf("Looking for MPFFS active index block\n");
+	sector_addr = mpffs_base_addr;
+	for (i = 0; i < mpffs_nsectors; i++) {
+		sector_ptr = (u8 *) sector_addr;
+		if (!bcmp(sector_ptr, ffs_sector_signature, 6) &&
+		    sector_ptr[8] == 0xAB) {
+			printf("Found at %08X\n", sector_addr);
+			mpffs_active_index = (struct inode *) sector_ptr;
+			return(0);
+		}
+		sector_addr += mpffs_sector_size;
+	}
+	printf("Error: Not found in any of the %d candidate sectors\n",
+		mpffs_nsectors);
+	return(-1);
+}
+
+static
+find_rootino()
+{
+	int ino;
+	struct inode *irec;
+
+	printf("Looking for the root inode\n");
+	for (ino = 1; ; ino++) {
+		if (ino >= mpffs_sector_size >> 4) {
+		    printf("Error: Hit end of sector, no root inode found\n");
+			return(-1);
+		}
+		irec = mpffs_active_index + ino;
+		if (!bcmp((u8 *) irec, blank_flash_line, 16)) {
+			printf("Error: Hit blank flash, no root inode found\n");
+			return(-1);
+		}
+		if (irec->type == OBJTYPE_DIR && *inode_to_dataptr(irec) == '/')
+			break;
+	}
+	printf("Found at inode #%x\n", ino);
+	mpffs_root_ino = ino;
+	return(0);
+}
+
+mpffs_init()
+{
+	int stat;
+
+	if (mpffs_init_done)
+		return(0);
+	stat = find_indexblk();
+	if (stat < 0)
+		return(stat);
+	stat = find_rootino();
+	if (stat < 0)
+		return(stat);
+	mpffs_init_done = 1;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/macros.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,1 @@
+#define	inode_to_dataptr(i)	((u8 *)mpffs_base_addr + ((i)->dataptr << 4))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/rdinmem.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,42 @@
+#include <sys/types.h>
+#include "types.h"
+#include "struct.h"
+#include "globals.h"
+#include "macros.h"
+
+mpffs_read_into_ram(pathname, buf, maxlen, lenrtn)
+	char *pathname;
+	u8 *buf;
+	size_t maxlen, *lenrtn;
+{
+	int stat, cont;
+	u8 *chunk_start;
+	size_t chunk_size, real_len, roomleft;
+
+	stat = mpffs_find_file(pathname, &chunk_start, &chunk_size, &cont);
+	if (stat < 0)
+		return(stat);
+	if (chunk_size > maxlen) {
+toobig:		printf("Error: %s is bigger than the read buffer\n", pathname);
+		return(-1);
+	}
+	real_len = chunk_size;
+	bcopy(chunk_start, buf, chunk_size);
+	buf += chunk_size;
+	roomleft = maxlen - chunk_size;
+	while (cont) {
+		stat = mpffs_get_segment(cont, &chunk_start, &chunk_size,
+					 &cont);
+		if (stat < 0)
+			return(stat);
+		if (chunk_size > roomleft)
+			goto toobig;
+		real_len += chunk_size;
+		bcopy(chunk_start, buf, chunk_size);
+		buf += chunk_size;
+		roomleft -= chunk_size;
+	}
+	if (lenrtn)
+		*lenrtn = real_len;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/libtiffs/struct.h	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,25 @@
+struct inode {
+	u16	len;
+	u8	reserved1;
+	u8	type;
+	u16	descend;
+	u16	sibling;
+	u32	dataptr;
+	u16	sequence;
+	u16	updates;
+};
+
+#define	OBJTYPE_FILE	0xF1
+#define	OBJTYPE_DIR	0xF2
+#define	OBJTYPE_SEGMENT	0xF4
+
+struct journal {
+	u8	status;
+	u8	objtype;
+	u16	this_ino;
+	u16	link_ptr;
+	u16	replacee;
+	u32	location;
+	u16	size;
+	u16	repli;	/* ??? */
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/loadagent/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,38 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+LD=	arm-elf-ld
+OBJCOPY=arm-elf-objcopy
+
+INSTDIR=/usr/local/share/freecalypso
+
+PROG=	loadagent
+OBJS=	crt0.o cmdtab.o main.o mygetchar.o
+LIBS=	../libload/libload.a ../libcommon/libcommon.a ../libprintf/libprintf.a \
+	../libbase/libbase.a
+LDS=	../env/iram.lds
+
+TC_LIBS=`${CC} -print-file-name=libc.a` \
+	`${CC} -print-file-name=libgcc.a`
+
+all:	${PROG}.srec
+
+crt0.S:	../env/crt0.S
+	ln -s $< .
+
+${PROG}.elf:	${OBJS} ${LIBS} ${LDS}
+	${LD} -N --defsym Base_addr=0x838000 --defsym stack_bottom=0x83FFFC \
+		-T ${LDS} -o $@ ${OBJS} ${LIBS} \
+		--start-group ${TC_LIBS} --end-group
+
+${PROG}.srec:	${PROG}.elf
+	${OBJCOPY} -O srec --srec-forceS3 --srec-len=30 $< $@
+
+install:
+	mkdir -p ${INSTDIR}
+	install -c ${PROG}.srec ${INSTDIR}
+
+clean:
+	rm -f *.o *errs *core *.elf *.bin *.srec crt0.S
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/loadagent/cmdtab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,52 @@
+#include "cmdtab.h"
+
+extern void cmd_AMFB();
+extern void cmd_AMFW();
+extern void cmd_INFB();
+extern void cmd_INFW();
+extern void cmd_abbr();
+extern void cmd_abbw();
+extern void cmd_blankchk();
+extern void cmd_crc32();
+extern void cmd_jump();
+extern void cmd_r8();
+extern void cmd_r16();
+extern void cmd_r32();
+extern void cmd_w8();
+extern void cmd_w16();
+extern void cmd_w32();
+
+extern void cmd_baud_switch();
+extern void cmd_intel_rewrite_sector();
+extern void cmd_memdump_human();
+extern void cmd_memdump_machine();
+extern void cmd_memload();
+
+extern void abb_init();
+extern void abb_power_off();
+
+const struct cmdtab cmdtab[] = {
+	{"AMFB", cmd_AMFB},
+	{"AMFW", cmd_AMFW},
+	{"DUMP", cmd_memdump_machine},
+	{"INFB", cmd_INFB},
+	{"INFW", cmd_INFW},
+	{"ML", cmd_memload},
+	{"abbinit", abb_init},
+	{"abbr", cmd_abbr},
+	{"abbw", cmd_abbw},
+	{"baud", cmd_baud_switch},
+	{"blankchk", cmd_blankchk},
+	{"crc32", cmd_crc32},
+	{"dump", cmd_memdump_human},
+	{"intel-rewrite-sector", cmd_intel_rewrite_sector},
+	{"jump", cmd_jump},
+	{"poweroff", abb_power_off},
+	{"r8", cmd_r8},
+	{"r16", cmd_r16},
+	{"r32", cmd_r32},
+	{"w8", cmd_w8},
+	{"w16", cmd_w16},
+	{"w32", cmd_w32},
+	{0, 0}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/loadagent/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,15 @@
+/*
+ * FreeCalypso loadagent main() function lives here
+ */
+
+main()
+{
+	uart_select_init();
+	printf("FreeCalypso loadagent running\n");
+	print_boot_rom_info();
+	for (;;) {
+		putchar('=');
+		if (command_entry())
+			command_dispatch();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/loadagent/mygetchar.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+/*
+ * The interactive command entry (editing) function in libcommon
+ * will call mygetchar() for its character input.  It is supposed
+ * to be a blocking wait for input, but in some programs other
+ * processing can be done while waiting - for example, check for
+ * keypad presses as well.  This is the basic version which waits
+ * for serial input and nothing else.
+ */
+
+extern int serial_in_poll();
+
+int
+mygetchar()
+{
+	register int c;
+
+	do
+		c = serial_in_poll();
+	while (c < 0);
+	return c;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,32 @@
+CC=	arm-elf-gcc
+CFLAGS=	-Os -fno-builtin
+CPPFLAGS=-I../include
+LD=	arm-elf-ld
+OBJCOPY=arm-elf-objcopy
+
+PROG=	pirexplore
+OBJS=	crt0.o cmdtab.o ffsparam.o flashid.o lcd.o main.o mygetchar.o rtc.o
+LIBS=	../libtiffs/libtiffs.a ../libcommon/libcommon.a \
+	../libprintf/libprintf.a ../libbase/libbase.a
+LDS=	../env/iram.lds
+
+TC_LIBS=`${CC} -print-file-name=libc.a` \
+	`${CC} -print-file-name=libgcc.a`
+
+all:	${PROG}.srec
+
+crt0.S:	../env/crt0.S
+	ln -s $< .
+
+${PROG}.elf:	${OBJS} ${LIBS} ${LDS}
+	${LD} -N --defsym Base_addr=0x800750 --defsym stack_bottom=0x87FFFC \
+		-T ${LDS} -o $@ ${OBJS} ${LIBS} \
+		--start-group ${TC_LIBS} --end-group
+
+${PROG}.srec:	${PROG}.elf
+	${OBJCOPY} -O srec --srec-forceS3 --srec-len=30 $< $@
+
+clean:
+	rm -f *.o *errs *core *.elf *.bin *.srec crt0.S
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/cmdtab.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,57 @@
+#include "cmdtab.h"
+
+extern void cmd_abbr();
+extern void cmd_abbw();
+extern void cmd_baud_switch();
+extern void cmd_blit();
+extern void cmd_dieid();
+extern void cmd_find();
+extern void cmd_flashid();
+extern void cmd_jump();
+extern void cmd_lcdfill();
+extern void cmd_lcdinit();
+extern void cmd_lcdtest();
+extern void cmd_r8();
+extern void cmd_r16();
+extern void cmd_r32();
+extern void cmd_rtc();
+extern void cmd_rtccomp();
+extern void cmd_spca();
+extern void cmd_spcainit();
+extern void cmd_w8();
+extern void cmd_w16();
+extern void cmd_w32();
+
+extern void abb_init();
+extern void abb_power_off();
+extern void cmd_memdump_human();
+extern void mpffs_init();
+
+const struct cmdtab cmdtab[] = {
+	{"abbinit", abb_init},
+	{"abbr", cmd_abbr},
+	{"abbw", cmd_abbw},
+	{"baud", cmd_baud_switch},
+	{"blit", cmd_blit},
+	{"dieid", cmd_dieid},
+	{"dump", cmd_memdump_human},
+	{"ffsinit", mpffs_init},
+	{"find", cmd_find},
+	{"flashid", cmd_flashid},
+	{"jump", cmd_jump},
+	{"lcdfill", cmd_lcdfill},
+	{"lcdinit", cmd_lcdinit},
+	{"lcdtest", cmd_lcdtest},
+	{"poweroff", abb_power_off},
+	{"r8", cmd_r8},
+	{"r16", cmd_r16},
+	{"r32", cmd_r32},
+	{"rtc", cmd_rtc},
+	{"rtccomp", cmd_rtccomp},
+	{"spca", cmd_spca},
+	{"spcainit", cmd_spcainit},
+	{"w8", cmd_w8},
+	{"w16", cmd_w16},
+	{"w32", cmd_w32},
+	{0, 0}
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/ffsparam.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,11 @@
+/*
+ * This module is linked into pirexplore for the purpose of telling the
+ * somewhat generic libmpffs exactly where the FFS is located on this
+ * device and how it is structured.
+ */
+
+#include "types.h"
+
+const u32 mpffs_base_addr = 0x02000000;
+const u32 mpffs_sector_size = 0x40000;
+const int mpffs_nsectors = 18;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/flashid.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,29 @@
+#include "types.h"
+
+void
+cmd_flashid(argbulk)
+	char *argbulk;
+{
+	char *argv[2];
+	u32 base_addr;
+
+	if (parse_args(argbulk, 1, 1, argv, 0) < 0)
+		return;
+	if (argv[0][0] == '1' && !argv[0][1])
+		base_addr = 0x03000000;
+	else if (argv[0][0] == '2' && !argv[0][1])
+		base_addr = 0x02000000;
+	else {
+		printf("ERROR: argument must be 1 or 2\n");
+		return;
+	}
+	printf("Base addr: %08X\n", base_addr);
+	*(volatile u16 *)(base_addr + 0xAAA) = 0xAA;
+	*(volatile u16 *)(base_addr + 0x554) = 0x55;
+	*(volatile u16 *)(base_addr + 0xAAA) = 0x90;
+	printf("offset 00: %04X\n", *(volatile u16 *)(base_addr + 0x00));
+	printf("offset 02: %04X\n", *(volatile u16 *)(base_addr + 0x02));
+	printf("offset 1C: %04X\n", *(volatile u16 *)(base_addr + 0x1C));
+	printf("offset 1E: %04X\n", *(volatile u16 *)(base_addr + 0x1E));
+	*(volatile u16 *)(base_addr + 0xAAA) = 0xF0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/lcd.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,276 @@
+/*
+ * Almost all of this Pirelli LCD black magic has been lifted from OsmocomBB.
+ */
+
+#include <sys/types.h>
+#include "types.h"
+
+#define	GPIO_OUT_REG	(*(volatile u16 *)0xFFFE4802)
+#define	nCS4_ADDR0	(*(volatile u16 *)0x02800000)
+#define	nCS4_ADDR2	(*(volatile u16 *)0x02800002)
+
+fb_spca_write(addr, data)
+{
+	GPIO_OUT_REG &= 0xFF7F;
+	nCS4_ADDR0 = addr;
+	nCS4_ADDR2 = data;
+}
+
+void
+cmd_spca(argbulk)
+	char *argbulk;
+{
+	char *argv[3];
+	u_long addr, data;
+
+	if (parse_args(argbulk, 2, 2, argv, 0) < 0)
+		return;
+	if (parse_hexarg(argv[0], 4, &addr) < 0) {
+		printf("ERROR: arg1 must be a valid 16-bit hex value\n");
+		return;
+	}
+	if (parse_hexarg(argv[1], 4, &data) < 0) {
+		printf("ERROR: arg2 must be a valid 16-bit hex value\n");
+		return;
+	}
+	fb_spca_write(addr, data);
+}
+
+void
+cmd_spcainit()
+{
+	/*
+	 * Apparently we have to give it a reset pulse, then immediately
+	 * do the black magic register write sequence.
+	 */
+	GPIO_OUT_REG = 0x0000;
+	GPIO_OUT_REG = 0x0012;
+	/* non-understandable voodoo copied from OsmocomBB */
+	fb_spca_write(0x7e, 0x00);	/* internal register access */
+	osmo_delay_ms(10);
+	fb_spca_write(0x7a, 0x00);	/* keep CPU in reset state */
+	osmo_delay_ms(10);
+	fb_spca_write(0x7f, 0x00);	/* select main page */
+	osmo_delay_ms(5);
+	fb_spca_write(0x72, 0x07);	/* don't reshape timing, 16 bit mode */
+	fb_spca_write(0x14, 0x03);
+	fb_spca_write(0x7f, 0x00);	/* select main page */
+	osmo_delay_ms(5);
+	fb_spca_write(0x06, 0xff);
+	fb_spca_write(0x7f, 0x09);
+	fb_spca_write(0x19, 0x08);	/* backlight: 0x08 is on, 0x0c is off */
+	fb_spca_write(0x23, 0x18);
+}
+
+enum s6b33b1x_cmdflag { CMD, DATA, END };
+
+struct s6b33b1x_cmdlist {
+	enum s6b33b1x_cmdflag is_cmd:8;	/* 1: is a command, 0: is data, 2: end marker! */
+	u_char data;			/* 8 bit to send to LC display */
+};
+
+static const struct s6b33b1x_cmdlist
+s6b33b1x_initdata[] = {
+	{ CMD,  0x26 }, /* CMD   DCDC and AMP ON/OFF set */
+	{ DATA, 0x00 }, /* DATA: everything off */
+	{ CMD,  0x02 }, /* CMD   Oscillation Mode Set */
+	{ DATA, 0x00 }, /* DATA: oscillator off */
+	{ CMD,  0x2c }, /* CMD   Standby Mode off */
+	{ CMD,  0x50 }, /* CMD   Display off */
+	{ CMD,  0x02 }, /* CMD   Oscillation Mode Set */
+	{ DATA, 0x01 }, /* DATA: oscillator on */
+	{ CMD,  0x26 }, /* CMD   DCDC and AMP ON/OFF set */
+	{ DATA, 0x01 }, /* DATA: Booster 1 on */
+	{ CMD,  0x26 }, /* CMD   DCDC and AMP ON/OFF set */
+	{ DATA, 0x09 }, /* DATA: Booster 1 on, OP-AMP on */
+	{ CMD,  0x26 }, /* CMD   DCDC and AMP ON/OFF set */
+	{ DATA, 0x0b }, /* DATA: Booster 1 + 2 on, OP-AMP on */
+	{ CMD,  0x26 }, /* CMD   DCDC and AMP ON/OFF set */
+	{ DATA, 0x0f }, /* DATA: Booster 1 + 2 + 3 on, OP-AMP on */
+	{ CMD,  0x20 }, /* CMD   DC-DC Select */
+	{ DATA, 0x01 }, /* DATA: step up x1.5 */
+	{ CMD,  0x24 }, /* CMD   DCDC Clock Division Set */
+	{ DATA, 0x0a }, /* DATA: fPCK = fOSC/6 */
+	{ CMD,  0x2a }, /* CMD   Contrast Control */
+	{ DATA, 0x2d }, /* DATA: default contrast */
+	{ CMD,  0x30 }, /* CMD   Adressing mode set */
+	{ DATA, 0x0b }, /* DATA: 65536 color mode */
+	{ CMD,  0x10 }, /* CMD   Driver output mode set */
+	{ DATA, 0x03 }, /* DATA: Display duty: 1/132 */
+	{ CMD,  0x34 }, /* CMD   N-line inversion set */
+	{ DATA, 0x88 }, /* DATA: inversion on, one frame, every 8 blocks */
+	{ CMD,  0x40 }, /* CMD   Entry mode set */
+	{ DATA, 0x00 }, /* DATA: Y address counter mode */
+	{ CMD,  0x28 }, /* CMD   Temperature Compensation set */
+	{ DATA, 0x01 }, /* DATA: slope -0.05%/degC */
+	{ CMD,  0x32 }, /* CMD   ROW vector mode set */
+	{ DATA, 0x01 }, /* DATA: every 2 subgroup */
+	{ CMD,  0x51 }, /* CMD   Display on */
+	{ END,  0x00 }, /* MARKER: end of list */
+};
+
+static void
+fb_s6b33b1x_send_cmdlist(p)
+	struct s6b33b1x_cmdlist *p;
+{
+	while(p->is_cmd != END) {
+		nCS4_ADDR0 = p->data;
+		p++;
+	}
+}
+
+void
+cmd_lcdinit()
+{
+	GPIO_OUT_REG |= 0x0080;
+	fb_s6b33b1x_send_cmdlist(s6b33b1x_initdata);
+}
+
+set_lcd_addr_region(xstart, xend, ystart, yend)
+{
+	GPIO_OUT_REG |= 0x0080;
+	nCS4_ADDR0 = 0x42;
+	nCS4_ADDR0 = ystart + 4;
+	nCS4_ADDR0 = yend + 4;
+	nCS4_ADDR0 = 0x43;
+	nCS4_ADDR0 = xstart;
+	nCS4_ADDR0 = xend;
+}
+
+void
+cmd_lcdfill(argbulk)
+	char *argbulk;
+{
+	int argc;
+	char *argv[6];
+	u_long pixval;
+	int xstart, xend, ystart, yend;
+	int npix;
+
+	if (parse_args(argbulk, 1, 5, argv, &argc) < 0)
+		return;
+	if (parse_hexarg(argv[0], 4, &pixval) < 0) {
+		printf("ERROR: arg1 must be a valid 16-bit hex value\n");
+		return;
+	}
+	switch (argc) {
+	case 1:
+		xstart = ystart = 0;
+		xend = yend = 127;
+		break;
+	case 5:
+		xstart = atoi(argv[1]);
+		if (xstart < 0 || xstart > 127) {
+range_err:		printf("ERROR: coordinate arg out of range\n");
+			return;
+		}
+		xend = atoi(argv[2]);
+		if (xend < 0 || xend > 127)
+			goto range_err;
+		ystart = atoi(argv[3]);
+		if (ystart < 0 || ystart > 127)
+			goto range_err;
+		yend = atoi(argv[4]);
+		if (yend < 0 || yend > 127)
+			goto range_err;
+		if (xend < xstart || yend < ystart) {
+			printf("ERROR: negative range\n");
+			return;
+		}
+		break;
+	default:
+		printf("ERROR: wrong number of arguments\n");
+		return;
+	}
+	set_lcd_addr_region(xstart, xend, ystart, yend);
+	npix = (xend + 1 - xstart) * (yend + 1 - ystart);
+	while (npix--)
+		nCS4_ADDR2 = pixval;
+}
+
+void
+cmd_lcdtest()
+{
+	int i, j, k, p;
+
+	/*
+	 * The result of this command should be 8 vertical bars
+	 * in the natural RGB order.
+	 */
+	set_lcd_addr_region(10, 89, 10, 89);
+	for (i = 0; i < 80; i++) {
+		for (j = 0; j < 8; j++) {
+			p = 0;
+			if (j & 4)
+				p |= 0xF800;
+			if (j & 2)
+				p |= 0x07E0;
+			if (j & 1)
+				p |= 0x001F;
+			for (k = 0; k < 10; k++)
+				nCS4_ADDR2 = p;
+		}
+	}
+}
+
+void
+cmd_blit(argbulk)
+	char *argbulk;
+{
+	int argc;
+	char *argv[6];
+	u16 imgbuf[16384];
+	size_t img_file_size;
+	int xstart, ystart, width, height;
+	int npix, i;
+
+	if (parse_args(argbulk, 1, 5, argv, &argc) < 0)
+		return;
+	if (mpffs_read_into_ram(argv[0], imgbuf, 32768, &img_file_size) < 0)
+		return;
+	switch (argc) {
+	case 1:
+		xstart = ystart = 0;
+		width = height = 128;
+		break;
+	case 3:
+		xstart = ystart = 0;
+		goto widthheight;
+	case 5:
+		xstart = atoi(argv[3]);
+		if (xstart < 0 || xstart > 127) {
+range_err:		printf("ERROR: coordinate arg out of range\n");
+			return;
+		}
+		ystart = atoi(argv[4]);
+		if (ystart < 0 || ystart > 127)
+			goto range_err;
+		/* FALL THRU */
+	widthheight:
+		width = atoi(argv[1]);
+		if (width < 1 || width > 128)
+			goto range_err;
+		height = atoi(argv[2]);
+		if (height < 1 || height > 128)
+			goto range_err;
+		if (xstart + width > 128)
+			goto range_err;
+		if (ystart + height > 128)
+			goto range_err;
+		break;
+	default:
+		printf("ERROR: wrong number of arguments\n");
+		return;
+	}
+	npix = width * height;
+	if (img_file_size != npix * 2) {
+		printf("ERROR: image file size (%u bytes) does not match WxH\n",
+			img_file_size);
+		return;
+	}
+	set_lcd_addr_region(xstart, xstart + width - 1,
+				ystart, ystart + height - 1);
+	/* the artwork images in Pirelli's FFS appear to be inverted */
+	for (i = 0; i < npix; i++)
+		nCS4_ADDR2 = ~imgbuf[i];
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/main.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,34 @@
+#include "types.h"
+
+main()
+{
+	uart_select_init();
+	printf("Pirelli hardware exploration utility running\n");
+	print_boot_rom_info();
+	/*
+	 * Make the same register settings as in the init script used by
+	 * fc-loadtool and fc-xram: ../../loadtools/scripts/pirelli.init
+	 */
+	*(volatile u16 *)0xfffffb00 = 0x00A4;
+	*(volatile u16 *)0xfffffb02 = 0x00A4;
+	*(volatile u16 *)0xfffffb06 = 0x00A4;
+	*(volatile u16 *)0xfffef006 = 0x0008;
+	/*
+	 * Other register settings replicating what OsmocomBB does
+	 * in board/pirelli_dpl10/init.c
+	 */
+	*(volatile u16 *)0xfffef008 = 0x7090;
+	*(volatile u16 *)0xfffef00a = 0x021F;
+	*(volatile u16 *)0xfffe4804 = 0xFF6D;
+	*(volatile u16 *)0xfffe4802 = 0x0000;
+	/* nCS4 setup for SPCA552E */
+	*(volatile u16 *)0xfffffb0a = 0x00A7;
+	/* initialize PWL registers like OsmocomBB does */
+	*(volatile u8 *)0xfffe8000 = 0x32;
+	*(volatile u8 *)0xfffe8001 = 0x01;
+	for (;;) {
+		putchar('=');
+		if (command_entry())
+			command_dispatch();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/mygetchar.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,21 @@
+/*
+ * The interactive command entry (editing) function in libcommon
+ * will call mygetchar() for its character input.  It is supposed
+ * to be a blocking wait for input, but in some programs other
+ * processing can be done while waiting - for example, check for
+ * keypad presses as well.  This is the basic version which waits
+ * for serial input and nothing else.
+ */
+
+extern int serial_in_poll();
+
+int
+mygetchar()
+{
+	register int c;
+
+	do
+		c = serial_in_poll();
+	while (c < 0);
+	return c;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/pirexplore/rtc.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,59 @@
+#include "types.h"
+#include "rtc.h"
+
+static void
+read_time(tm)
+	struct rtctime *tm;
+{
+	tm->year = RTC_REGS.rtc_cur.year;
+	tm->month = RTC_REGS.rtc_cur.month;
+	tm->day_of_month = RTC_REGS.rtc_cur.day_of_month;
+	tm->day_of_week = RTC_REGS.rtc_cur.day_of_week;
+	tm->hours = RTC_REGS.rtc_cur.hours;
+	tm->minutes = RTC_REGS.rtc_cur.minutes;
+	tm->seconds = RTC_REGS.rtc_cur.seconds;
+}
+
+void
+cmd_rtc()
+{
+	u8 ctrl;
+	struct rtctime time1, time2;
+	int c;
+
+	ctrl = RTC_REGS.rtc_ctrl_reg;
+	printf("RTC_CTRL_REG = %02X ", ctrl);
+	switch (ctrl) {
+	case 0x00:
+		printf("(frozen)\n");
+		break;
+	case 0x01:
+		printf("(running)\n");
+		break;
+	default:
+		printf("(unexpected)\n");
+		return;
+	}
+	printf("Reading RTC time");
+	for (;;) {
+		c = serial_in_poll();
+		if (c >= 0) {
+			printf("<INTERRUPT>\n");
+			return;
+		}
+		read_time(&time1);
+		read_time(&time2);
+		if (!bcmp(&time1.minutes, &time2.minutes, 6))
+			break;
+	}
+	printf("\nDATE %02X-%02X-%02X DOW %02X TIME %02X:%02X:%02X\n",
+		time2.year, time2.month, time2.day_of_month, time2.day_of_week,
+		time2.hours, time2.minutes, time2.seconds);
+}
+
+void
+cmd_rtccomp()
+{
+	printf("%04X\n", (RTC_REGS.rtc_comp_msb_reg << 8) |
+			  RTC_REGS.rtc_comp_lsb_reg);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/tf-breakin/Makefile	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,20 @@
+CC=	arm-elf-gcc
+OBJCOPY=arm-elf-objcopy
+
+all:	payload.o payload.bin embed.c
+
+.SUFFIXES: .o .bin
+
+.o.bin:
+	${OBJCOPY} -O binary $< $@
+
+mkembed:	mkembed.c
+	gcc -O2 -o $@ $@.c
+
+embed.c:	payload.bin mkembed
+	./mkembed payload.bin $@
+
+clean:
+	rm -f *.o *errs *core *.bin mkembed embed.c
+
+FRC:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/tf-breakin/mkembed.c	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,77 @@
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define	PAYLOAD_SIZE	116
+u_char payload_buf[PAYLOAD_SIZE];
+
+read_binary(filename)
+	char *filename;
+{
+	int fd;
+	struct stat st;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		perror(filename);
+		exit(1);
+	}
+	fstat(fd, &st);
+	if (!S_ISREG(st.st_mode)) {
+		fprintf(stderr, "error: %s is not a regular file\n", filename);
+		exit(1);
+	}
+	if (st.st_size != PAYLOAD_SIZE) {
+		fprintf(stderr, "error: %s size mismatch\n", filename);
+		exit(1);
+	}
+	if (read(fd, payload_buf, PAYLOAD_SIZE) != PAYLOAD_SIZE) {
+		perror("read error");
+		exit(1);
+	}
+	close(fd);
+}
+
+write_output(filename)
+	char *filename;
+{
+	FILE *of;
+	int i, j, idx;
+
+	of = fopen(filename, "w");
+	if (!of) {
+		perror(filename);
+		exit(1);
+	}
+	fprintf(of, "u_char shellcode[%d] = {\n", PAYLOAD_SIZE);
+	idx = 0;
+	for (i = 0; i < 15; i++) {
+		for (j = 0; j < 8; j++) {
+			if (j)
+				putc(' ', of);
+			else
+				putc('\t', of);
+			fprintf(of, "0x%02X,", payload_buf[idx++]);
+			if (idx >= PAYLOAD_SIZE)
+				break;
+		}
+		putc('\n', of);
+	}
+	fputs("};\n", of);
+	fclose(of);
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc != 3) {
+		fprintf(stderr, "usage: %s payload.bin output.c\n", argv[0]);
+		exit(1);
+	}
+	read_binary(argv[1]);
+	write_output(argv[2]);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/target-utils/tf-breakin/payload.S	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,43 @@
+	.text
+	.org	0
+
+@ allow entry in Thumb state
+	.code	16
+	bx	pc
+	nop
+
+	.code	32
+
+@ set CPSR like mot931c payload does
+	msr	CPSR_c, #0xd3
+@ disable the watchdog
+	ldr	r1, =0xfffff802
+	mov	r0, #0xf5
+	strh	r0, [r1, #2]
+	mov	r0, #0xa0
+	strh	r0, [r1, #2]
+@ MODEM UART
+	ldr	r6, =0xffff5800
+@ wait for any previous output to flush out
+1:	ldrb	r0, [r6, #5]
+	tst	r0, #0x20
+	beq	1b
+@ send our indication
+	adr	r1, outstr
+	mov	r2, #6
+1:	ldrb	r0, [r1], #1
+	strb	r0, [r6]
+	subs	r2, r2, #1
+	bne	1b
+@ wait for this output to go out to the TxD pin
+1:	ldrb	r0, [r6, #5]
+	tst	r0, #0x40
+	beq	1b
+@ enable the Calypso boot ROM
+	ldr	r1, =0xFFFFFB10
+	mov	r2, #0x0100
+	strh	r2, [r1]
+@ jump to it!
+	mov	pc, #0
+	.ltorg
+outstr:	.byte	2,2,2,'O','K',2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolchain/binutils-patches/elf32-arm.patch	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,62 @@
+# The present patch to the ARM ELF linking code in binutils (made against
+# binutils-2.21.1a) is a hack that affects the generation of Thumb->ARM
+# call veneers.  The binutils linker can make these veneers in two forms:
+#
+#	"Short"			"Long"
+#	(16-bit) bx pc		(16-bit) bx pc
+#	(16-bit) nop		(16-bit) nop
+#	(32-bit) b dest		(32-bit) ldr pc, [pc, #-4]
+#				(32-bit) .long target
+#
+# For code that's going to run on the Calypso ARM7 processor, the
+# "short" form is the one we want: because of the way the address space
+# is laid out, the range of an ARM branch instruction will always be
+# sufficient for us, whereas the long version is more costly in terms
+# of the number of cycles required, especially when executing from
+# external memory.
+#
+# Unfortunately, the code which decides between these two forms
+# (located in the arm_type_of_stub() function in bfd/elf32-arm.c)
+# is broken.  The original code (which you can see below in the
+# "before" part of the patch hunk) exhibits two problems:
+#
+# 1. The distance that matters here is not from the original
+#    call point to the destination, but from the stub to
+#    the destination - and in this function we don't know
+#    where the stub will end up.
+#
+# 2. Because it's an ARM branch, the limits should be
+#    ARM_MAX_[BF]WD_BRANCH_OFFSET, not the THM_ ones.
+#
+# The 2nd problem would be trivial to fix, but the 1st one
+# much less so: this function doesn't know where the stub
+# will end up, and considering that for the present purpose
+# of the FreeCalypso project always emitting the "short"
+# version would be perfect, there is no justification for
+# expending the time and effort to restructure the linker
+# to do the proper relaxation for the case at hand.
+#
+# Therefore, the patch we apply is a hack: we simply force the
+# use of the "short" form unconditionally.
+
+*** elf32-arm.c	Wed May 11 07:29:12 2011
+--- elf32-arm-patched.c	Mon Aug 19 03:24:38 2013
+***************
+*** 3203,3211 ****
+  		   : arm_stub_long_branch_v4t_thumb_arm);
+  
+  	      /* Handle v4t short branches.  */
+! 	      if ((stub_type == arm_stub_long_branch_v4t_thumb_arm)
+! 		  && (branch_offset <= THM_MAX_FWD_BRANCH_OFFSET)
+! 		  && (branch_offset >= THM_MAX_BWD_BRANCH_OFFSET))
+  		stub_type = arm_stub_short_branch_v4t_thumb_arm;
+  	    }
+  	}
+--- 3203,3209 ----
+  		   : arm_stub_long_branch_v4t_thumb_arm);
+  
+  	      /* Handle v4t short branches.  */
+! 	      if (stub_type == arm_stub_long_branch_v4t_thumb_arm)
+  		stub_type = arm_stub_short_branch_v4t_thumb_arm;
+  	    }
+  	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolchain/build+install.sh	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,90 @@
+#!/bin/sh
+#
+# This script builds and installs binutils, gcc and newlib in the order
+# necessary for the whole thing to work.  The present version is based
+# on OsmocomBB's gnu-arm-build.2.sh script, which is in turn based on
+# yet another source.  The present version has been concocted by
+# Spacefalcon the Outlaw for use in the FreeCalypso project.
+#
+# This script needs to be invoked with two arguments:
+#
+# $1: the path to the directory containing the upstream binutils, gcc
+#     and newlib source tarballs;
+#
+# $2: the path to the directory where the built toolchain should be
+#     installed.
+#
+# Note that there isn't a single install step at the end, instead this
+# script will build and install one component, then proceed to build the
+# next component which depends on the previous one, etc.  For this reason
+# you should create a dedicated install directory that is writable by your
+# regular (non-root) uid, then run this script to populate that directory
+# with the toolchain.
+
+if [ $# != 2 ]
+then
+	echo "usage: $0 path-to-tarballs path-to-install"
+	exit 1
+fi
+
+set -ex
+
+target_args="--target=arm-elf"
+
+# binutils come first
+tar xjf $1/binutils-2.21.1a.tar.bz2
+# apply patches
+patch binutils-2.21.1/bfd/elf32-arm.c < binutils-patches/elf32-arm.patch
+mkdir -p binutils-build
+cd binutils-build
+../binutils-2.21.1/configure --prefix=$2 ${target_args} --disable-nls
+make all
+make install
+cd ..
+
+# unpack gcc and newlib sources at the same time: the gcc build
+# will be pointed to newlib headers
+gcc_version=4.5.4
+newlib_version=2.0.0
+tar xjf $1/gcc-core-${gcc_version}.tar.bz2
+tar xzf $1/newlib-${newlib_version}.tar.gz
+
+# patch gcc as needed
+cp t-arm-elf gcc-${gcc_version}/gcc/config/arm
+
+# gcc depends on binutils - add our install destination dir to the PATH
+# we prepend it to avoid surprises if some other arm-elf- toolchain
+# happens to be present already
+PATH=$2/bin:$PATH
+export PATH
+
+mkdir -p gcc-build
+cd gcc-build
+../gcc-${gcc_version}/configure --prefix=$2 ${target_args} \
+	--enable-interwork --enable-multilib \
+	--with-cpu=arm7tdmi --with-float=soft \
+	--enable-languages=c --with-newlib \
+	--with-headers=`pwd`/newlib-${newlib_version}/newlib/libc/include \
+	--with-system-zlib --disable-shared \
+	--disable-nls
+make all-gcc
+make install-gcc
+cd ..
+
+# now we can build newlib
+mkdir newlib-build
+cd newlib-build
+../newlib-${newlib_version}/configure --prefix=$2 ${target_args} \
+	--enable-interwork --enable-multilib \
+	--enable-target-optspace --enable-newlib-reent-small \
+	--disable-newlib-supplied-syscalls \
+	--disable-nls
+make all
+make install
+cd ..
+
+# and finally, libgcc
+cd gcc-build
+make all
+make install
+cd ..
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolchain/clean.sh	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,8 @@
+#!/bin/sh
+set -ex
+rm -rf binutils-2.21.1
+rm -rf binutils-build
+rm -rf gcc-4.5.4
+rm -rf gcc-build
+rm -rf newlib-2.0.0
+rm -rf newlib-build
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toolchain/t-arm-elf	Sat Jun 11 00:13:35 2016 +0000
@@ -0,0 +1,130 @@
+# This is a modified version of the gcc/config/arm/t-arm-elf file
+# from gcc-4.5.4.  It has been modified by Spacefalcon the Outlaw
+# for the FreeCalypso project; the changes are in the multilib
+# configuration:
+#
+# a) The fpu multilib has been commented out
+# b) The -mthumb-interwork multilib has been uncommented
+
+# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+# 2008 Free Software Foundation, Inc.
+#
+# This file is part of GCC.
+#
+# GCC is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# GCC is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# For most CPUs we have an assembly soft-float implementations.
+# However this is not true for ARMv6M.  Here we want to use the soft-fp C
+# implementation.  The soft-fp code is only build for ARMv6M.  This pulls
+# in the asm implementation for other CPUs.
+LIB1ASMFUNCS += _udivsi3 _divsi3 _umodsi3 _modsi3 _dvmd_tls _bb_init_func \
+	_call_via_rX _interwork_call_via_rX \
+	_lshrdi3 _ashrdi3 _ashldi3 \
+	_arm_negdf2 _arm_addsubdf3 _arm_muldivdf3 _arm_cmpdf2 _arm_unorddf2 \
+	_arm_fixdfsi _arm_fixunsdfsi \
+	_arm_truncdfsf2 _arm_negsf2 _arm_addsubsf3 _arm_muldivsf3 \
+	_arm_cmpsf2 _arm_unordsf2 _arm_fixsfsi _arm_fixunssfsi \
+	_arm_floatdidf _arm_floatdisf _arm_floatundidf _arm_floatundisf \
+	_clzsi2 _clzdi2 
+
+MULTILIB_OPTIONS     = marm/mthumb
+MULTILIB_DIRNAMES    = arm thumb
+MULTILIB_EXCEPTIONS  = 
+MULTILIB_MATCHES     =
+
+#MULTILIB_OPTIONS      += march=armv7
+#MULTILIB_DIRNAMES     += thumb2
+#MULTILIB_EXCEPTIONS   += march=armv7* marm/*march=armv7*
+#MULTILIB_MATCHES      += march?armv7=march?armv7-a
+#MULTILIB_MATCHES      += march?armv7=march?armv7-r
+#MULTILIB_MATCHES      += march?armv7=march?armv7-m
+#MULTILIB_MATCHES      += march?armv7=mcpu?cortex-a8
+#MULTILIB_MATCHES      += march?armv7=mcpu?cortex-r4
+#MULTILIB_MATCHES      += march?armv7=mcpu?cortex-m3
+
+# Not quite true.  We can support hard-vfp calling in Thumb2, but how do we
+# express that here?  Also, we really need architecture v5e or later
+# (mcrr etc).
+# MULTILIB_OPTIONS       += mfloat-abi=hard
+# MULTILIB_DIRNAMES      += fpu
+# MULTILIB_EXCEPTIONS    += *mthumb/*mfloat-abi=hard*
+
+# MULTILIB_OPTIONS    += mcpu=ep9312
+# MULTILIB_DIRNAMES   += ep9312
+# MULTILIB_EXCEPTIONS += *mthumb/*mcpu=ep9312*
+# 	
+# MULTILIB_OPTIONS     += mlittle-endian/mbig-endian
+# MULTILIB_DIRNAMES    += le be
+# MULTILIB_MATCHES     += mbig-endian=mbe mlittle-endian=mle
+# 
+# MULTILIB_OPTIONS    += mhard-float/msoft-float
+# MULTILIB_DIRNAMES   += fpu soft
+# MULTILIB_EXCEPTIONS += *mthumb/*mhard-float*
+# 
+MULTILIB_OPTIONS    += mno-thumb-interwork/mthumb-interwork
+MULTILIB_DIRNAMES   += normal interwork
+# 
+# MULTILIB_OPTIONS    += fno-leading-underscore/fleading-underscore
+# MULTILIB_DIRNAMES   += elf under
+# 
+# MULTILIB_OPTIONS    += mcpu=arm7
+# MULTILIB_DIRNAMES   += nofmult
+# MULTILIB_EXCEPTIONS += *mthumb*/*mcpu=arm7*
+# # Note: the multilib_exceptions matches both -mthumb and
+# # -mthumb-interwork
+# #
+# # We have to match all the arm cpu variants which do not have the
+# # multiply instruction and treat them as if the user had specified
+# # -mcpu=arm7.  Note that in the following the ? is interpreted as
+# # an = for the purposes of matching command line options.
+# # FIXME: There ought to be a better way to do this.
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm7d
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm7di
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm70
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm700
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm700i
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm710
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm710c
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm7100
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm7500
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm7500fe
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm6
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm60
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm600
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm610
+# MULTILIB_MATCHES    += mcpu?arm7=mcpu?arm620
+
+EXTRA_MULTILIB_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+# If EXTRA_MULTILIB_PARTS is not defined above then define EXTRA_PARTS here
+# EXTRA_PARTS = crtbegin.o crtend.o crti.o crtn.o
+
+LIBGCC = stmp-multilib
+INSTALL_LIBGCC = install-multilib
+
+# Currently there is a bug somewhere in GCC's alias analysis
+# or scheduling code that is breaking _fpmul_parts in fp-bit.c.
+# Disabling function inlining is a workaround for this problem.
+TARGET_LIBGCC2_CFLAGS = -fno-inline
+
+# Assemble startup files.
+$(T)crti.o: $(srcdir)/config/arm/crti.asm $(GCC_PASSES)
+	$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+	-c -o $(T)crti.o -x assembler-with-cpp $(srcdir)/config/arm/crti.asm
+
+$(T)crtn.o: $(srcdir)/config/arm/crtn.asm $(GCC_PASSES)
+	$(GCC_FOR_TARGET) $(GCC_CFLAGS) $(MULTILIB_CFLAGS) $(INCLUDES) \
+	-c -o $(T)crtn.o -x assembler-with-cpp $(srcdir)/config/arm/crtn.asm
+