------ A BRIEF INTRODUCTION ------
----------------------------------
Hi guys!!! Only this: my english is orrible! And I'm looking for someone to
translate my work. If you feel to do this, contact me: minkiux@texin.it
This README cover this aspect of TxEngine:
- Installation note;
- How to run it;
- How to write a module for this toy;
- How to write a useful module for this toy :)
- Other nice stuff;
------- INSTALLATION NOTE: -------
----------------------------------
If you won't compile TxEngine you are
Due to my ignorance, to compile the program you must write:
./configure
make -k
and forget errors that the operation reports.
Ah...I used uClibc to compile the programs, so you would use standard gcc with:
./configure CC=gcc
-------- HOW TO RUN IT --------
--------------------------------
Woowwww!! If you get here, you are the Master of the Universe!!!
No one (me apart) was capable to compile TxEngine without problems... :)
Hmmm...now we start.
Fundamentally TxEngine expects to find some modules in ./moduli directory
to run. These modules are simply .so objects compiled from a c source.
Source directory of the project contains some examples that I supplied.
These examples cover some basic aspects and give us an idea of the framework.
If there is someone of you that need to run *NOW* the program, I prepared
precomiled binary of both TxEngine and modules. You find them in bin/ directory.
(Only on -bin- package!).
----- HOW TO WRITE A MODULE FOR THIS TOY -----
----------------------------------------------
Your question now is how to write a module, of course. Yeah!
Follow me and my horrend english in this quick tutorial.
Now we start with an empty module. We need to follow a base scheme
that you will find in templates/ directory. Open it and you get your
first module source!!!
Analyze that (or PARSE...): on top we found the obviosly standard #include
directives (few, peraphs. Just we need printf()). Then some global structures
and (the main thing) functions. We focalize on theese.
Suppose that you want to write a simple C program: first write main()
function and then other stuff. To write a module you must think that the
main function is suppressed by load() function. Prototipe is:
int load(struct Kernel *kernel, struct Mod *new_mod)
HU?? What is this?!?!?
Naaa, nothing! Structs Kernel and Mod are defined in tx.engine.h and
scheduler.h. The first is what you will use to access TxEngine API.
The second is a pointer to a structure that represent your module.
Template file that you found in templates/ is a bit complicate for
our needs. So let me write a very simple working module:
/*------------- dummy_mod.c ---------------------*/
#include <stdio.h> /* for printf() */
#include "tx.engine.h" /* our shiny TxEngine! */
int load(struct Kernel *kernel, struct Mod *new_mod)
{
printf("Hello world!\n");
return 0;
}
/*-----------------------------------------------*/
Oh no! Yet another "hello world" program!!
Sorry, but this is the best I can do!
Compile it with:
gcc -fPIC -shared -o dummy_mod.so dummy_mod.c
We created a simple shared object. Now copy this in moduli/ directory
(remember that TxEngine search modules in ./moduli directory) and put
fire on dynamite!
./tx.engine-0.7.03 -debug
-debug option allow execution to ordinary user and not only root.
Output of this command is like this:
----------------------------------------------------------------
> tx.Engine - ver. 0.7.03
> build: #76 - 3 Nov 2003
> author: Davide Matarese (davide.matarese@texin.it)
----------------------------------------------------------------
Calibrating delay loop...done - delay=0.0000039290 sec. [correction=251.399]
version=0.7.03
author=Davide Matarese (davide.matarese@texin.it)
max_interfaces=8
Search modules...
Loading dummy_mod.so... Hello world!
Initializing modules...
done
--- INSTALLED MODULES ------------------------------------------------
| id=0 type=0x0000 flag=0 load_order=0 kernel
----------------------------------------------------------------------
(tx.Engine kernel): run...
Ok! You got the skill to run a module! But someone (if not all) is in
trouble.
"Why I need a product like this if it only replaces main() ???"
Yeah! Right! GO TXENGINE!
----- HOW TO WRITE A USEFUL MODULE FOR THIS TOY -----
-----------------------------------------------------
Now that we know how to start a module, we go to RUN a module! :)
In previous example we simply LOADED our module. Life of module is
essentially subdivided in:
- LOAD phase
- INIT phase
- RUN phase
- CLEANUP phase
If RUN and CLEANUP can be clear, you are in trouble about the distinction
LOAD/INIT.
LOAD phase is intended for DEFINITION and ALLOCATION of user structures.
In particular definition is main thing LOAD is designed. Mean of definition
is setting some module attributes. Things like type, flags and function
pointer.
INIT phase is intended for initialization of data, resource (like serial port)
and simila. Because this operation is done AFTER that all modules are installed,
is mandatory to include here (and not in LOAD) operations that affects other
modules. Keep in mind that the work of your application is subdivided across more
modules.
Ok, go on the next step: let me adding some trash code in our beautiful load()
function...
/*------------- dummy_mod2.c ---------------------*/
#include <stdio.h> /* for printf() */
#include "tx.engine.h" /* our shiny TxEngine! */
int load(struct Kernel *kernel, struct Mod *new_mod)
{
printf("Hi girl, my id is %d\n", kernel->loader->insertModule(new_mod));
return 0;
}
/*-----------------------------------------------*/
Compile this source like before and run txEngine. Now you can see interesting things.
Note that "INSTALLED MODULES" shows your module:
--- INSTALLED MODULES ------------------------------------------------
| id=0 type=0x0000 flag=0 load_order=0 kernel
| id=1 type=0x0000 flag=0 load_order=0 ./moduli/dummy_mod2.so
----------------------------------------------------------------------
This because we called kernel->loader->insertModule(). This is the first API function
we encountered. Note that all API is accessible by Kernel structure. Extensive discussion
on this object is not my target, now. Instead we go through the explanation of other
things such as Mod struct and other related...
Struct Mod is defined like:
struct Mod
{
struct Mod *next;
void *handle;
char *path;
unsigned int id;
unsigned int type;
unsigned int sub_type;
unsigned char flag;
float p_time;
int load_order;
int sched_count;
int fail_count;
pthread_t thread;
void *object;
void *comm_int[8];
void *user_struct[8];
void (*init)();
void (*run)();
void (*cleanup)();
void (*event)(unsigned int, void *);
};
Most of it's attributes is not of our interest. Note the function pointers. They are
the same we discussed before. Now we go to implement some of theese.
/*------------- dummy_mod3.c ---------------------*/
#include <stdio.h> /* for printf() */
#include "tx.engine.h" /* our shiny TxEngine! */
static void run()
{
printf("RUUUUUUUUN\n");
}
static void init()
{
printf("Init time!\n");
}
static void cleanup()
{
printf("cleanup time!\n");
}
int load(struct Kernel *kernel, struct Mod *new_mod)
{
new_mod->type = 1;
new_mod->init = init;
new_mod->run = run;
new_mod->cleanup = cleanup;
printf("Hi girl, my id is %d\n", kernel->loader->insertModule(new_mod));
return 0;
}
/*-----------------------------------------------*/
Compile and run. AHAHAHAHA!!!! YES, this is cool!! (Uh?)
What did we do? Nothing! A simple implementation of load, init, run, cleanup phases.
But...why I need to set type = 1? What is type?
Type is simple a way for user to subdivide modules in various categories.
TxEngine install itself such a module with type 0. Scheduler do not schedule this
type of special module (do not call run()), so we need to define our module type with
other number!
With run defined, internal scheduler of TxEngine will work to recall run() for ever.
Note that you can install many modules like this, and all execute correctly in a
simil-threaded fashion.
---------------------- OTHER NICE STUFF ------------------------
----------------------------------------------------------------
It's time to spend some words about all the features of TxEngine. Some basic concept
I thinked in development.
First at all I'll try to make an ascii art... :-)
_________
---------->| Mod. C|------> Phisic device
__________ _____|___ ---^-----
| Mod. A |- - > | Mod. B| |
/---------|----------^---------\ |
| |__________| _ _ _ _ | _ _ _ |
| TxEngine |
\______________________________/
This scheme represent all ways to interface with other modules or phisical devices.
In words: I had in mind to develop a LAYERED and modularized mechanism to allow re-use
of single module.
An example: do you want to develop a system that has some digital I/O following a logic.
Yes, you can do all in a single big module that do everything, but isn't so beautiful.
So you can divide job into various modules: one assigned to simple do I/O on a device,
one assigned as interface to a logic module and all the I/O modules (in case you have
many I/O modules) and finally the logic assigned module. You refer to scheme. Think that
our I/O module is Mod. C, I/O interface module is Mod. B and logic module is Mod. A.
We want to isolate logic from hardware-related stuff, so we deliver all I/O job to Mod. B.
This, finally, select correct I/O module based on the out index, or so on and deliver I/O
commands to this.
You note that to do all this work we need some communication between modules. I previewed
this ways to allow this:
- direct communication via object struct exported by modules;
- direct communication via comm_int (common interface) struct implemented/exported by
modules;
- TxEngine assisted communication through an event based infrastructure.
Now I explain all these methods.
--------- MODULE COMMUNICATION VIA OBJECT STRUCTURE -----------
---------------------------------------------------------------
Previously we have seen Mod struct containing a "void *object". What is it? Generally you
want to export some features implemented by your module. Suppose that you have a module doing
a series of printf() and you want to make avaiable this work to other modules. Well, you need
to implement "object". Due to its void pointer nature, you can assign any thing, but is
interesting to assign a pointer to a structure defined by module itself. You can fairly imagine
"object" like objects in Java/C++. OOOOOHHH!!!! I shot high!!! The *ONLY* ( :-) difference is
that you must explicitly define the object structure and function pointers to our public methods.
Write down this:
/*------------- dummy_mod4.c ---------------------*/
#include <stdio.h> /* for printf() */
#include "tx.engine.h" /* our shiny TxEngine! */
/* We define the struct that represent our module*/
struct My_module
{
unsigned int mid;
void (*my_func)();
} this; // ...and we call it this!!!
/* This is a public method */
static void my_func()
{
printf("Welcome to My_module!!!\n");
printf("Ahh...my ID is %u.\n", this.mid);
printf("\t...and you are in my_func()\n");
}
int load(struct Kernel *kernel, struct Mod *new_mod)
{
new_mod->type = 1;
new_mod->object = &this;
this.mid = kernel->loader->insertModule(new_mod));
this.my_func = my_func;
return 0;
}
/*-----------------------------------------------*/
Now we need another module to speak with My_module. Note that to do this the caller module
need to know struct My_module definition and Type. First is needed because we must cast void *
to appropriate struct pointer, letter is needed to tell TxEngine which module we want. So is
convenient write a .h file for our module and rewrite it:
/*------------- my_module.h ---------------------*/
#ifndef __MY_MODULE__
#define __MY_MODULE__
#define TYPE_MY_MODULE 1
struct My_module
{
unsigned int mid;
void (*my_func)();
};
#endif
/*-----------------------------------------------*/
/*------------- my_module.c ---------------------*/
#include <stdio.h> /* for printf() */
#include "tx.engine.h" /* our shiny TxEngine! */
#include "my_module.h"
static struct My_module this;
/* This is a public method */
static void my_func()
{
printf("Welcome to My_module!!!\n");
printf("Ahh...my ID is %u.\n", this.mid);
printf("\t...and you are in my_func()\n");
}
int load(struct Kernel *kernel, struct Mod *new_mod)
{
new_mod->type = 1;
new_mod->object = &this;
this.mid = kernel->loader->insertModule(new_mod));
this.my_func = my_func;
return 0;
}
/*-----------------------------------------------*/
Ok!!! Now we can write the caller module!
/*------------- caller.c ---------------------*/
#include <stdio.h> /* for printf() */
#include "tx.engine.h" /* our shiny TxEngine! */
#include "my_module.h"
static struct Scheduler *scheduler;
static struct Loader *loader;
static void init()
{
struct Mod *found_mod;
struct My_module *my_mod = NULL;
if ((found_mod = loader->searchByType(scheduler->mod_list, TYPE_MY_MODULE)))
my_mod = found_mod->object;
if (my_mod)
{
my_mod->my_func();
}
}
int load(struct Kernel *kernel, struct Mod *new_mod)
{
scheduler = kernel->scheduler;
loader = kernel->loader;
new_mod->type = 2;
new_mod->init = init;
kernel->loader->insertModule(new_mod));
return 0;
}
/*-----------------------------------------------*/
Try to compile and run. You got what you aspect, caller module invoke my_func() and
all play correctly. Put an eye on the caller code. There we see a couple of strange
things! One is "scheduler" and "loader" pointers. Are necessary to access searchByType()
API function of TxEngine. This is standard way to access it. Simply save API objects
of interest into appropriate struct pointer and then use it though a simple pointer
reference.
In caller.c we use loader->searchByType to find our module. If there is, a pointer to it
is returned, NULL otherwise. Pointer is struct Mod type, so we need to extract object fiels
and cast it (I'm doing implicitly). So we have got a reference to My_module object!