![[MAT logo]](../../images/MAT-linux-h100.gif)
IO modules are all very similar, except for the way they access the hardware, and the format of how that hardware's IO addresses are specified in the configuration file. That is why Mario wrote an IO library to be used by IO modules. It essentially has everything required by an IO module (including the main() function), except for the functions to access the hardware and interpret the hardware's addresses specified in the config file.
What you need to do is write the functions listed below (the precise
definition is in the lib/io/io_hw.h file), and link everything
together, and presto, you have an IO module!
The I/O library will call the your functions in the following order:
io_hw_parse_config()
io_hw_parse_io_addr()
io_hw_init()
while (1)io_hw_read()
io_hw_read_end()
plc_update() is called at this point)
io_hw_write()
io_hw_write_end()
io_hw_done()
plc_init(), plc_update(),
plc_scan_beg/end() and plc_done() functions will
be called for you at the appropriate times.
Your module will be given 8 bytes per I/O address to store the details;
if this is insufficien, feel free to malloc(3) more space in
the io_hw_parse_config() and/or
io_hw_parse_io_addr() functions.
const char *IO_MODULE_DEFAULT_NAME
int io_hw_parse_config(void)
For instance, it might parse a list of devices with device parameters. The individual I/O addresses would then use the names of these devices, as given in this list.
It should also check that the size of whatever you're casting io_addr to is no more than the size of io_addr_t, like this:
if (sizeof(my_io_addr_t) > sizeof(io_addr_t)) {
plc_log_errmsg(1,"Datatype size problem.");
return -1;
}
Should return 0 on success, -1 on failure.
int io_hw_parse_io_addr(io_addr_t *io_addr,
const char *addr_stri,
dir_t dir,
int pt_len)
addr_stri, and store it in *io_addr, which is 8
bytes long. Should return 0 on success, -1 on failure.
The direction and point-length values may be used for consistency
checking. The definition of dir_t is enum { dir_in, dir_out,
dir_none }
int io_hw_init(void)
int io_hw_write(io_addr_t *io_addr, u32 value)
int io_hw_read (io_addr_t *io_addr, u32 *value)
Should return 0 on success, -1 on failure.
int io_hw_write_end(void)
int io_hw_read_end (void)
return 0;
Should return 0 on success, -1 on failure.
int io_hw_done(void)
int io_hw_dump_config(int debug_level)
Should return 0 on success, -1 on failure.
char *io_hw_ioaddr2str(io_addr_t *io_addr)
malloc()'d, and will be free()'d
by the calling function in the io library.
If the module is not suited to the standard scan cycle, as for instance
many network and bus slaves aren't, this can be over-ridden by setting the
run_loop callback.
Just write a function the way you want it, and in one of the setup
functions, set run_loop to point to it, thus:
int my_run_loop(void*foo) {
...
}
int io_hw_init(void) {
run_loop = my_run_loop;
...
}
The function my_run_loop() should return a negative value
on error. It is not expected to return on success, but if it does, the
value should be non-negative. The parameter is an internal structure,
access to which has not been thought through at this point :-) We apologise
for the inconvenience.
plc_pt_t io_status_pt(const char *base, const char *suffix, int loglevel)
This function looks for a point called base.suffix. If it
exists, it's returned. If it doesn't exist, a warning is logged at
loglevel, and a null point is returned.
The upshot is that if an optional status point is desired, one simply calls this function and uses the returned point as the status point. The user will take advantage of it, or not.
The base should be the name of the point to which the
status point will refer. The suffix should indicate what kind
of status is reported: it might be "ok", "err",
"timeout", "errno", or similar, depending on what
condition is to be indicated by the status point.
To run in slave mode, where it responds to requests from the bus... FIXME
$Date: 2003/08/23 04:32:26 $