view TCS211-fw-arch @ 24:00216b7cfc4d

Quadband-ideas: better explanation of DCS/PCS switching or diplexing mystery
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 09 Nov 2019 23:45:15 +0000
parents f5ddeacbe744
children
line wrap: on
line source

This document describes the architecture of TI's TCS211 firmware and that of
our FreeCalypso Magnetite and Selenite firmwares which are based on it.

What is TCS211, and why we use it as our reference
==================================================

TI were in the business of making GSM baseband chipsets for about a decade
from the late 1990s up until 2009, and over that time span both their silicon
and their firmware architecture had evolved in many different ways.  All of our
work in the FreeCalypso family of projects is based on one fairly arbitrary
snapshot, a rather arbitrarily picked single point in that long evolutionary
line: we use the Calypso chipset as opposed to both the ones before and the
ones after, and we use TI's TCS211 firmware from 2007 as our golden reference,
as opposed to other equally valid ways of architecturing the fw that came
before and after our arbitrarily picked snapshot.

Q: Why do we use the Calypso chipset as opposed to LoCosto or E-Costo or
whatever was TI's very last offering before they got out of that business?

A: Because that's what Openmoko used: their Neo FreeRunner aka GTA02 smartphones
were our primary hardware target for many years before we gathered the money
and the courage to build our own board-level hardware starting from just chips
bought on the Chinese surplus market.

Q: Why do we use TI's TCS211 firmware from 2007 and its architecture as our
golden reference, as opposed to any of the other infinitely many equally valid
ways of architecturing a working firmware implementation for the same Calypso
chipset?

A: Because it works flawlessly, and is extremely stable as a commercial product.
The firmware which Openmoko got from TI had only a tiny difference from TI's
internal TCS211 mainline (TSPACT signal definitions in tpudrv12.h which are
different between the quadband RFFE on TI's internal reference hw and the
triband one in FIC's commercial implementation), and with only a few additional
changes related to our use of a newer flash chip that wasn't supported back in
TI's and Openmoko's days, this golden reference fw can run equally well on our
own FCDEV3B.

Relation between TCS211 and FreeCalypso
=======================================

The only "pure" TCS211 firmware we got is the one that has been salvaged from
the ruins of Openmoko.  To the best of our knowledge, it is the world's only
surviving copy of any version of TCS211 - it is entirely possible that even TI
may not have it any more in any of their archives, given the length of time that
has passed and the total lack of interest in this "ancient junk".  In its pure
form, this world's sole surviving copy of TI's TCS211 fw is laden with blobs
(many components exist only as binary object libraries with no corresponding
source), and it features a build system that is very thoroughly Windows-based.
And to top it off, that configuration and build system has many critical
components which also exist only as compiled binaries (Windows executables or
Java bytecode) with no corresponding source.

We started by replacing the original configuration and build system of TCS211
with our own one that is Unix-based rather than Windows-based, and implemented
in Bourne shell with a few C helpers instead of XML, Java and Perl.  The result
was named FreeCalypso Magnetite.  At first we changed only the configuration
and build system, but kept all of the original TCS211 code, including all of
the binary-only components.  Then we deblobbed it gradually, replacing binary-
only components with source, one component at a time.  Where did we get the
source for the pieces that came as binary objects with no corresponding source?
The answer is different for different components:

* For GSM Layer 1 (a very critical and highly chipset-dependent component), we
  did a painstaking reconstruction which you can see in the tcs211-l1-reconst
  repository.  That world's last surviving copy of TCS211 which we got only had
  *.c files censored out, while all of the original *.h files were preserved -
  and thanks to the preserved configuration and build system, we also got all
  of the original compilation lines including compiler options, -D definitions
  and -I include paths.  For most of the missing *.c files we got a "wrong"
  version from the TCS3/LoCosto source.  The reconstruction proceeded by taking
  these "wrong version" *.c files, putting them one module (one *.c file) at a
  time into the TCS211 build environment, and massaging each individual *.c
  file until it compiled into a perfect match to the original binary object.
  Thus we have reconstructed a full C source for the L1 component which for all
  practical purposes can be treated as if it were the lost original source.

* For some small pieces like the tpudrv12 RF driver and the OSL and OSX
  components of GPF it was more of a translation from disassembly to C: the C
  code we use is of our own writing, but it faithfully matches the logic
  implemented by the original blobs as recovered through disassembly.

* The G23M protocol stack is a very large and complex component, and our copy
  of TCS211 (the world's only surviving copy to the best of our knowledge) has
  it in binary-only form.  Trying to source-reconstruct it precisely like we
  did with L1 would have been infeasible, hence we took a different approach:
  we put together a TCS2/TCS3 hybrid in which we made a wholesale replacement
  of all G23M components: we adopted the new version of G23M wholesale without
  trying to recreate the old version.

* Both TCS211 and TI's newer TCS3.2 fw for the LoCosto chipset are based on
  Nucleus PLUS RTOS (different versions), and both firmwares have their Nucleus
  only as binary object libraries, no source.  However, we got another version
  of Nucleus from about the same time frame (slightly newer than the one TI used
  in TCS211, but slightly older than the one in TCS3.2) from a non-TI source
  (it was posted on a Russian web forum by Comrade XVilka), and in FreeCalypso
  Selenite we use this new Nucleus as a replacement for TCS211 original version
  in the same manner as how we had earlier made a wholesale replacement of the
  G23M protocol stack.

With two major components (Nucleus and the G23M PS) replaced with non-TCS211
versions, our Magnetite hybrid and Selenite firmwares are no longer TCS211, but
they still faithfully follow the _architecture_ of TCS211: in each case when we
replaced the code, we made the new code version fit perfectly into the original
architecture without any disruptive changes.  Thus anyone who desires to
understand our current FreeCalypso firmwares (Magnetite and Selenite) needs to
first understand the original TCS211 architecture, as it is essentially
unchanged.

Why not use the LoCosto chipset and its TCS3.2 firmware?
========================================================

We went the Calypso route and not the LoCosto route because of the circumstances
that surrounded the beginning of our family of projects.  We did not get all of
the tools needed for working with LoCosto chips and TI's TCS3.2 fw (CSST and
SBuild) until the spring of 2015, and by that time we had invested too much into
the Calypso to throw it all away and restart anew in the uncharted waters of
LoCosto.  Another factor is that the software for talking to LoCosto's ROM
bootloader (CSST) exists only as Windows binaries sans source, and it would
require some effort to reverse-engineer the protocol and implement a free and
Unix-based alternative - whereas for the Calypso this work was already done by
OsmocomBB folks before we entered the scene.  Finally, in the case of the
Calypso we have read out the actual content of the ROMs (both the ARM boot ROM
and the DSP ROM) and the ARM boot ROM code has been disassembled and thoroughly
understood - whereas in the case of LoCosto it is not certain if we can even
read out the ROM content, as it is said to be protected against reading.

If someone else desires to play with LoCosto, either by hacking a Peek device
or by building an I-Sample board from the available PADS PCB file, go for it!
But the FreeCalypso core team is sticking with the Calypso chipset for now, and
our actively maintained Magnetite and Selenite firmwares follow the architecture
of TCS211, not that of TCS3.2.

Relation between the ARM and DSP cores in the Calypso
=====================================================

The Calypso digital baseband processor chip has two processor cores in it: an
ARM7TDMI core that runs the main firmware and a C54x DSP core that performs the
more burdensome signal processing tasks.  The DSP is subservient to the ARM:
only the ARM comes out of reset and starts executing code upon power-up, while
the DSP is held in reset (does not run) until and unless the ARM firmware starts
it running.

The ARM core executes code from outside of the Calypso chip itself: in normal
operation (outside of development) there is a flash memory chip connected to
Calypso's external memory bus, and the Calypso's ARM core executes firmware
stored in this flash.  There is an optional (enabled or disabled by a hardware
pin) ARM boot ROM inside the Calypso chip; when this boot ROM is enabled by
nIBOOT pin strapping on the board (like it is on Openmoko and FreeCalypso
hardware), the ARM core executes code from this boot ROM first upon power-up or
reset before jumping to external flash.  The tiny piece of code that is hard-
cast in this mask ROM acts as an unbricking aid: it gives a certain time window
during which the boot process can be interrupted and diverted if certain magic
characters are sent into either of Calypso's two UARTs by an external
development host, and if nothing is received on either UART during that time
window (as would be the case in normal usage of a Calypso phone or modem), the
boot ROM transfers control to the firmware image in the external flash.  The
end result is that the ARM core always runs code from outside of the Calypso
chip itself, either the firmware image in the flash or whatever code is fed by
an external development host to the boot ROM serially over a UART.

There is also an internal RAM inside the Calypso from which the ARM can execute
code (512 KiB on the full Calypso version or 256 KiB on Calypso Lite silicon
used in some historical low-end phones); the primary purpose of this internal
RAM is to allow chosen sections of code to execute faster without the
performance penalty of the external memory bus, but it is volatile RAM, not ROM
or flash, hence it doesn't have any code in it until and unless loaded by the
firmware copying code from flash or via the serial boot protocol.

In contrast, the DSP is very different.  The DSP core can never execute any
code from outside the chip, and has no access to the Calypso chip's external
memory bus at all.  Instead the only two memories accessible to the DSP are a
mask ROM and a fast internal RAM.  The DSP's dedicated mask ROM is 128 Kwords;
the DSP's RAM is 28 Kwords, out of which 8 Kwords constitute the so-called API
RAM which is accessible to both ARM and DSP cores.  (The C54x DSP addresses
memory by words instead of bytes, hence the memory sizes are given in Kwords
instead of KiB.)

The main bulk of the DSP's operating program is already hard-cast in the silicon
in the 128 Kword mask ROM.  The DSP ROM code is structured in such a way that
any part of it can be overridden by downloadable patch codes which get loaded
somewhere in the DSP's 28 Kword RAM, but because the RAM is significantly
smaller than the ROM, downloadable DSP code cannot replace the entirety of the
ROM code - instead the code needs to be patched very selectively only where
necessary to fix a bug that was discovered after the silicon was made or to
extend the DSP functionality with a new feature.

The DSP ROM code in the Calypso silicon we are using has been successfully read
out, but it is only the executable binary code and data - we never found a copy
of the source for this DSP ROM code.  And even if we had this source, we would
not be able to casually modify and recompile it without spending millions of
dollars to fab a new chip revision with a modified mask ROM.  Having this source
would allow us to develop our own DSP patch codes and to understand and maintain
the existing ones, hence we need to make an effort to convince TI to release
the source for the DSP ROM if they have it in their archives, but if no
surviving copy of this source exists anywhere in the world, the fallback plan
would be to reverse-engineer the DSP ROM code by disassembly.  The latter plan
has not been pursued yet because of the very high labor cost it would involve.

It is possible to run the Calypso DSP without any patches, i.e., have it run
only the code that is already in the mask ROM.  Our competitor OsmocomBB
operates in this manner, and we have also built and run modified versions of
our TCS211-based FreeCalypso firmware with DSP patch loading disabled as an
experiment.  However, all ARM-side firmwares that have been officially released
by TI for production use including our TCS211-20070608 golden reference do apply
downloadable patches to the DSP, and are designed to run with this patched DSP;
running them with DSP patching disabled results in unstable operation.

DSP patch codes that are included in ARM-side Calypso firmwares take the form
of const char arrays initialized with hex bytes; these C source files with hex
char arrays inside were apparently produced from C54x COFF files with a tool
called coff2c, but we never got any of those COFF files or whatever source (C
or assembly) they were built from.  At the present time in the FreeCalypso
family of projects we use the DSP patch codes (hex char arrays) which we got
with our copy of TCS211 from 20070608, and we treat the entire DSP block (the
combination of mask ROM plus patches) as a functional black box.

Having to treat the DSP as a black box is certainly a major shortcoming of our
FreeCalypso solution.  However, I (Mother Mychaela) would much rather have a
phone or modem in which only the DSP is a black box while I get to maintain all
of the upper layers with full freedom, as opposed to the status quo alternative
of a very high-level black box with FOTA backdoors.  Unlike the ubiquitous
high-level black boxes from the likes of Qualcomm, the DSP in the Calypso cannot
be backdoored: it has no access to the ARM address space, thus no access to the
flash (cannot surreptitiously modify the firmware) and no access to any of the
higher-level radio protocol state maintained by the ARM, all it can do is
modulate and demodulate bursts and run voice codecs _as commanded by the ARM_.
Furthermore, the DSP has no access to the Calypso chip's TPU (Time Processing
Unit, the block that controls board-level RF hardware) and thus has no direct
control over any of the RF hardware: it cannot initiate radio transmission or
even reception on its own, instead the ARM firmware has to configure the RF
hardware via the TPU for each and every Rx or Tx time window.

