Libtsctl Initialization Scheme

From embeddedTS Manuals

The libtsctl sub-system initializes itself on demand, deferring initialization of resources until they are requested or needed by a resource that has been requested. All initialization is initiated by a user call to one of the class Init functions (DIOInit, CANInit, etc.) All class init functions are funneled through the ClassInit function in Arch.c, which initializes by the given class number (an enum) and an instance number. The ClassInit function first calls ArchInit to obtain an instance of the Arch object for the top-level piece of hardware present - this will be the board that has the main CPU on it.

The ArchInit function detects the top-level architecture and calls the corresponding initialization function for that architecture. The ts.c file contains most of the code that actually detects the various CPU and model IDs. The Arch.c file uses conditional defines (#ifdef) to include support for various platforms. The build process constructs these defines from the SUPPORT variable in the makefile, which also can be passed on the command line. In this way it is trivial to include or exclude support for various architectures for any given build.

After the top-level architecture has been detected and returned, ClassInit then tries (returning NULL if it fails) to find the requested instance of the specified class by iterating through the functions returned by the Function function of the Arch class. The basic idea is that any given system may be comprised of multiple architectures which combine to form a given platform. For instance, a TS-4200 CPU board might be connected to a TS-8160 base board which in turn has a TS-RELAY8 PC-104 board attached. Each of these boards would be a separate architecture, and combined they would form the platform TS-4200 + TS-8160 + TS-RELAY8. The Arch class encapsulates functions which return information about the architecture:

void *Function(int class,int inst);

This function returns all the class initialization function for the given instance of the given class. For instance, passing class=ClassSystem and inst=0 will return a function to call to initialize and return a pointer to instance 0 of the System class. By iterating through the class and instance values it is possible to obtain all initialization functions for a class.

Arch *NextSubArch();

This function returns the next (sub)architecture in the platform. All the architectures in the platform are chained together to form a linked list of sorts. This function allows one to iterate through this list.

void *NextSubSetSet(Arch *);

This function is for internal use. During initialization, an architecture will call this function in its sub-architecture objects to set up the chain. This function should not be called outside of initialization.

int CANBusId(int inst);

This function returns the instance of the Bus class containing the CAN registers for the specified CAN instance.

The instance number of a given class stack so that instances implemented by the top-level (CPU) Arch are followed by the instances implemented by the sub-architectures. So for example if the CPU Arch implements 5 busses they would be accessible using BusInit(n) where n is between 0 and 4. The first Bus implemented by the next sub-architecture would be n=5, and so on. In addition, sub-architectures can override instances of their parent. This is typically done in the Pin class, where an architecture like a base board must override certain pins to enable them (e.g. CAN on the TS-81X0) but pass other requests through to the parent class. Internally, this is done by negative instances: ClassInit will first initialize a class, then pass object to the negative value (-1-n) function of the sub-architecture, if it exists, which returns the new class to continue up the chain.

During the actual initialization of the top-level architecture, the architecture initialization function is responsible for detecting and initializing any potential sub-architectures. Some boards do not have any possible sub-architectures and thus do not do this. At the moment the only sub-architecture that can be detected is the TS-SOCKET baseboard. This is accomplished by the top-level architecture (which must be a TS-SOCKET CPU board) calling the ArchBBInit() function in Arch.c. This function detects if a supported baseboard is present, and if so, it calls the appropriate base board initialization function and returns the Arch object for that base board. Likewise, the Base Board initialization function must detect and initialize any sub-architectures it supports. The only sub-architecture for the TS-SOCKET baseboards is currently the PC-104 peripheral board. The ArchPC104Init() function must be called by the Base Board initialization function to detect and initialize any PC-104 boards. This function detects and initializes all supported PC-104 boards, links them, and in addition adds dummy objects in order to fix PC-104 DIO and other objects in fixed positions based on their I/O address. For instance, there can be up to 4 TS-RELAY8 boards, so if one is found at the second I/O address, a dummy instance is put in the first I/O address so that adding a new board at the first address will not cause DIO numbers to change.

All architecture initialization functions should only run once, then save their return value in a local static variable so that subsequent calls merely returns the already initialized object. The except to this is if initialization fails, in which case a subsequent call will try again.