Finally, if anyone is truly paranoid about the possibility of backdoors in the
DSP, the DSP ROM code has been read out - you are welcome to hire a professional
reverser of your choice to disassemble and audit it as thoroughly as you like.
This code is unchangeable by virtue of being hard-cast in a mask ROM in the
silicon.

The rest of this document covers the firmware that runs on the ARM core; it
controls the DSP via its API RAM, a form of shared memory interface.

High-level structure of TCS211 firmware
=======================================

The code base that makes up TI's TCS211 firmware consists of 3 main divisions:
chipset software, Condat G23M (GSM and GPRS L23 protocol stacks, ACI and
optional handset UI layers) and GPF.  Let us look at them in turn:

chipsetsw division
------------------

In the original TCS211 delivery there was a top-level directory named chipsetsw
(chipset software), containing code that is specific to TI's chipsets in
particular and was never intended to run on any other hardware.  This code
division has been retained intact in our FreeCalypso Magnetite and Selenite
firmwares, taken in its entirety from our TCS211 golden reference, although we
have shortened the name: this code division now resides under src/cs in
Magnetite and Selenite.  Aside from a few bits of system glue, this chipsetsw
breaks down into two further subdivisions: the L1+drivers core and the SSA
division.

L1+drivers core
---------------

This division resides under chipsetsw/layer1 and chipsetsw/drivers/drv_core, or
under src/cs/layer1 and src/cs/drivers/drv_core in our version.  The most
important piece here is L1 (GSM Layer 1): this code drives the DSP and the RF
hardware, and thereby makes the Calypso function as a GSM MS (mobile station)
and not merely as a general purpose microprocessor platform.  This code can be
considered to be the most important part of the entire firmware.

At one time TI had a so-called standalone L1 configuration, selected by the
OP_L1_STANDALONE C preprocessor symbol.  We don't have the bits that are needed
to build this configuration (they were probably never released outside of TI at
all), but it appears that this fw build configuration consisted of just Nucleus,
L1, the drivers under drv_core, the OSL and OSX parts of GPF without the rest,
and some stubs for the few higher-level functions that are intertied with L1.

The drivers under chipsetsw/drivers are divided into drv_core and drv_app: the
former are the most essential or fundamental ones, used by L1 and/or needed for
the OP_L1_STANDALONE config; the latter belong to the higher-level SSA division
described below.

SSA division
------------

TI had a group called System Software and Applications (SSA), and they supplied
those parts of the firmware that are neither L1+drv_core nor Condat G23M.  The
more interesting pieces here include the flash file system (FFS), the debug
trace facility (RVT), the Enhanced Test Mode (ETM) facility that allows
external development and production tools to poke at the firmware, RiViera Audio
Service (playing various beeps and ringtones through the DSP, a front-end to L1
audio functions), LCD and keypad drivers for Calypso-based handsets, and various
supportive functions implemented via the Iota ABB: switch-on and switch-off
logic, battery monitoring and charging, backlight LED control.

All firmware components in the SSA division are built on top of a framework
called RiViera - more will be said about it later.  Everything under
chipsetsw/drivers/drv_app, chipsetsw/riviera and chipsetsw/services (or under
src/cs/drivers/drv_app, src/cs/riviera and src/cs/services in our version)
belongs to the SSA realm.

Condat G23M division
--------------------

At the beginning of TI's involvement in the GSM baseband chipset business, they
only developed and maintained their own L1 code, which eventually grew into the
larger chipsetsw division described above, while the rest of the protocol stack
(which is hardware-independent) was licensed from another company called Condat.
Later Condat as a company was fully acquired by TI, and the once-customer of
this code became its owner.  The name of TI/Condat's implementation of GSM
layers 2&3 for the MS side is G23M, and it forms its own major division of the
overall fw architecture.

The overall Condat code realm can be further subdivided into GSM and GPRS L23
protocol stacks, the Application Control Interface (ACI) which includes the AT
command interpreter (ATI), and additional phone UI layers which are only
included in handset but not modem firmwares.

We don't know exactly how TI maintained this software internally: given that it
is mostly hardware-independent aside from integration details and some minor
features which may be present on one hw platform but not on another, it would
have made the most sense for TI to maintain a single internal mainline common
to both Calypso and LoCosto, and then integrate the code from this mainline into
chipset-specific customer releases.  We have no way of knowing if TI indeed
followed this approach or not, but when we took the version of G23M from the
TCS3.2 source for the LoCosto chipset and grafted it onto the chipsetsw
foundation from TCS211 for the Calypso to produce our TCS2/TCS3 hybrid, the
integration went surprisingly smoothly.  The full-source version of G23M which
we took from TCS3/LoCosto is newer than the binary-only version featured in the
world's last surviving copy of TCS211 from Openmoko.

GPF island of stability
-----------------------

Underlying the G23M protocol stack is a special layer called GPF, which was
originally Condat's Generic Protocol stack Framework.  Apparently Condat were
in the business of developing and maintaining a whole bunch of protocol stacks:
GSM MS side, GSM network side, TETRA and who knows what else.  GPF was their
common underpinning for all of their protocol stack projects, which ran on top
of many different OS environments: Nucleus, pSOS, VxWorks, Unix/Linux, Win32
and who knows what else.

In the case of TI/FreeCalypso GSM fw, both the protocol stack and the underlying
OS environment are fixed: GSM and Nucleus, respectively.  But GPF is still a
critically important layer in the firmware architecture: in addition to serving
as the glue between the G23M stack and Nucleus, it provides some important
support infrastructure for the protocol stack.

However, what makes GPF very special is the way in which it relates to the rest
of the firmware architecture.  GPF remained common and unchanged across TI's
many different projects, and it is so independent from the rest of the firmware
and its build configuration that TI were able to make company-wide GPF library
builds and then plop them into multiple fw projects which used them as
configuration-independent prebuilt libraries.  All TI firmware (semi-)sources
we've got use GPF in prebuilt library form and are not set up to recompile any
part of it from source.

Our FC Magnetite firmware uses the original binary libs from TCS211-Openmoko
for its GPF component, but for FC Selenite the project requirement is to be
completely blob-free, hence we had to reconstruct the source for GPF.  The
original source for most parts of GPF was found between TCS3.2 from Peek/FGW
and TCS211 from OM (the former had the source for the core "frame" modules and
the latter had the source for misc and tst), but we never got the source for the
OSL and OSX components, hence we had to reconstruct them from disassembly.  OSL
is the glue layer between GPF and Nucleus, OSX is the glue layer between GPF
and L1.

Firmware boot process
=====================

As already mentioned earlier, the Calypso chip itself includes an ARM boot ROM
in the silicon that serves as an unbricking aid: it provides a certain time
window during which the boot process can be interrupted and diverted if certain
magic characters are sent into either of Calypso's two UARTs by an external
host, and if nothing is received on either UART during that time window, the
boot ROM transfers control to the firmware image in the external flash.  As we
understand it, Calypso was TI's first DBB (digital baseband processor) chip to
include this boot ROM, and their previous DBB chips did not have such: they
would always execute code directly from external flash immediately out of reset.

TI's TCS211 and earlier firmwares are structured in such a way that they boot
and run exactly the same way whether the Calypso boot ROM is present and
enabled, present but disabled, or not present at all.  They put magic constant
0x00000001 in the 32-bit word at flash address 0x2000, which tells the Calypso
boot ROM (if it is present and enabled) to boot the flash fw image in legacy
mode: after providing the unbricking time window, the boot ROM moves itself out
of the way (sets two bits in the FFFF:FB10 register which tell the chip to unmap
the boot ROM and to map external memory at address 0) and induces a watchdog
reset, causing the chip to re-execute the reset vector, this time directly out
of external flash - thus the firmware boots as if the boot ROM weren't there,
but the ROM's unbricking function is retained.

In order to make it easier to load new firmware images during development on
pre-Calypso platforms which didn't have a boot ROM, TI had developed a flash-
resident bootloader stage and included it in their fw architecture.  This
bootloader stage is placed at the beginning of the flash at the reset vector,
and the rest of the firmware begins at an erase unit boundary.  The bootloader
stage executes first, and before it jumps to the main firmware entry point
(_INT_Initialize) for normal boot, it offers an opportunity for the boot process
to be interrupted and diverted if an external host sends certain magic command
packets into either of the two UARTs during the allotted time window.  If the
external host does interrupt and divert the boot process in this manner, it can
feed a code image to the bootloader to be written somewhere in target RAM, and
then command the bootloader to jump to it.  It is exactly the same functionality
(though with different serial protocol specifics) as implemented in the Calypso
boot ROM.  The ROM version is obviously superior because it is unbrickable, but
the flash-resident, built-with-firmware version is what TI used before they
came up with the idea of the boot ROM for the Calypso.

When the boot-ROM-equipped Calypso came along, TI kept the flash-resident
bootloader in the firmware: it does no harm aside from adding a little bit of
delay to the boot process, it does not conflict with the ROM bootloader as the
two speak different serial protocols and respond to different interrupt-boot
sequences, and it allowed TI to keep the same firmware architecture for
platforms with and without a boot ROM.  However, in our FreeCalypso firmwares
starting with Magnetite we have removed this extra bootloader stage for the
following reasons:

* It is not useful to us on any of our hardware targets: on those devices that
  have the Calypso boot ROM enabled, we use that boot ROM and get full
  unbrickability, whereas on Mot C1xx phones we have to work with Mot/Compal's
  own different bootloader and serial protocol at least initially, hence it
  makes the most sense to stick with the same after the conversion to
  FreeCalypso as well.

* As delivered by TI with their full production TCS211 fw releases, their
  firmware-resident bootloader works as intended only on hw platforms with
  13 MHz VCXOs like the original D-Sample (Clara RF), and is broken on platforms
  like Rita RF (the only RF chip for which we have driver code!) with 26 MHz
  VCXOs: there is no conditionally-compiled code anywhere in the bootloader
  code path to set the VCLKOUT_DIV2 bit in the CNTL_CLK register on 26 MHz
  platforms, thus the UARTs are fed with 26 MHz instead of the standard 13 MHz
  clock expected in normal operation, and the intended baud rate of 115200 bps
  turns into 230400.  Because 230400 bps is a baud rate which Calypso UARTs
  *cannot* produce in normal GSM operation (when the peripheral clock network
  runs at the expected 13 MHz), tools that are designed to talk to Calypso GSM
  devices are typically not designed to support this baud rate.  In particular
  for CP2102 USB-serial adapters, the precedent established by the factory
  CP2102 EEPROM programming in the Pirelli DP-L10 phone is that the baud rate
  entry for 230400 bps is replaced with 203125 bps, which is a valid baud rate
  for Calypso UARTs running at 13 MHz.

* We have no source for TI's firmware-resident bootloader, only linkable binary
  objects that came with our world's last surviving copy of TCS211, which are
  incompatible with our goal of blob-free firmware.

Because this extra bootloader stage is ultimately unnecessary in our
environment, the deblobbing goal was easier accomplished by removing it
altogether instead of expending effort on a blob-free replacement.  Because I
wasn't comfortable with modifying TMS470 assembly code and linker script magic,
the removal of the bootloader was accomplished by stubbing out its C body with
an empty function.  In the gcc-built FC Selenite version it is removed
completely, without any leftover stubs.

Finally, it needs to be noted for the sake of completeness that Compal's
bootloader used on Mot C1xx phones is a modified version based on TI's original
bootloader.  However, this factoid matters only for historians and genealogists;
for all practical purposes it is an unrelated animal, as Mot/Compal's serial
protocol for interrupting and diverting the boot process is their own and bears
no resemblance to TI's version.  And yes, Mot/Compal's version does set the
VCLKOUT_DIV2 bit in the CNTL_CLK register to adjust for the 26 MHz clock input
as its first order of business; it was probably the very first issue they had
to fix.

When we build FC Magnetite or FC Selenite TMS470 firmware for Mot C1xx targets,
we use dd to strip off the first 64 KiB of the image produced by TI's linker
(the part where TI's bootloader resides, be it intact or stubbed out) and flash
the remaining image (the main body of the fw) starting at flash address 0x10000.
In the gcc-built Selenite version we natively link images that are designed to
be flashed at 0x10000 without any dirty hacks.  Common to all FC firmwares for
C1xx targets, the bootloader image we put at 0 (in the brickable flash sector)
is a modified version based on one of Mot/Compal's originals: we have binary-
patched it to redirect the exception vectors from Mot/Compal's 0x20A0 to 0x10000
and to move the main fw entry point from Mot/Compal's 0x20F8 to TI's 0x10058.

None of this muckery applies to our own FreeCalypso hardware or to our
predecessor Openmoko's hw: on these good hw targets the complete fw image as
built is flashed at 0, and there is no possibility of bricking because we use
the boot ROM to gain access irrespective of what's in the flash.

Main firmware entry point
-------------------------

With the bootloader distraction out of the way, the main fw entry point is at
the _INT_Initialize symbol in the int.s assembly module, located in
src/cs/system/main/int.s in Magnetite and Selenite.  The functional equivalent
for the gcc environment in Selenite is in src/cs/system/main/gcc/bootentry.S.
This assembly code performs some basic hardware initialization, sets up
sensible memory timings for the boot path phase before DPLL setup, copies the
IRAM code (the code that is intended to execute out of the fast internal RAM)
from flash to where it needs to be, zeros both IRAM and XRAM .bss regions, does
TI's cinit/auto_init business for initialized data in the TMS470 environment
(Selenite gcc version copies .data from flash to RAM instead), sets up the
system, IRQ, FIQ and exception stacks, does some assembly initialization for
Nucleus and finally jumps to Nucleus' C entry point INC_Initialize().

Further initialization takes place in the Init_Target() and Init_Drivers()
functions called from Application_Initialize(), which is the last function
called by INC_Initialize() before starting the Nucleus task scheduler.

Nucleus environment
===================

Like all classic TI firmwares, ours is based on the Nucleus PLUS RTOS.  Just
like TI's original code on which we are based, we use only a small subset of
the functionality provided by Nucleus - but because the latter is a library,
the pieces we don't use simply don't get pulled into the link.  The main
function we get out of Nucleus is the scheduling of threads, or tasks as
Nucleus calls them.

Aside from pre-stack-setup assembly init code and ARM exception handlers, every
piece of code in the firmware executes in one of the following contexts:

* Application_Initialize(): this function and everything called from it execute
  just before Nucleus' thread scheduler starts; at this point interrupts are
  disabled at the ARM7 core level (in the CPSR) and must not be enabled; the
  stack is Nucleus' "system stack" which is also used by the scheduler and LISRs
  as explained below.

* Regular threads or tasks: once Application_Initialize() finishes, all code
  with the exception of interrupt handlers (LISRs and HISRs as explained below)
  runs in the context of some Nucleus task.  Whenever you are trying to debug
  or simply understand some piece of code in the firmware, the first question
  you should ask is "which task does this code execute in?".  Most functional
  components run in their own tasks, i.e., a given piece of code is only
  intended to run within the Nucleus task that belongs to the component in
  question.  On the other hand, some components are implemented as APIs,
  functions to be called from other components: these don't have their own task
  associated with them, and instead they run in the context of whatever task
  they were called from.  Some only get called from one task: for example, the
  "uartfax" driver API calls only get called from the protocol stack's UART
  entity, which is its own task.  Other component API functions like FFS and
  trace can get called from just about any task in the system.  Many components
  have both their own task and some API functions to be called from other tasks,
  and the API functions oftentimes post messages to the task to be worked on by
  the latter; the just-mentioned FFS and trace functions work in this manner.

  In our TCS211-mimicking Magnetite and Selenite firmwares every Nucleus task is
  created either through RiViera or through GPF, and not in any other way - see
  the description of RiViera and GPF below.

* LISRs (Low level Interrupt Service Routines): these are the interrupt handlers
  that run immediately when an ARM IRQ or FIQ comes in.  The code at the IRQ and
  FIQ vector entry points calls Nucleus' magic stack switching function
  (switches the CPU from IRQ/FIQ into SVC mode, saves the interrupted thread's
  registers on that thread's stack, and switches to the "system" stack) and
  then calls TI's IRQ dispatcher implemented in C.  The latter figures out
  which Calypso interrupt needs to be handled and calls the handler configured
  in the compiled-in table.  Nucleus' LISR registration framework is not used
  by the GSM fw, but these interrupt handlers should be viewed as LISRs
  nonetheless.

  There is one additional difference between canonical Nucleus and TI's version
  (we've replicated the latter): canonical Nucleus was designed to support
  nested LISRs, i.e., IRQs re-enabled in the magic stack switching function,
  but in TI's version which we follow this IRQ re-enabling is removed: each LISR
  runs with interrupts disabled and cannot be interrupted.  (The corner case of
  an FIQ interruping an IRQ remains to be looked at more closely as bugs may be
  hiding there, but Calypso doesn't really use FIQ interrupts.)  There is really
  no need for LISR nesting in our GSM fw, as each LISR is very short: most LISRs
  do nothing more than trigger the corresponding HISR.

* HISRs (High level Interrupt Service Routines): these hold an intermediate
  place between LISRs and tasks, similar to softirqs in the Linux kernel.  A
  HISR can be activated by a LISR calling NU_Activate_HISR(), and when the LISR
  returns, the HISR will run before the interrupted task (or some higher
  priority task, see below) can resume.  HISRs run with CPU interrupts enabled,
  thus more interrupts can occur, with their LISRs executing and possibly
  triggering other HISRs.  All triggered HISRs must complete and thereby go
  "quiescent" before task scheduling resumes, i.e., all HISRs as a group have a
  higher scheduling priority than tasks.

Nucleus implements priority scheduling for tasks.  Tasks have their priority set
when they are created (through RiViera or GPF, see below), and a higher priority
task will run until it gets blocked waiting for something, at which time lower
priority tasks will run.  If a lower priority task sends a message to a higher
priority task, unblocking the latter which was waiting for incoming messages,
the lower priority task will effectively suspend itself immediately while the
higher priority task runs to process the message it was sent.

HISRs oftentimes post messages to their associated tasks as well; if one of
these messages unblocks a higher priority task, that unblocked task will run
upon the completion of the HISR instead of the original lower priority task
that was interrupted by the LISR that triggered the HISR.  Nucleus' scheduler
is fun!

RiViera and GPF
===============

RiViera and GPF are two parallel/independent/competing wrappers around or
layers above Nucleus.  GPF comes from Condat and is used by the G23M protocol
stack and indirectly by L1 (the peculiar way in which L1 ties in with the rest
of the firmware will be covered later), whereas RiViera is used by the fw
components from TI's SSA group: flash file system, debug trace, RiViera Audio
Service and so forth.

At some point in their post-Calypso TCS3.x program TI decided to eliminate
RiViera as an independent framework and to reimplement RiViera APIs (used by
peripheral but necessary code such as FFS, ETM, various drivers etc) over GPF.
This arrangement is used in the TCS3.2 LoCosto firmware from which we have
lifted our source replacements for much of the code that came as binary objects
in our reference TCS211 version.  However, our current Magnetite and Selenite
firmwares follow the architecture of TCS211, not that of TCS3.2, and because
the entire SSA division of the fw including the RiViera core came in full source
form in our copy of TCS211, it was only natural to keep this code and its
architecture.

Start-up process continued
==========================

As mentioned earlier, Nucleus calls the application's software init function
called Application_Initialize() after it initializes itself but before starting
the task scheduler.  This function in TCS211 is just the following:

Application_Initialize()
{
	Init_Target();
	Init_Drivers();
	Cust_Init_Layer1();
	Init_Serial_Flows();
	StartFrame();
	Init_Unmask_IT();
}

Cust_Init_Layer1() is in L1, StartFrame() is in GPF, and the remaining 4 init
functions live in the init.c module under src/cs/system/main.

The Init_Target() function finishes the hardware initialization that was
started by the assembly code at the firmware boot entry point (int.s): among
other things, it sets up the final memory timings that will be used by the
running fw and configures the Calypso DPLL which provides multiplied internal
clocks to both ARM and DSP cores.  On Calypso C035 silicon which is used on our
own FreeCalypso boards and on most of our pre-existing hw targets the DPLL and
the DSP run at 104 MHz and the ARM gets half of that, running at 52 MHz.
Init_Target() also calls AI_InitIOConfig(), the function that initializes
Calypso GPIO directions and initial outputs; both of these functions typically
need to be tweaked when adding support for a new Calypso board target.

The Init_Drivers() function is primarily responsible for initializing RiViera
and FFS, although it also does a bit of init related to ABB and SIM drivers.

I mentioned earlier that every Nucleus task in our firmware gets created and
started either through RiViera or through GPF.  All GPF tasks are created and
placed into the runable state in the Application_Initialize() context: the work
is done by GPF init code in gpf/frame/frame.c, and the top level GPF init
function called from Application_Initialize() is StartFrame().  Thus when
Application_Initialize() finishes and the Nucleus thread scheduler starts
running for the first time, all GPF tasks are there to be scheduled.

There is a compiled-in table of all protocol stack entities and the tasks in
which they need to run; in TCS211 these GPF config bits live under
g23m/condat/frame/config for the GSM+GPRS configuration and under
g23m/condat/com/src/config for the GSM-only config without GPRS.  Canonically
each protocol stack entity runs in its own task, but sometimes two or more are
combined to run in the same task: for example, in the minimal GSM "voice only"
configuration (no CSD, fax or GPRS) CC, SMS and SS entities share the same task
named CM.  Unlike RiViera, GPF does not support dynamic starting and stopping
of tasks.

As each GPF task starts running (immediately upon entry into Nucleus' scheduling
loop as Application_Initialize() finishes), pf_TaskEntry() function in
gpf/frame/frame.c is the first code it runs.  This function creates the queue
for messages to be sent to all entities running within the task in question,
calls each entity's pei_init() function (repeatedly until it succeeds: it will
fail until the other entities to which this entity needs to send messages have
created their message queues), and then falls into the main body of the task:
for all "regular" entities/tasks except L1, this main body consists of waiting
for messages (or signals or timeouts) to arrive on the queue and dispatching
each received message to the appropriate handler in the right entity.

RiViera tasks get started in a different way.  The responsible code lives in
src/cs/system/main/create_RVtasks.c, and the create_tasks() function found in
that module is called by Init_Drivers() in the Application_Initialize() context.
But this function does not directly create and start every configured RiViera
task like StartFrame() does for GPF.  Instead it creates a special helper task
which will do this work once scheduled.  Thus at the completion of
Application_Initialize() and the beginning of scheduling the set of runable
Nucleus tasks consists of all GPF ones plus the special RV starter task.  Once
the RV starter task gets scheduled, it will call rvm_start_swe() to launch
every configured RiViera SWE (SoftWare Entity), which in turns entails creating
the tasks in which these SWEs are to run.

Dynamic memory allocation
=========================

All dynamic memory allocation (i.e., all RAM usage beyond statically allocated
variables and buffers) is once again done either through RiViera or through GPF,
and in no other way.  Ultimately all areas of the physical RAM that will ever
be used by the fw in any way are allocated when the fw is compiled and linked:
the areas from which RiViera and GPF serve their dynamic memory allocations are
statically allocated as char arrays in the respective C modules and placed in
the appropriate IRAM or XRAM .bss section by the linker script; RiViera and GPF
then provide API functions that allocate memory dynamically from these
statically allocated large pools.

RiViera and GPF have entirely separate memory pools from which they serve their
respective clients, hence there is no possibility of one affecting the other.
Riviera's memory allocation scheme is very much like the classic malloc&free:
there is one large unstructured pool from which all allocations are made, one
can allocate a chunk of any size, free chunks are merged when physically
adjacent, and fragmentation is an issue: a memory allocation request may fail
even when there is enough memory available in total if it is too fragmented.

GPF's dynamic memory allocation facility is considerably more robust: while it
does maintain one or two (depending on configuration) memory pools of the
traditional "dynamic" kind (like malloc&free, susceptible to fragmentation),
most GPF memory allocation works on "partition" memory instead.  Here GPF
maintains 3 separate groups of pools: PRIM, TEST and DMEM; each allocation
request must specify the appropriate pool group and cannot affect the others.
Within each pool there is a fixed number of partitions of a fixed size: for
example, in TI's TCS211 GSM+GPRS configuration the PRIM pool group consists of
190 partitions of 60 bytes, 110 partitions of 128 bytes, 50 partitions of 632
bytes and 7 partitions of 1600 bytes.  An allocation request from a given pool
group (e.g., PRIM) can request any arbitrary size in bytes, but it gets rounded
up to the nearest partition size and allocated out of the respective pool.  If
no free partition is available, the requesting task is suspended until another
task frees one.  Because these partitions are used primarily for intertask
communication, if none are free, it can only mean (assuming that the firmware
functions correctly) that all partitions have been allocated and sent to some
queue for some task to work on, hence eventually they will get freed.

This scheme implemented in GPF is extremely robust in the opinion of this
author, and the other purely "dynamic" scheme is used (in the case of GPF) only
for init-time allocations which are never freed, such as task stacks - hence
the GPF-based part of the firmware is not suspectible at all to the problem of
memory fragmentation.  But Riviera does suffer from this problem, and the
concern is more than just theoretical: one major user of Riviera-based dynamic
memory allocation is the trace facility (described in its own section below),
and my observation of the trace output from Pirelli's proprietary fw (which
appears to use the same architecture with separate Riviera and GPF) suggests
that after the fw has been running for a while, Riviera memory gets fragmented
to a point where many traces are being dropped.  Replacing Riviera's poor
dynamic memory allocation scheme with a GPF-like partition-based one is a to-do
item for our project.

Message-based intertask communication
=====================================

Even though all entities of the G23M protocol stack are linked together into
one monolithic fw image and there is nothing to stop them from calling each
other's functions and accessing each other's variables, they don't work that
way.  Instead all communication between entities is done through messages, just
as if they ran in separate address spaces or even on separate processors.
Buffers for this message exchange are allocated from a GPF partition pool: an
entity that needs to send a message to another entity allocates a buffer of the
needed size, fills it with the message to be sent, and posts it on the recipient
entity's message queue, all through GPF services.  The other entity simply
processes the stream of messages that arrives on its message queue, freeing each
message (returning the buffer to the partition pool it came from) as it is
processed.

Riviera-based tasks use a similar mechanism: unlike G23M protocol stack
entities, most Riviera-based functional modules provide APIs that are called as
functions from other tasks, but these API functions typically allocate a memory
buffer (through Riviera), fill it with the call parameters, and post it to the
associated task's message queue (also in the Riviera land) to be worked on.
Once the worker task gets the job done, it will either call a callback function
or post a response message back to the requestor - the latter option is only
possible if the requesting entity is also Riviera-based.

A closer look at GPF
====================

There are certain sublayers within GPF which need to be pointed out.  The 3
major subdivisions within GPF are:

* The meaty core of GPF: this part is the code under src/gpf/frame in our
  Selenite GPF reconstruction, originating from gpf/FRAME in the TCS3.2 source
  from Peek/FGW.  It appears that this part was originally intended to be both
  project-independent (same for GSM, TETRA etc) and OS-independent (same for
  Nucleus, pSOS, VxWorks etc).  This is the part of GPF that matters for the
  G23M stack: all APIs called by PS entities are implemented here, and so are
  all other PS-facing functions such as startup.  (PS = protocol stack)

* OS adaptation layer (OSL): this is the part of GPF that adapts it to a given
  underlying OS, in our case Nucleus.

* Test interface: see the code under gpf/tst in TCS211 from Openmoko or in
  FC Selenite.  This part handles the trace output from all entities that run
  under GPF and the mechanism for sending external debug commands to the GPF+PS
  subsystem.

GPF was a difficult step in our GSM firmware deblobbing process because no
complete source for it could be found anywhere: apparently GPF was so stable
and so independent of firmware particulars (Calypso or LoCosto, GSM only or
GSM+GPRS, modem or complete phone with UI etc) that it appears to have been
used and distributed as prebuilt binary libraries even inside TI.  All TI fw
(semi-)sources we've got use GPF in prebuilt library form and are not set up to
recompile any part of it from source.  (They had to include all GPF header
files though, as most of them are included by G23M C modules, and it would be
too much hassle to figure out which ones are or aren't needed, hence all were
included.)

Fortunately though, we were able to find the sources for most parts of GPF:

* The LoCosto source in TCS3.2_N5.24_M18_V1.11_M23BTH_PSL1_src.zip features the
  source for the "core" part of GPF under gpf/FRAME - these sources aren't
  actually used by that fw's build system (it only uses the prebuilt binary
  libs for GPF), but they are there.

* Our TCS211 semi-src doesn't have any sources for the core part of GPF, but
  instead it features the source for the test interface and some "misc" parts:
  under gpf/MISC and gpf/tst in that source tree - these sources are not present
  in the LoCosto version from Peek.

The GPF frame, misc and tst sources we have found have been verified to match
the binary objects that came with TCS211 from OM: they can be compiled into a
bit-for-bit match.  However, one critical piece was still missing: the OS
adaptation layer.  It appears that the GPF core (vsi_??? modules) and OSL
(os_??? modules) were maintained and built together, ending up together in
frame_<blah>.lib files in the binary form used to build firmwares, but the
source for the "frame" part in the Peek find contained only vsi_*.c and others,
but not any of os_*.c.

Our FC Magnetite firmware uses the original binary libs from TCS211-Openmoko
for its GPF component, but for FC Selenite the project requirement is to be
completely blob-free, hence we had to reconstruct the source for the OSL part
of GPF from disassembly.  This work was originally done in 2014 in the context
of our first attempt at gcc-built blob-free GSM fw (FC Citrine, now deemed to
be a dead end and fully retired); this reconstruction was then dug up and
adapted for Selenite in 2018.  As of this writing, this reconstruction is still
not 100% complete (one complex error handling function is stubbed out) and not
yet trusted to be fully correct, thus our fully deblobbed Selenite firmware is
currently considered experimental; our current production fw is still Magnetite
with blobs for GPF.

A closer look at L1
===================

The L1 code is remarkable in how little intertie it has with the rest of the
firmware it is linked into.  It is almost entirely self-contained, expecting
only 4 functions to be provided by the underlying OS environment:

os_alloc_sig	-- allocate message buffer
os_free_sig	-- free message buffer
os_send_sig	-- send message to upper layers
os_receive_sig	-- receive message from upper layers

It helps to remember that at the beginning of TI's involvement in the GSM
baseband chipset business, L1 was the only thing they "owned", while Condat,
the maintainers of the higher level protocol stack, was a separate company.
TI's "turnkey" solution must have consisted of their own L1 code plus G23M code
(including GPF etc) licensed from Condat, but I'm guessing that TI probably
wanted to retain the ability to sell their chips with their L1 without being
entangled by Condat: let the customer use their own GSM L23 stack, or perhaps
work out their own independent licensing arrangements with Condat.  I'm
guessing that L1 was maintained as its own highly independent and at least
conceptually portable entity for these reasons.

The way in which L1 is intertied into the rest of the fw is the same in all TI
production firmwares we have seen, including both our TCS211 reference and the
TCS3.2 LoCosto version.  There is a module called OSX, which is an extremely
thin adaptation layer that implements the APIs expected by L1 in terms of GPF.
Furthermore, this OSX layer provides header file isolation: the only "outside"
(non-L1) header included by L1 is cust_os.h, and it defines the necessary
interface to OSX *without* including any other headers (no GPF headers in
particular), using only the C language's native types.  Apart from this
cust_os.h header, the entire OSX layer is implemented in one C module (osx.c,
which we had to reconstruct from osx.obj as the source was missing - but it's
very simple) which does include some GPF headers and implements the OSX API in
terms of GPF services.  Thus in both TI's production firmwares and our own ones,
L1 does sit on top of GPF, but very indirectly.

More specifically, the "production" version of OSX implements its API in terms
of *high-level* GPF functions, i.e., VSI.  However, they also had an interesting
OP_L1_STANDALONE configuration which omitted not only all of G23M, but also the
core of GPF and possibly the Riviera environment as well.  We don't have a way
to recreate this configuration exactly as it existed inside TI because we don't
have the source bits specific to this configuration, but we do have a little
bit of insight into how it worked.

It appears that TI's OP_L1_STANDALONE build used a special "gutted" version of
GPF in which the "meaty core" (VSI etc) was removed.  The OS layer (os_???
modules implementing os_*() functions) that interfaces to Nucleus was kept, and
so was OSX used by L1 - but this time the OSX API functions were implemented in
terms of os_*() ones (low-level wrappers around Nucleus) instead of the higher-
level VSI APIs provided by the "meaty core" of GPF.  It is purely a guess on my
part, but perhaps this hack was also done in the days before TI's acquisition
of Condat, and by omitting the "meaty core" of GPF, TI could claim that their
OP_L1_STANDALONE configuration did not contain any of Condat's "intellectual
property".

Run-time structure of L1
========================

L1 consists of two major parts: L1S and L1A.  L1S is the synchronous part where
the most time-critical functions are performed; it runs as a Nucleus HISR.  The
hardware in the Calypso generates an interrupt on every TDMA frame (4.615 ms,
or more precisely 60/13 ms), and the LISR handler for this interrupt triggers
the L1S HISR.  L1S communicates with L1A through a shared memory data structure,
and also sometimes allocates message buffers and posts them to L1A's incoming
message queue (both via OSX API functions, i.e., via GPF in disguise).

L1A runs as a regular task under Nucleus, and includes a blocking call (to GPF
via OSX) to wait for incoming messages on its queue.  It is one big loop that
waits for incoming messages, then processes each received message and commands
L1S to do most of the work.  The entry point to L1A in the L1 code proper is
l1a_task(), although the responsibility for running it as a task falls on some
"glue" code outside of L1 proper.  TI's production firmwares with G23M included
have an L1 protocol stack entity within G23M whose only job (aside from some
initialization) is to run l1a_task() in the Nucleus task created by GPF for
that protocol stack entity; we do the same in our firmwares.

Communication between L1 and G23M
=================================

It is remarkable that L1 and G23M don't have any header files in common: L1
uses its own (almost fully self-contained), whereas the G23M+GPF realm is its
own world with its own header files.  One has to ask then: how do they
communicate?  OK, we know they communicate through primitives (messages in
buffers allocated from GPF's PRIM partition memory pool) passed via message
queues, but what about the data structures in these messages?  Where are those
defined if there are no header files in common between L1 and G23M?

The answer is that there are separate definitions of the L1<->G23M interface on
each side, and TI must have kept them in sync manually.  Not exactly a
recommended programming or software maintenance practice for sure, but TI took
care of it, and the existing proprietary products based on TI's firmware are
rock solid, so it is not really our place to complain.

TI's firmwares from the era we are working with (both our TCS211 golden
reference and the TCS3.2/LoCosto source from which we took the newer full-source
version of G23M for our TCS2/TCS3 hybrid) also include a component called ALR.
It resides in the G23M code realm: G23M coding style, uses Condat header files,
runs as its own protocol stack entity under GPF.  This component appears to
serve as a glue layer between the rest of the G23M stack (which is supposed to
be truly hardware-independent) and TI's L1.

Speaking of ALR, it is worth mentioning that there is a little naming
inconsistency here.  ALR is known to the connect-by-name logic in GPF as "PL"
(physical layer, apparently), while the ACI entity (Application Control
Interface, the top level entity) is known to the same logic as "MMI".  No big
deal really, but hopefully knowing this quirk will save someone some confusion.

A closer look at our FreeCalypso TCS2/TCS3 hybrid
=================================================

Because we don't have an official TI firmware release for the Calypso in full
source form and because I am not willing to throw away all of our Calypso work
and restart anew with LoCosto with its own host of unknowns, the only currently
available way for us to have blob-free production-quality GSM mobile station fw
is the TCS2/TCS3 hybrid implemented in FC Magnetite and Selenite.  This hybrid
is made by taking the G23M version from TCS3/LoCosto and grafting it onto the
chipsetsw foundation from TCS211, including the original TCS211/Calypso version
of L1 which we have meticulously source-reconstructed.  The version of GPF used
for this hybrid is also the TCS211 version in Magnetite or our source
reconstruction thereof in Selenite.

The Condat G23M pieces have been hybridized as follows:

* cdginc generated header files are a special hybrid version described below;

* The include files under condat/com/inc and condat/com/include are the TCS3
  version, except for pwr.h and rtc.h for which we use the TCS2 version;

* comlib is the TCS2 version, except for cl_rlcmac.c which is from TCS3;

* config modules (condat/com/src/config and condat/frame/config) are the TCS2
  version, with some fixes for the needs of the TCS3 version of G23M PS and our
  own FreeCalypso fixes;

* Condat drivers (condat/com/src/driver) are the TCS2 version;

* All G23M PS components are the TCS3 version by necessity, as this is the part
  for which the source is missing in our TCS211 version, with the exception of
  ALR - the original source for the TCS211 version of ALR has miraculously
  survived, the ALR source in TCS211 from OM can be compiled into a perfect
  match for the binary lib version;

* We use the TCS2 version of ALR (the interface to our TCS211 L1) and not the
  TCS3 version (a change from Citrine), but it is compiled with the same hybrid
  cdginc headers as the rest of hybrid G23M, not the old TCS211 ones;

* ACI is the TCS3 version - we have the source for both versions, but trying to
  use the old TCS2 version of ACI on top of the new TCS3 version of the PS
  would cause untold breakage;

* The UI layers (MFW and BMI) for handset fw builds are handled like ACI: we
  have the source for both versions, but we use the TCS3 version which works
  with the TCS3 versions of ACI and cdginc;

* The CST (Customer Specific Task) component is the TCS2 version - while it
  logically belongs in the Condat realm, the code lives in the chipsetsw realm
  under chipsetsw/services/cst (yes, it's under services with SSA stuff even
  though it doesn't use RiViera) and thus our copy of TCS211 from OM has this
  source preserved.

With this hybrid arrangement the main splice point lies above ALR, and there
are many little splice points throughout the code where some upper-level code
from TCS3 needs to talk to lower-level code from TCS2.  There are no inversions,
i.e., no places where TCS2 code sits on top of code from TCS3, although there
are a few instances where TCS2 C code uses some TCS3 header files.

TCS3 feature flags
------------------

Our TCS3.2/LoCosto code from Peek/FGW from 20090327 supports several new GSM
features (apparently related to GSM release 99) which are not supported by our
TCS211-20070608 golden reference from OM.  All of these new features can be
enabled or disabled with conditional compilation flags.  Our TCS2/TCS3 hybrid
currently has all of these new features disabled: it was too difficult for me
to figure out if these new features require some support from the hardware or
the DSP which is present on LoCosto but not Calypso, and even if our hw and DSP
have all of the necessary capabilities, at least some of the new features
require adding some code to L1, which is incompatible with my approach of
reconstructing TCS211 L1 pristinely.

In any case, the GSM functionality we get by using the new version of G23M with
new feature flags disabled on top of pristine TCS211 L1 cannot be any worse
than what we would have had if we had the full corresponding source for our
TCS211-20070608 golden reference, and it is probably a little better because we
are using a newer version of G23M code.

cdginc headers
--------------

Much of the code in the Condat G23M realm makes heavy use of a set of machine-
generated C header files called cdginc.  These header files contain various
definitions related both to the GSM air protocols being implemented and to G23M
protocol stack internals (interfaces and message structures between components),
and they are generated from a set of message definition files (*.mdf) and
primitive definition files (*.pdf) by a tool called ccdgen.  The *.{mdf,pdf}
inputs to ccdgen are human-readable ASCII, and of course the generated C header
files are human-readable too, but we have no source for the ccdgen tool itself,
only a Windows binary which we can run under Wine.

The ccdgen binary problem is yet another instance of so far incomplete
liberation of the GSM firmware.  It is currently a very low-priority problem:
we do not casually edit any of the *.{mdf,pdf} inputs to ccdgen, and we don't
run ccdgen on every fw build - instead we have run ccdgen once and checked its
output files (generated C headers) into our Magnetite and Selenite trees as if
they were sources.  If we are not able to convince TI to dig up and release the
source for ccdgen, there is a viable albeit costly alternative: hire a Windows
reverser to RE the ccdgen.exe binary (262144 bytes) and produce a C
reimplementation that replicates all of its logic.  It is a Win32 console app,
no GUI, and it is a pure data processing application without any hardware access
or OS functions or any other muckery: it is probably pure ANSI C code that reads
and parses a bunch of ASCII input files, performs some business logic on the
data, and writes another bunch of ASCII text files as outputs.  It is currently
a very low-priority task though; reversing the Calypso DSP ROM code should
probably be a higher priority.

The set of cdginc headers for our TCS2/TCS3 hybrid has been generated as
follows:

* All of the *.mdf files are the TCS3 version;

* All of the *.pdf files except mphc.pdf and mphp.pdf are also the TCS3 version;

* mphc.pdf and mphp.pdf are the TCS211 version - this is the interface to
  TCS211 L1;

* All new feature flags (see discussion above) are set to disabled.

Condat Coder and Decoder (CCD)
------------------------------

CCD is a firmware component in the Condat G23M realm which I haven't really
studied yet.  It consists of two parts:

* A fixed portion which TI used to distribute in binary form and which various
  firmware projects used as a prebuilt library like GPF - technically TI
  considered it to be a part of GPF, although we prefer to treat it as its own
  more independent entity;

* The ccddata portion which needs to be compiled with cdginc headers for each
  given project.

We got the source for both parts of CCD only in the TCS3.2/LoCosto version, but
not in the TCS211 version, hence the decision was easy: we use the TCS3 version
of CCD (both parts) with the TCS3 version of cdginc with the TCS3 version of
the G23M PS.

TCS3.2 GPF discrepancy
----------------------

A careful examination of the prebuilt GPF libraries under gpf/LIB in the TCS3.2
LoCosto source tree has revealed that a few of the binary objects exhibit some
differences from the TCS211 version which we've been treating as our golden
reference:

* The os_mis module (OSL miscellany) in the IRAM library implements a new
  function called os_CheckQueueEvent() and defines a new global data object
  named my_os_mis_Protect;

* The os_tim module (OSL timer code) in the flash (XIP) library has some code
  differences;

* The vsi_tim module (VSI timer code) in the flash (XIP) library has some code
  differences;

* The vsi_tim module (VSI timer code) in the IRAM library has some code
  differences and makes use of the new os_CheckQueueEvent() function.

In the case of os_??? modules we have no corresponding source for either
version, but the vsi_tim difference is more bizarre: we got our vsi_tim.c source
(and the rest of vsi_*.c) from the TCS3.2/LoCosto source, but this source
matches the TCS211 binary version and not the newer and different binary version
used by the TCS3.2 build system!  (Remember that none of TI's firmware build
systems that we have seen are set up to recompile any part of GPF from source,
they used it only as prebuilt libraries.)

Because we have the corresponding source for the "old" version of GPF frame core
but not for the "new" version, we are continuing to treat the "old" TCS211
version as our golden reference: we use the source pieces which we got, and we
use the "old" os_???.obj blobs as our basis for reconstruction via disassembly.

Because the changes in the TCS3.2 binary version of GPF involve only the
implementation of a part of VSI but not its API (there are no changes to any
part of the GPF API presented to the G23M PS that I can see anywhere), I have
every good reason to believe that there is no problem with using the new TCS3.2
version of G23M with the old version of GPF from TCS211: it should work no worse
than pure TCS211.

It should also be noted that if we ever succeed in getting some more complete
GPF source out of TI (including the source for the OS adaptation layer which is
difficult to reconstruct), thanks to the great stability and independence of
GPF, we will be happy with *any* version, does not need to match either TCS211
or TCS3.2.

GPRS implementation differences
-------------------------------

There is a visible difference between the way GPRS is implemented in the old
TCS211-20070608 blob version of G23M and the way it is implemented in the newer
TCS3.2/LoCosto version we are using for our hybrid.  The new implementation adds
a new protocol stack entity named UPM (User Plane Manager), and the pre-existing
SM and SNDCP entities have been significantly changed to work with this UPM.
Because we are using the GPRS config modules (condat/frame/config) from TCS211,
we had to add a -DFF_UPM compilation flag to include UPM in the GPF
configuration for the GSM+GPRS protocol stack.

A closer look at ACI
====================

The Application Control Interface (ACI) is the crown that sits on top of the
G23M protocol stack.  It includes the AT command interpreter (ATI) component,
and this AT command interface is brought to the outside world via the UART
protocol stack entity.  The UART entity implements the GSM 07.10 MUX, can
operate the physical UART in either multiplexed or non-multiplexed mode (the
latter is the default on boot for a plain ASCII AT command interface) as
commanded by ACI, and establishes 1 to 4 logical channels carrying AT commands
to ACI.  When a CSD or fax call or a GPRS PPP session is in progress, the data
path is switched to run between the UART entity and the appropriate GSM or GPRS
protocol stack destination.  In the case of modem products that are designed to
be controlled by an external host via AT commands, this combination of ACI and
UART entities provides the ultimate end function of the device.

The set of implemented AT commands is defined in ati_cmd.c: this is the C file
where new AT commands get added; there is also an enum of command IDs in
aci_cmh.h which needs to be extended.  For every AT command listed in the table
in ati_cmd.c there is a handler function: for example, for the AT+CFUN command
there is a setatPlusCFUN() function that handles setting and a queatPlusCFUN()
function that handles querying.  For some simple AT commands like AT+CGxx
queries the function listed in ati_cmd.c does the entirety of the work, but for
most of the interesting GSM commands (including the AT+CFUN example just used)
the set and query functions implemented in the ATI layer only handle the parsing
of ASCII arguments and generation of ASCII output (if any), whereas the actual
command implementation happens in the CMH layer below.

Below ATI but still within ACI lies the sublayer of command handlers (CMH).
For each AT command that does something to the GSM mobile station there is a
functional equivalent, a C function that performs the same operation as the
spec-defined AT command, but is designed to be used natively from C code,
without AT command string parsing or output formatting.  For the AT+CFUN example
used above, the setatPlusCFUN() ATI function parses the arguments from ASCII
and then calls sAT_PlusCFUN() to perform the actual operation, whereas the
queatPlusCFUN() ATI function calls qAT_PlusCFUN() to retrieve the current state
and then prints it out in ASCII.  This functional interface is used by TI's
demo/prototype phone UI implementation described in the Handset-UI-fw companion
document.

Finally, at the bottom of ACI lies the sublayer of Protocol Stack Adapters
(PSA): these are pieces of code that execute within the ACI task and exchange
primitives with various G23M protocol stack entities below.

We have the source for both TCS2 and TCS3 versions of ACI.  The TCS2 version is
from Openmoko, containing OM's modifications, and we had to go through these
changes and additions by OM, reject the bogus ones and reimplement the sensible
ones in the new TCS3 version of ACI for our TCS2/TCS3 hybrid going forward.

Flash file system
=================

Every GSM device that is based on TI's firmware architecture contains not only
the firmware image proper, but also a flash file system that is separate from
the fw image and is maintained in a different part of the flash chip.  The FFS
implementation code is a mandatory part of the firmware; in TCS211 it resides
in chipsetsw/drivers/drv_app/ffs and logically belongs to the SSA realm.  This
code initializes early in the fw boot process in the Application_Initialize()
context before the start of Nucleus task scheduling; the responsible function
is ffs_main_init() called from Init_Drivers().

Flash driver support and FFS location
-------------------------------------

Determining the location of the flash area allocated for FFS and the flash
driver to be used to write to it is a combination of autodetection and hard-
coding.  The approach implemented in the original TCS211 code is as follows:
there is a piece of autodetection code that reads the flash chip ID, and the
autodetected ID is then looked up in a hard-coded table that gives the driver
and geometry details and the location of the FFS sectors for each supported
flash chip type.  However, this approach has its limitations:

* The sequence of write operations which TI's original autodetection code
  issues in order to put the flash chip into its Read ID mode worked for older
  flash chips that were used by TI and Openmoko, but does not work for the newer
  Spansion S71PL129NC0HFW4B flash chip which we (FreeCalypso) have copied from
  the Pirelli DP-L10 phone.  We have now fixed it, but until recently we had to
  disable flash autodetection and hard-code the flash chip type on Pirelli and
  FCDEV3B targets.

* While the physical flash chip used on a given phone or modem board is a
  physical property that can be autodetected, the choice of which flash sectors
  should be used for FFS is a matter of policy.  Before we built our own
  FreeCalypso hardware, we had to run our fw on some pre-existing "alien" hw
  targets, and we still support such usage to a limited extent.  When we run
  our FreeCalypso fw on an alien hw target as an aftermarket deal, our
  aftermarket FFS location needs to be chosen quite carefully.

* Some flash chips have two chip select banks, and with such chips it is
  generally desirable to put the FFS in the second bank.  However, it is a
  matter of board wiring whether that second flash chip select is connected to
  Calypso chip select nCS2, nCS3 or nCS4 - thus FFS addresses in the second bank
  have to be hard-coded with conditional compilation per board type and cannot
  be autodetected.

To support our new repertoire of possible hardware targets, we have made the
following changes in our Magnetite and Selenite firmwares:

* We have a new version of the ffsdrv_device_id_read() autodetection function
  that issues AMD's Read ID command sequence in a way that works with all flash
  chips which we've encountered so far in real life, including Openmoko's
  Samsung K5A3281 and our new (originally Pirelli's) Spansion flash chip.  We
  have also incorporated the logic from Pirelli's firmware that distinguishes
  between S71PL-J and S71PL-N chips: they have different sector sizes which FFS
  needs to know about, but they have the same ID codes and can only be
  distinguished through CFI.

* The autodetected flash ID code is looked up in a compiled-in table like
  before, but we now have 4 different versions of this table selected by
  conditional compilation based on the target for which the firmware is being
  built:

  - For our own FC hardware family (CONFIG_TARGET_FCFAM) we have our brand-new
    table of possible flash configurations which we keep free of any legacy
    gunk;

  - For Mot C1xx targets (CONFIG_TARGET_COMPAL) we have a dedicated table
    giving our aftermarket FFS configurations for Intel flash chip types found
    in these phones;

  - For the Pirelli DP-L10 target (CONFIG_TARGET_PIRELLI) we likewise have
    another dedicated table giving our aftermarket FFS config for Pirelli's
    S71PL-J or S71PL-N flash;

  - The #else clause is the original table from TI/Openmoko, used on
    dsample and gtamodem targets.

The advantages of this new approach over our previous approach of disabling
flash autodetection and using a strictly fixed hard-coded FFS config for
FreeCalypso and Pirelli targets are:

* The high-capacity flash chip we are currently using (S71PL129NC0HFW4B) is
  great for development boards or perhaps for a high-end Pirelli-like feature
  phone, but it would be way overkill for an embedded modem product - for the
  latter device class a smaller flash chip like Openmoko's K5A32xx would be
  more appropriate.  The new autodetection approach makes it possible to build
  a single fw image that can run on both large-flash and small-flash boards.

* I've only seen Pirelli phones with S71PL-N flash so far, but their original
  fw supports both S71PL-J and S71PL-N with autodetection.  We can now do
  likewise in our FreeCalypso fw.

Finally, independent of flash chip type autodetection vs. hard-coding issues,
we have had to change the AMD multibank flash driver to issue write commands in
a way that is compatible with our new S71PL129NC0HFW4B chip.  It still works
just as well with Openmoko's K5A32xx.

FFS life cycle
--------------

In products that have been built according to TI's original way, including
Openmoko GTA01/02 and our own FreeCalypso devices, the FFS is formatted and
initialized with some essential content at the time of device manufacture, and
this factory-created and factory-initialized FFS then persists for the lifetime
of the device.  In our factory environment at FreeCalypso hardware manufacturing
we initialize the flash on our freshly assembled boards like this:

flash erase 0 0x800000
flash program-bin 0 fwimage.bin
flash2 erase 0 0x800000

This factory procedure (which should ONLY be executed at the factory and never
by any end users or even sw/fw developers and tinkerers) ensures that the flash
is completely blank everywhere except the fw image loaded at the time of
production, and when this fw image boots for the first time, it will see blank
flash in the FFS sectors.  When TI's FFS code in ffs_main_init() sees this
condition, it performs what TI called a preformat: it writes a basic FFS block
header into each FFS sector, but does not automatically perform a full format -
instead the latter needs to be commanded explicitly by the production station
via one of TMFFS command packet protocols as described later in this article.
In FreeCalypso we have adopted TMFFS2 as our choice of Test Mode FFS access
protocol, our host side implementation of this protocol is fc-fsio, and we
format and initialize the FFS on our devices with an fc-fsio command script as
part of our factory procedure.

FFS content and usage
---------------------

TI's firmware architecture uses the FFS for many purposes:

* The IMEI is stored in the FFS - GSMA can proclaim all they want that it
  "MUST" be stored in some kind of super-secure one-time programmable fuses,
  but in TI's architecture and in FreeCalypso it is just a regular file in the
  FFS.

* A number of RF calibration tables are stored in FFS and read by the RF code
  in L1.  If you have a Rohde&Schwarz CMU200 instrument which is itself in good
  repair and calibration standing and a metrology-grade RF cabling setup whose
  insertion loss at the relevant GSM frequencies is precisely known, creating
  or recreating these RF calibration values is as simple as executing one shell
  script that takes a few minutes to run - this is how we do it at FreeCalypso
  hw manufacturing - but if you are an ordinary user or sw/fw developer or
  tinkerer without a professional calibration station setup, you need to use
  the RF calibration values that have been written into the FFS by the device
  manufacturer.  These RF calibration tables live under /gsm/rf.

* /gsm/com/rfcap tells the RR component in the G23M protocol stack (not L1!)
  which frequency bands are supported on a given device - on our devices it is
  a factory-programmed file distinguishing between tri900 and tri850 units and
  telling the firmware which bands it should scan for possible GSM cells.

* Manufacturer, model and revision ID strings may be written into /pcm/CGMI,
  /pcm/CGMM and /pcm/CGMR, respectively, to be returned by the corresponding
  AT+CGxx query commands.

* The G23M protocol stack writes a number of dynamically updated files under
  the /gsm hierarchy and under /pcm.

* TI's demo/prototype UI code (see Handset-UI-fw companion document) writes its
  persistent state in files under /mmi.

* Audio mode configuration files are kept under /aud - see the Audio-mode-config
  article in freecalypso-tools.

* If a given product uses the Melody E1 mechanism, melody files to be played
  through the RiViera Audio Service are kept in FFS - see the Melody_E1 article
  in freecalypso-tools.

Building firmware for different targets
=======================================

TI's TCS3.2 firmware for their LoCosto chipset which was rejected by the Mother
for reasons described near the beginning of this article makes a complete break
from the past and has no possibility of supporting any pre-LoCosto chips such
as our beloved Calypso, but TI's previous evolutionary developments weren't so
drastic: the evolution to Calypso from previous chips such as Hercules and
Ulysse was smoother, and our reference TCS211 fw is littered with C preprocessor
conditionals supporting TI's earlier development boards prior to D-Sample and
DBB chips prior to Calypso.

TI's configuration management architecture supported only TI's own development
boards and not any of the end product boards: unfortunately they did not follow
a development model like the Linux kernel where everyone is encouraged to
contribute their custom board support bits upstream and the mainline kernel
strives to support every hw target that was ever supported with a single source
tree, instead it was the divergent model where every end device manufacturer
would take TI's reference firmware source and hack it for their specific needs
with no concern for upstreamability or support for targets or applications
other than their own.  TI's firmware build configuration model defined the
following C preprocessor symbols relating to support for different hw targets,
all numeric, i.e., each symbol is always defined to a number:

BOARD identifies which board is to be targeted, with numbers assigned for
different development boards made by various TI groups, but generally not for
customer boards.  The only Calypso-based BOARD number is 41, originally
assigned for the D-Sample but then also reused for the Leonardo; all other
BOARD numbers are for some other chipsets that aren't Calypso.  The previous
board before D-Sample was C-Sample, which is BOARD 9, but I am not sure exactly
what chipset it had - perhaps it was Ulysse/Nausica/Clara.  There is still
plenty of support for BOARD 9 and even earlier boards in the firmware source we
got.

CHIPSET identifies the main DBB chip.  The interesting numbers are 7 for the
very original Calypso C05 rev A, 8 for Calypso C05 rev B (found on the D-Sample
board which the Mother scored in 2015), 10 for Calypso C035 (the Calypso silicon
version we work with in FreeCalypso), 11 for Calypso Lite (same as the regular
Calypso except for smaller IRAM), 12 for Calypso+ (a short-lived intermediate
step between Calypso and LoCosto) and 15 for LoCosto.

ANLG_FAM (previously ANALOG) identifies the ABB chip.  The numbers are 1 for
Nausica, 2 for Iota (what we use) and 3 for Syren (typically used with Calypso+
like on the E-Sample board).

RF_FAM (previously just RF) identifies the RF hardware hooked up to the baseband
chipset.  The interesting numbers are 10 for Clara (D-Sample) and 12 for Rita,
the latter being the only RF chip for which we have driver support.

Naturally any code that cares about DBB register differences would use the
CHIPSET definition, ABB support code would use ANLG_FAM, RF support code would
use RF_FAM, and finally code that needs to know about board-level peripherals
like LCDs and keypads would use the BOARD symbol.  This model worked fine up to
D-Sample: for example, the code for C-Sample vs. D-Sample LCDs and keypads is
cleanly conditionalized on BOARD 9 vs. BOARD 41.  However, the waters got badly
muddied when TI introduced their Leonardo board and instead of giving it its
own BOARD number, reused BOARD number 41 from D-Sample.

D-Sample was TI's primary internal development platform for the Calypso,
featuring Iota for the ABB and Clara for the RF part.  It was a great solid
platform in every way except the RF part: the old Clara RF is inconvenient
(needs more external parts) and TI were marketing their newer Rita RF to real
end device manufacturers, but the D-Sample still worked great for development:
if you aren't working specifically on the RF part, it doesn't matter as long as
you have a working driver for it, which we lack.  Then TI made another Calypso
development board called Leonardo, featuring the same Calypso+Iota baseband
plus the newer Rita RF.  But this Leonardo never fully replaced the D-Sample
for any of the high-level development in the SSA and UI groups.

Openmoko's modem is a direct derivative of the Leonardo, the only change being
the RFFE (for some reason FIC didn't like TI's quadband RFFE as implemented on
Leonardo and E-Sample boards and used their own slightly hobbled triband RFFE
instead), and the firmware build given to OM was TI's Leonardo fw with just a
few tweaks in tpudrv12.h to account for the RFFE control signal differences.
However, because Leonardo never got its own BOARD number and the BOARD symbol
is still set to 41, all of the SSA/UI code (LCD, keypad, battery charging etc)
is still built as if for D-Sample - but none of that code is used on a pure AT
command modem without UI functions or UI hardware, hence OM probably never
noticed anything odd.

And it wasn't just Openmoko - it appears that TI used their Leonardo boards
mostly or perhaps even solely in the ACI configuration without UI layers
(MMI=0 build configuration), while all or most UI development was done on
D-Sample kits.  Their TCS211 reference fw product officially supported both
D-Sample and Leonardo targets in both ACI and BMI+MFW configurations, but if
one were to build a high-end UI-enabled config for the Leonardo like pdt_2272,
it would target a 176x220 pixel color LCD, the LCD output driver would be the
one for the D-Sample (expecting memory-mapped LCD registers on nCS3), and the
keypad driver would expect D-Sample keypad wiring.  Looking at the available
Leonardo schematics I see a serial (uWire) LCD interface instead and a more
basic keypad with different wiring, so I don't see how those Leonardo+UI
firmware builds could possibly work.  Perhaps some other group at TI did some
UI work on Leonardo boards, but never made it into the internal mainline
from which TCS211 releases were cut - who knows...

Finally, aside from the basic failure to distinguish properly between D-Sample
and Leonardo boards, this whole BOARD number system provides absolutely no
mechanism to distinguish between TI's development boards and end product boards
derived from them, or between end product boards of vendor A vs. vendor B, or
between end product model A and model B from the same vendor - it's always
BOARD 41 as far as TI's code is concerned.  When TI had to modify their code
for OM to support FIC's different TSPACT signal wiring, they just edited the
definitions in tpudrv12.h without any conditionals, so one couldn't build
binaries for the original Leonardo vs. OM's hardware from the same source tree
in different configs.

The build system of TCS211 produces a set of generated C header files named
*.cfg (instead of the more natural *.h); these generated config headers define
all of the C preprocessor symbols listed above and many more.  They are included
sometimes as #include "board.cfg" and othertimes as #include "config/board.cfg"
(ditto for other *.cfg), thus the list of -I directories passed by the build
system on compiler invocation lines needs to include both the config directory
and its parent.  In our Magnetite and Selenite build systems we likewise
generate these *.cfg headers; some of the symbols defined therein are variable
and originate from Bourne shell variables in our own configuration system, but
many others are fixed.  See scripts/cfg-template in our Magnetite and Selenite
trees for the magic.

The BOARD symbol is always fixed at 41 in all FreeCalypso firmwares,
corresponding to TI's D-Sample and Leonardo, and we use our own different
mechanism to distinguish among our supported targets.  The solution adopted in
Magnetite and Selenite is as follows: we are supplementing TI's *.cfg and
rv_swe.h files with our own fc-target.h (included as #include "fc-target.h" or
as #include "config/fc-target.h" matching whatever existing TI code we are
gently extending), and this fc-target.h header is populated by the build system
by copying the appropriate targets/*.h header file.  These targets/*.h
header snippets define C preprocessor symbols of our own invention like
CONFIG_TARGET_xxx, and whenever we need to know our target in C code, we
#include "fc-target.h" and use #ifdef logic based on these preprocessor symbols
of our own addition.

RVTMUX debug and development interface
======================================

The Calypso chip has two UARTs, and TI's TCS211 firmware and its predecessors
are designed with the assumption that both of these UARTs are available.  Per
TI's fw architecture, Calypso's MODEM UART presents the standard AT command
interface with GSM 07.10 MUX, CSD, fax and GPRS capabilities as described
earlier when we looked at ACI and ATI, whereas the other UART (called the IrDA
UART in hardware docs but not used for that purpose) presents a vitally
important debug, development and production interface called RVTMUX.  This
RVTMUX interface can also be moved to the MODEM UART, in which case the standard
AT command interface is lost.

RVTMUX is a binary packet interface, and it got its name because it is a MUX of
multiple logical channels managed by the RiViera Trace (RVT) firmware component.
RVTMUX is often thought of as being primarily a debug trace interface, as that
is the primary use to which it is put: in normal operation the firmware emits
quite voluminous debug trace output on the IrDA UART, encapsulated in 3
different RVTMUX channels as explained below.  However, it is also possible to
send a number of different debug and development commands to the firmware via
this interface, and this functionality is used as a critical component in
Calypso GSM device factory production line processes: this RVTMUX interface is
the only way by which the FFS can be initialized, RF calibration and tests can
be performed and the IMEI can be set at the factory.

Communication with a running firmware over this RVTMUX interface in a
development or production setting (whether passively reading debug traces or
actively sending development or test commands to the running fw) requires
specialized host tools.  TI originally had a suite of Windows-based tools for
this purpose, but we are not using them in FreeCalypso: we only got Windows
binaries without any sources, and even in the case of those binaries we only
got an incomplete set with some important tools missing.  Instead we are using
our own Unix-based tools called FreeCalypso host tools; these tools have been
developed from scratch by Mother Mychaela after studying the firmware components
with which they need to communicate.

Debug trace output
==================

The firmware component that "owns" the physical UART channel assigned to RVTMUX
is RVT, contained in chipsetsw/riviera/rvt in TCS211 or in src/cs/riviera/rvt
in our Magnetite and Selenite firmwares.  It is a Riviera-based component,
and it has a Nucleus task that is created and started through Riviera.  All
calls to the actual driver for the UART are made from RVT.  In the case of
output from the Calypso GSM device to an external host, all such output is
performed in the context of RVT's Nucleus task; this task drains RVT's message
queue and emits the content of allocated buffers posted to it, freeing them
afterward.  (The dynamic memory allocation system in this case is Riviera's,
which is susceptible to fragmentation - see discussion earlier in this article.)
Therefore, every trace or other output packet emitted from a GSM device running
our fw (or any of the proprietary firmwares based on the same architecture)
appears as a result of a message in a dynamically allocated buffer having been
posted to RVT's queue.

RVT exports several API functions that are intended to be called from other
tasks, it is by way of these functions that most output is submitted to RVT.
One can call rvt_send_trace_cpy() with a fully prepared output message, and
that function will allocate a buffer from Riviera's dynamic memory allocator
properly accounted to RVT, fill it and post it to the RVT task's queue.
Alternatively, one can call rvt_mem_alloc() to allocate the buffer, fill it in
and then pass it to rvt_send_trace_no_cpy().

At higher levels, there are a total of 3 kinds of debug traces that can be
emitted:

* Riviera traces: these are generated by various components implemented in
  Riviera land, although in reality any component can generate a trace of this
  form by calling rvf_send_trace() - this function can be called from any task.

* L1 traces: L1 has its own trace facility implemented in
  src/cs/layer1/cfile/l1_trace.c; it generates its traces as ASCII messages and
  sends them out via rvt_send_trace_cpy().

* GPF traces: code that runs in GPF/G23M land and uses those header files and
  coding conventions etc can emit traces through GPF.  GPF's trace functions
  (implemented in gpf/frame/vsi_trc.c) allocate a memory partition from
  GPF's TEST pool, format the trace into it, and send the trace primitive to
  GPF's special test interface task.  That task receives trace and other GPF
  test interface primitives on its queue, performs some manipulations on them,
  and ultimately generates RVT trace output, i.e., a new dynamic memory buffer
  is allocated in the Riviera land, the trace is copied there, and the Riviera
  buffer goes to the RVT task for the actual output.

Trace masking
=============

The RV trace facility invoked via rvf_send_trace() has a crude masking ability,
but by default all traces are enabled.  In TI's standard firmwares most of the
trace output comes from L1: L1's trace output is very voluminous, and most of
it is fully enabled by default.

On the other hand, GPF and therefore G23M traces are mostly disabled by default.
One can turn the trace verbosity level from any GPF-based entity up or down by
sending a "system primitive" command to the running fw, and another such command
can be used to save these masks in FFS, so that they will be restored on the
next boot cycle and be effective at the earliest possible time.  Enabling *all*
GPF trace output for all entities is generally not useful though, as it is so
verbose that a developer trying to make sense of it will likely drown in it -
and it will also overwhelm the debug trace facility itself, causing most of
these far too voluminous traces to be lost.  Therefore, a developer seeking to
debug an issue in the G23M protocol stack needs to enable traces very
judiciously.

GPF compressed trace hack
=========================

TI's Windows-based GSM firmware build systems include a hack called str2ind.
Seeking to reduce the fw image size by eliminating trace ASCII strings from it,
and seeking to reduce the load on the RVTMUX serial interface by eliminating
the transmission time of these strings, they passed their sources through an
ad hoc preprocessor that replaces these ASCII strings with numeric indices.
The compilation process with this str2ind hack becomes very messy: each source
file is first passed through the C preprocessor, then the intermediate form is
passed through str2ind, and finally the de-string-ified form is compiled, with
the compiler being told not to run the C preprocessor again.

TI's str2ind tool maintains a table of correspondence between the original trace
ASCII strings and the indices they've been turned into, and a copy of this table
becomes essential for making sense of GPF trace output: the firmware now emits
only numeric indices which are useless without this str2ind.tab mapping table.

Our FC Magnetite build system retains the option of using str2ind, but it is
disabled by default: str2ind significantly increases firmware compilation times,
the resulting fw image sizes without str2ind are fine (the slight increase does
not push us over any limits), and we haven't had any issues with ASCII strings
overloading the trace interface.  However, there is an additional complication
stemming from the choice of two possible G23M PS versions, one of which is a
set of blob libraries:

* If Magnetite is compiled in a pure TCS211 configuration using the original
  blob version of G23M PS, these blobs already have str2ind indices baked into
  them instead of trace ASCII strings, hence the frozen str2ind.tab file from
  Openmoko that maps these indices back to strings needs to be used.

* If Magnetite is compiled in a TCS2/TCS3 hybrid config without G23M blobs,
  then unless you enable it explicitly with USE_STR2IND=1, no str2ind will be
  used at all.

Our blob-free FC Selenite firmware does not support str2ind at all - we shall
stick with full ASCII string traces until and unless we run into an actual (as
opposed to hypothetical) problem with either fw image size or serial interface
load.

RVTMUX command input
====================

RVTMUX is not just debug trace output: it is also possible for an external host
to send commands to the running fw via RVTMUX.

Inside the fw RVTMUX input is handled by the RVT entity by way of a Nucleus
HISR.  This HISR gets triggered when Rx bytes arrive at the designated UART,
and it calls the UART driver to collect the input.  RVT code running in this
HISR parses the message structure and figures out which fw component the
incoming message is addressed to.  Any fw component can register to receive
RVTMUX packets, and provides a callback function with this registration; this
callback function is called in the context of the HISR.

In the original TCS211 fw there are only two components that register to receive
external host commands via RVTMUX: ETM and GPF, hence these are the only command
packet types that can be sent to this original fw.  In FreeCalypso we have kept
these, and we've also added some new RVTMUX channels of our own invention.

Test Mode (TM) and Enhanced Test Mode (ETM)
===========================================

A 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 like TCS211 does, 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 the sole surviving copy of TCS211 that serves as our golden
reference came with L1TM in binary object form like the rest of L1, and we got
to source-reconstructing it only much later.

ETM is implemented as a Riviera SWE and has its own Nucleus task; the callback
function that gets called from the RVT HISR posts received messages onto ETM's
own queue drained by its task.  The ETM task gets scheduled, picks up the
command posted to its queue, executes it, and sends a response message back to
the external host through RVT.

Because all ETM commands funnel through ETM's queue and task, and that task
won't start looking at a new command until it finished handling the previous
one, all ETM commands and responses are in strict lock-step: it is not possible
to send two commands and have their responses come in out of order, and it makes
no sense to send another ETM command prior to receiving the response to the
previous one.  (But there can still be debug traces or other traffic intermixed
on RVTMUX in between an ETM command and the corresponding response!)

L1TM commands get posted to the message queue of the L1A task and then executed
in that task's context.

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 (at least in TI's original architecture,
as opposed to Motorola's bastardized version) is the ability to access and
manipulate the GSM device's flash file system (FFS) that was described earlier
in this article.  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 Magnetite and
Selenite firmwares.

Our choice of TMFFS2 over TMFFS1 was driven by the need to develop our own host
tools to replace TI's original ones which we never got.  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 (Mother
Mychaela) 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 the implementation
code in chipsetsw/drivers/drv_app/ffs/board/tmffs.c in TCS211 if you would like
to judge for yourself.)  Our host tool that speaks the TMFFS2 protocol is
fc-fsio.

GPF external command input
==========================

The other component that can receive external commands is GPF.  GPF's test
interface can receive so-called "system primitives", which are ASCII string
commands parsed and acted upon by GPF, and also binary protocol stack
primitives.  Remember how all entities in the G23M stack communicate by sending
messages to each other?  Well, GPF's test interface allows such messages to be
injected externally as well, directed to any entity in the running fw.  System
primitive commands can also be used to cause entities to send their outgoing
primitives to the test interface, either instead of or in addition to the
originally intended recipient.

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, in the case of our FreeCalypso family of projects about 2 years had
passed between our first functional GSM fw attempts in 2015 and us successfully
building our own development board in 2017; during this time we had to work on
various crippled pre-existing Calypso devices, and many of them had only one
UART practically accessible.  In response to this situation 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, described below.

Now that we have built a proper FreeCalypso development board with two UARTs,
the use of this AT-over-RVTMUX hack is deprecated for general usage: this hack
does not support any data services (CSD or GPRS), and even for SMS it was
crippled for a long time because maximum-length messages could not be sent in
the more capable PDU mode until our recent extension that works around this
limitation.  However, it still comes in handy during certain casual testing
sessions, and it is required if one needs to run our FreeCalypso firmware on
Mot C1xx or Pirelli DP-L10 hardware.

FC host tools for talking to firmwares via RVTMUX
=================================================

The fundamental tool for talking to running firmwares via RVTMUX is a program
called rvinterf.  It runs on a Unix/Linux host machine, opens a serial port
that is expected to be connected to the RVTMUX UART on the target, and then
speaks TI's binary packet protocol on that serial port.  It then performs two
functions:

* If rvinterf is run in the foreground in a terminal window (or more precisely,
  if its default terminal output is not disabled), every packet received from
  the target is decoded and printed on stdout in human-readable ASCII.  For
  some packets like TM/ETM responses this "human-readable" form is just a hex
  dump, but the trace messages which the firmware emits on its own are printed
  in truly human-readable form.  This output can also be saved to a log file.

* Rvinterf creates a local UNIX domain socket on the machine it is running on,
  and other host tools can then connect to this socket to exchange packets with
  the firmware.  Client programs connected to rvinterf via this local socket
  interface can register to receive copies of packets sent by the target on
  specific RVTMUX channels, and they can also send arbitrary packets to the
  target.

Our main "client" programs for actively interacting with running firmwares via
rvinterf are:

fc-tmsh		This utility speaks the TM/ETM protocol.  It supports almost
		all ETM and L1TM commands that are supported by our reference
		TCS211 fw with the important exception of TMFFS; support means
		that fc-tmsh can issue these commands and decode the firmware's
		responses to them.  fc-tmsh operates asynchronously in that the
		issuance of commands to the target and the display of firmware
		responses are completely decoupled; this asynchronous model is
		a good match for L1/RF test mode commands and simple ETM
		operations, but is a poor fit for FFS manipulation.  fc-tmsh's
		companion fc-fsio implements FFS access via TMFFS2, and we
		don't have a host side implementation for TI's older TMFFS1
		protocol.

fc-fsio		This utility speaks the TMFFS2 protocol over the TM/ETM RVTMUX
		channel (same channel as used by fc-tmsh, so don't try to run
		both at the same time) and implements fairly high-level FFS read
		and write operations.  fc-fsio is used to format and initialize
		the FFS on newly made devices in our hardware manufacturing
		environment, it can upload files or entire subtrees into target
		device FFS, it has higher-level commands for writing some files
		like the IMEI, rfcap and AT+CGxx ID strings, and it can list and
		read out FFS content.  Unlike fc-tmsh, fc-fsio is synchronous:
		it is built on command-response (send a command and expect a
		response) primitives, and a single user command can turn into a
		large number of command-response exchanges on the RVTMUX
		interface.  fc-fsio also implements a few non-FFS commands
		because they naturally fit into this ETM synchronous model.

fc-shell	This tool is asynchronous like fc-tmsh, but instead of talking
		and listening on the TM/ETM RVTMUX channel, it talks and listens
		on GPF's channel and on the new AT-over-RVTMUX channel which we
		added in FreeCalypso.  fc-shell can be used to issue system
		primitive commands to GPF (and to see firmware responses to
		them), and to talk AT commands via RVTMUX.

Finally, if you only need to passively observe the firmware's debug trace output
and don't need to make any active pokes at the target, our rvtdump utility is a
stripped-down version of rvinterf (or historically its predecessor) that only
decodes and prints/logs the output from the target without sending anything to
it.

Further reading
===============

Believe it or not, some of the documentation that was written by the original
vendors of the software in question and which we've been able to locate turns
out to be fairly relevant and helpful, such that I recommend reading it.

Documentation for Nucleus PLUS RTOS:

	ftp://ftp.freecalypso.org/pub/embedded/Nucleus/nucleus_manuals.tar.bz2

	Quite informative, and fits our version of Nucleus just fine.

Riviera environment:

	ftp://ftp.freecalypso.org/pub/GSM/Calypso/riviera_preso.pdf

	It's in slide presentation form, not a detailed technical document, but
	it covers a lot of points, and all that Riviera stuff described in the
	preso *is* present in our fw for real, hence it should be considered
	relevant.

GPF documentation:

	https://www.freecalypso.org/LoCosto-docs/SW%20doc/frame_users_guide.pdf
	https://www.freecalypso.org/LoCosto-docs/SW%20doc/vsipei_api.pdf

	Very good reading, helped me understand GPF when I first reached this
	part of firmware reintegration.

TCS3.x/LoCosto fw architecture:

	https://www.freecalypso.org/LoCosto-docs/SW%20doc/TCS2_1_to_3_2_Migration_v0_8.pdf
	ftp://ftp.freecalypso.org/pub/GSM/LoCosto/LoCosto_Software_Architecture_Specification_Document.pdf

	These TI docs focus mostly on how they changed the fw architecture from
	their TCS2.x program (Calypso) to their newer TCS3.x (LoCosto), but one
	can still get a little insight into the "old" TCS211 architecture they
	were moving away from, which is the architecture we've adopted for
	FreeCalypso.