Crate linuxcnc_hal_sys

source ·
Expand description

This crate provides generated bindings for LinuxCNC’s HAL using bindgen.

The high level, safe interface at linuxcnc-hal is recommended for user code.

Development setup

bindgen must be set up correctly. Follow the requirements section of its docs.

To run and debug any HAL components, the LinuxCNC simulator can be set up. There’s a guide here for Linux Mint (and other Debian derivatives).

Project setup

The LINUXCNC_SRC environment variable is required to build this crate. The value must be the absolute path to the root of the LinuxCNC source code.

The version of the LinuxCNC sources must match the LinuxCNC version used in the machine control.

git clone https://github.com/LinuxCNC/linuxcnc.git

cd linuxcnc && git checkout v2.8.0 && cd ..

cargo new --lib my_comp

cd my_comp

cargo add linuxcnc-hal-sys

LINUXCNC_SRC=/path/to/linuxcnc/source/code cargo build

Examples

Running the examples in the LinuxCNC simulator

Ensure you have the LinuxCNC source repository cloned, checked out to the desired version and built with the build instructions.

Note that the LinuxCNC source is located in the same parent directory as linuxcnc-hal-rs in the example paths below.

LINUXCNC_SRC=$(realpath ../linuxcnc) cargo build --examples

. ../linuxcnc/scripts/rip-environment

linuxcnc ./linuxcnc-hal-sys/examples/<example>.ini

All functions exported from this crate are unsafe, hence each example is wrapped in a big unsafe block for clarity.

The LinuxCNC HAL requires a certain setup procedure to operate correctly. The basic program structure should be roughly as follows:

  1. Call hal_init to create a new HAL component
  2. Register SIGTERM and SIGINT signals, likely with the signal_hook crate. LinuxCNC will hang if these signals are not registered.
  3. Register pins with hal_pin_float_new, hal_pin_u32_new, etc
  4. Call hal_ready to signal to LinuxCNC that the component is ready
  5. Enter an infinite loop to continuously update input/output pin values and perform component logic

These examples can be loaded into LinuxCNC using a HAL file similar to this:

loadusr -W /path/to/your/component/target/debug/comp_bin_name
net input-1 spindle.0.speed-out pins.input-1

If LinuxCNC is configured to run in place, liblinuxcnchal.so.0 may not be found on startup. To fix, try setting the library path with e.g. export LD_LIBRARY_PATH=~/Repositories/linuxcnc/lib

Create an input pin

This example creates a component called pins and registers an input pin to it that accepts a floating point value using hal_pin_float_new. Each HAL pin requires some memory allocated to store its value which is performed with hal_malloc.

The example can be loaded into LinuxCNC using a HAL file similar to this:

Note that there is no error handling in this example for brevity.

use linuxcnc_hal_sys::*;
use signal_hook::iterator::Signals;
use std::ffi::CString;
use std::mem;
use std::thread;
use std::time::Duration;

unsafe {
    let id = hal_init(CString::new("pins").unwrap().as_ptr() as *const u8);

    println!("ID {}", id);

    let mut signals = Signals::new(&[signal_hook::consts::SIGTERM, signal_hook::consts::SIGINT]).unwrap();

    let storage = hal_malloc(mem::size_of::<*mut f64>() as i64) as *mut *mut f64;

    println!("Storage {:?}", storage);

    let pin_name = CString::new("pins.input-1").unwrap();

    let ret = hal_pin_float_new(
        pin_name.as_ptr() as *const u8,
        hal_pin_dir_t_HAL_IN,
        storage,
        id,
    );

    println!("Pin init {}", ret);

    let ret = hal_ready(id);

    println!("Ready {}", ret);

    while !signals.pending().any(|signal| match signal {
        signal_hook::consts::SIGTERM | signal_hook::consts::SIGINT | signal_hook::consts::SIGKILL => true,
        _ => false,
    }) {
        println!("Input {:?}", **storage);

        thread::sleep(Duration::from_millis(500));
    }
}

Error handling

Errors are handled in this crate the same way as in the C code. Some consts are exported like EINVAL and EPERM to allow matching of returned error codes.

use linuxcnc_hal_sys::*;
use signal_hook::iterator::Signals;
use std::ffi::CString;
use std::mem;
use std::thread;
use std::time::Duration;

unsafe {
    let ret = hal_init(CString::new("pins").unwrap().as_ptr() as *const u8);

    // Check that component was created successfully
    let component_id = match ret {
        x if x == -(EINVAL as i32) => panic!("Failed to initialise component"),
        x if x == -(ENOMEM as i32) => panic!("Not enough memory to initialise component"),
        id if id > 0 => id,
        code => unreachable!("Hit unreachable error code {}", code),
    };

    println!("Component registered with ID {}", component_id);

    let mut signals = Signals::new(&[signal_hook::consts::SIGTERM, signal_hook::consts::SIGINT]).unwrap();

    let storage = hal_malloc(mem::size_of::<*mut f64>() as i64) as *mut *mut f64;

    if storage.is_null() {
        panic!("Failed to allocate storage");
    }

    let pin_name = CString::new("pins.input-1").unwrap();

    let ret = hal_pin_float_new(
        pin_name.as_ptr() as *const u8,
        hal_pin_dir_t_HAL_IN,
        storage,
        component_id,
    );

    // Check that pin was registered successfully
    match ret {
        0 => println!("Pin registered successfully"),
        x if x == -(EINVAL as i32) => panic!("Failed to register pin"),
        x if x == -(EPERM as i32) => {
            panic!("HAL is locked. Register pins before calling hal_ready()`")
        }
        x if x == -(ENOMEM as i32) => panic!("Failed to register pin"),
        code => unreachable!("Hit unreachable error code {}", code),
    }

    let ret = hal_ready(component_id);

    // Check that component is ready
    match ret {
        0 => println!("Component is ready"),
        x if x == -(EINVAL as i32) => panic!("HAL component was not found or is already ready"),
        code => unreachable!("Hit unreachable error code {}", code),
    }

    while !signals.pending().any(|signal| match signal {
        signal_hook::consts::SIGTERM | signal_hook::consts::SIGINT | signal_hook::consts::SIGKILL => true,
        _ => false,
    }) {
        println!("Input {:?}", **storage);

        thread::sleep(Duration::from_millis(500));
    }
}

Structs

Constants

Functions

hal_add_funct_to_thread() adds a function exported by a realtime HAL component to a realtime thread. This determines how often and in what order functions are executed. ‘funct_name’ is the name of the function, as specified in a call to hal_export_funct(). ‘thread_name’ is the name of the thread to which the function should be added. When the thread runs, the functions will be executed in the order in which they were added to the thread. ‘position’ is the desired location within the thread. This determines when the function will run, in relation to other functions in the thread. A positive number indicates the desired location as measured from the beginning of the thread, and a negative is measured from the end. So +1 means this function will become the first one to run, +5 means it will be the fifth one to run, -2 means it will be next to last, and -1 means it will be last. Zero is illegal. Returns 0, or a negative error code. Call only from within user space or init code, not from realtime code.
hal_comp_name() returns the name of the given component, or NULL if comp_id is not a loaded component
hal_create_thread() establishes a realtime thread that will execute one or more HAL functions periodically. ‘name’ is the name of the thread, which must be unique in the system. It must be no longer than HAL_NAME_LEN. ‘period_nsec’ is the desired period of the thread, in nano- seconds. All threads must run at an integer multiple of the fastest thread, and the fastest thread must be created first. In general, threads should be created in order, from the fastest to the slowest. HAL assigns decreasing priorities to threads that are created later, so creating them from fastest to slowest results in rate monotonic priority scheduling, usually a good thing. ‘uses_fp’ should be non-zero if the thread will call any functions that use floating point. In general, it should be non-zero for most threads, with the possible exception of the very fastest, most critical thread in a system. On success, hal_create_thread() returns a positive integer thread ID. On failure, returns an error code as defined above. Call only from realtime init code, not from user space or realtime code.
hal_del_funct_from_thread() removes a function from a thread. ‘funct_name’ is the name of the function, as specified in a call to hal_export_funct(). ‘thread_name’ is the name of a thread which currently calls the function. Returns 0, or a negative error code. Call only from within user space or init code, not from realtime code.
‘hal_exit()’ must be called before a HAL component exits, to free resources associated with the component. ‘comp_id’ is the ID of the component as returned from its initial call to ‘hal_init()’. ‘hal_exit()’ will remove the component’s realtime functions (if any) from realtime threads. It also removes all pins and parameters exported by the component. If the component created any threads, when it exits all threads will be stopped, and the ones it created will be deleted. It is assumed that the system will no longer function correctly after a component is removed, but this cleanup will prevent crashes when the component’s code and data is unmapped. ‘hal_exit()’ calls ‘rtapi_exit()’, so any rtapi reaources allocated should be discarded before calling hal_exit(), and rtapi functions should not be called afterwards. On success, hal_exit() returns 0, on failure it returns a negative error code.
hal_export_funct() makes a realtime function provided by a component available to the system. A subsequent call to hal_add_funct_to_thread() can be used to schedule the execution of the function as needed by the system. ‘name’ is the name of the new function. It must be no longer than HAL_NAME_LEN. This is the name as it would appear in an ini file, which does not need to be the same as the C function name. ‘funct’ is a pointer to the function code. ‘funct’ must be the address of a function that accepts a void pointer and a long int. The pointer will be set to the value ‘arg’ below, and the long will be set to the thread period in nanoseconds. ‘arg’ is a void pointer that will be passed to the function each time it is called. This is useful when one actual C function will be exported several times with different HAL names, perhaps to deal with multiple instances of a hardware device. ‘uses_fp’ should be non-zero if the function uses floating point. When in doubt, make it non-zero. If you are sure that the function doesn’t use the FPU, then set ‘uses_fp’ to zero. ‘reentrant’ should be zero unless the function (and any hardware it accesses) is completely reentrant. If reentrant is non-zero, the function may be prempted and called again before the first call completes. ‘comp_id’ is the ID of the calling component, as returned by a call to hal_init(). On success, hal_export_funct() returns 0, on failure it returns a negative error code. Call only from realtime init code, not from user space or realtime code.
The ‘hal_get_lock()’ function returns the current locking level locking types defined in hal.h HAL_LOCK_NONE -locks none HAL_LOCK_* - intermediate locking levels HAL_LOCK_ALL - locks everything
‘hal_get_param_value_by_name()’ returns the value of any arbitrary HAL parameter by parameter name.
‘hal_get_pin_value_by_name()’ gets the value of any arbitrary HAL pin by pin name.
‘hal_get_signal_value_by_name()’ returns the value of any arbitrary HAL signal by signal name.
‘hal_init()’ is called by a HAL component before any other hal function is called, to open the HAL shared memory block and do other initialization. ‘name’ is the name of the component. It must be unique in the system. It must be no longer than HAL_NAME_LEN. On success, hal_init() returns a positive integer component ID, which is used for subsequent calls to hal_xxx_new() and hal_exit(). On failure, returns an error code (see above). ‘hal_init()’ calls rtapi_init(), so after calling hal_init(), a component can use any rtapi functions. The component ID returned by ‘hal_init()’ is also the RTAPI module ID for the associated module, and can be used when calling rtapi functions. Call only from within user space or init/cleanup code, not from realtime code.
‘hal_link()’ links a pin to a signal. ‘pin_name’ and ‘sig_name’ are strings containing the pin and signal names. If the pin is already linked to the desired signal, the command succeeds. If the pin is already linked to some other signal, it is an error. In either case, the existing connection is not modified. (Use ‘hal_unlink’ to break an existing connection.) If the signal already has other pins linked to it, they are unaffected - one signal can be linked to many pins, but a pin can be linked to only one signal. On success, hal_link() returns 0, on failure it returns a negative error code.
hal_malloc() allocates a block of memory from the main HAL shared memory area. It should be used by all components to allocate memory for HAL pins and parameters. It allocates ‘size’ bytes, and returns a pointer to the allocated space, or NULL (0) on error. The returned pointer will be properly aligned for any variable HAL supports (see HAL_TYPE below.) The allocator is very simple, and there is no ‘free’. It is assumed that a component will allocate all the memory it needs during initialization. The entire HAL shared memory area is freed when the last component calls hal_exit(). This means that if you continuously install and remove one component while other components are present, you eventually will fill up the shared memory and an install will fail. Removing all components completely clears memory and you start fresh.
‘hal_param_alias()’ assigns an alternate name, aka an alias, to a parameter. Once assigned, the parameter can be referred to by either its original name or the alias. Calling this function with ‘alias’ set to NULL will remove any existing alias.
The ‘hal_param_xxx_new()’ functions create a new ‘parameter’ object. A parameter is a value that is only used inside a component, but may need to be initialized or adjusted from outside the component to set up the system properly. Once a parameter has been created, it’s value can be changed using the ‘hal_param_xxx_set()’ functions. There are eight functions, one for each of the data types that the HAL supports. Pins may only be linked to signals of the same type. ‘name’ is the name of the new parameter. It must be no longer than .HAL_NAME_LEN. If there is already a parameter with the same name the call will fail. ‘dir’ is the parameter direction. HAL_RO parameters are read only from outside, and are written to by the component itself, typically to provide a view “into” the component for testing or troubleshooting. HAL_RW parameters are writable from outside and also sometimes modified by the component itself as well. ‘data_addr’ is the address where the value of the parameter is to be stored. ‘data_addr’ must point to memory allocated by hal_malloc(). Typically the component allocates space for a data structure with hal_malloc(), and ‘data_addr’ is the address of a member of that structure. Creating the parameter does not initialize or modify the value at *data_addr - the component should load a reasonable default value. ‘comp_id’ is the ID of the component that will ‘own’ the parameter. Normally it should be the ID of the caller, but in some cases, a user mode component may be doing setup for a realtime component, so the ID should be that of the realtime component that will actually be using the parameter. If successful, the hal_param_xxx_new() functions return 0. On failure they return a negative error code.
printf_style-style versions of hal_param_XXX_new
The ‘hal_param_xxx_set()’ functions modify the value of a parameter. ‘name’ is the name of the parameter that is to be set. The parameter type must match the function type, and the parameter must not be read-only. ‘value’ is the value to be loaded into the parameter. On success, the hal_param_xxx_set() functions return 0, and on failure they return a negative error code.
‘hal_param_new()’ creates a new ‘parameter’ object. It is a generic version of the eight functions above. It is provided ONLY for those special cases where a generic function is needed. It is STRONGLY recommended that the functions above be used instead, because they check the type of ‘data_addr’ against the parameter type at compile time. Using this function requires a cast of the ‘data_addr’ argument that defeats type checking and can cause subtle bugs. ‘name’, ‘data_addr’ and ‘comp_id’ are the same as in the functions above. ‘type’ is the hal type of the new parameter - the type of data that will be stored in the parameter. ‘dir’ is the parameter direction. HAL_RO parameters are read only from outside, and are written to by the component itself, typically to provide a view “into” the component for testing or troubleshooting. HAL_RW parameters are writable from outside and also sometimes modified by the component itself as well. If successful, hal_param_new() returns 0. On failure it returns a negative error code.
‘hal_param_set()’ is a generic function that sets the value of a parameter. It is provided ONLY for those special cases where a generic function is needed. It is STRONGLY recommended that the functions above be used instead, because they are simpler and less prone to errors. ‘name’, is the same as in the functions above. ‘type’ is the hal type of the the data at *value_addr, and must match the type of the parameter. The parameter must not be read only. ‘value_addr’ is a pointer to the new value of the parameter. The data at that location will be interpreted according to the type of the parameter. If successful, hal_param_set() returns 0. On failure it returns a negative error code.
‘hal_pin_alias()’ assigns an alternate name, aka an alias, to a pin. Once assigned, the pin can be referred to by either its original name or the alias. Calling this function with ‘alias’ set to NULL will remove any existing alias.
The ‘hal_pin_xxx_new()’ functions create a new ‘pin’ object. Once a pin has been created, it can be linked to a signal object using hal_link(). A pin contains a pointer, and the component that owns the pin can dereference the pointer to access whatever signal is linked to the pin. (If no signal is linked, it points to a dummy signal.) There are eight functions, one for each of the data types that the HAL supports. Pins may only be linked to signals of the same type. ‘name’ is the name of the new pin. It must be no longer than HAL_NAME_LEN. If there is already a pin with the same name the call will fail. ‘dir’ is the pin direction. It indicates whether the pin is an input or output from the component. ‘data_ptr_addr’ is the address of the pointer that the component will use for the pin. When the pin is linked to a signal, the pointer at ‘data_ptr_addr’ will be changed to point to the signal data location. ‘data_ptr_addr’ must point to memory allocated by hal_malloc(). Typically the component allocates space for a data structure with hal_malloc(), and ‘data_ptr_addr’ is the address of a member of that structure. ‘comp_id’ is the ID of the component that will ‘own’ the variable. Normally it should be the ID of the caller, but in some cases, a user mode component may be doing setup for a realtime component, so the ID should be that of the realtime component that will actually be using the pin. If successful, the hal_pin_xxx_new() functions return 0. On failure they return a negative error code.
The hal_pin_XXX_newf family of functions are similar to hal_pin_XXX_new except that they also do printf-style formatting to compute the pin name If successful, the hal_pin_xxx_newf() functions return 0. On failure they return a negative error code.
‘hal_pin_new()’ creates a new ‘pin’ object. It is a generic version of the eight functions above. It is provided ONLY for those special cases where a generic function is needed. It is STRONGLY recommended that the functions above be used instead, because they check the type of ‘data_ptr_addr’ against the pin type at compile time. Using this function requires a cast of the ‘data_ptr_addr’ argument that defeats type checking and can cause subtle bugs. ‘name’, ‘dir’, ‘data_ptr_addr’ and ‘comp_id’ are the same as in the functions above. ‘type’ is the hal type of the new pin - the type of data that will be passed in/out of the component through the new pin. If successful, hal_pin_new() returns 0. On failure it returns a negative error code.
hal_port_buffer_size returns the total number of bytes that a port can buffer
hal_port_clear emptys a given port of all data without consuming any of it. hal_port_clear should only be called by a reader
hal_port_peek operates the same as hal_port_read but no bytes are consumed from the input port. Repeated calls to hal_port_peek will return the same data. This function should only be called by the component that owns the IN PORT pin. returns true: count bytes were read into dest false: no bytes were read into dest
hal_port_peek_commit advances the read position in the port buffer by count bytes. A hal_port_peek followed by a hal_port_peek_commit with the same count value would function equivalently to hal_port_read given the same count value. This function should only be called by the component that owns the IN PORT pin. returns: true: count readable bytes were skipped and are no longer accessible false: no bytes wer skipped
hal_port_read reads count bytes from the port into dest. This function should only be called by the component that owns the IN PORT pin. returns true: count bytes were read into dest false: no bytes were read into dest
hal_port_readable returns the number of bytes available for reading from the port.
hal_port_writable returns the number of bytes that can be written into the port
hal_port_write writes count bytes from src into the port. This function should only be called by the component that owns the OUT PORT pin. returns: true: count bytes were written false: no bytes were written into dest
hal_ready() indicates that this component is ready. This allows halcmd ‘loadusr -W hal_example’ to wait until the userspace component ‘hal_example’ is ready before continuing.
hal_set_constructor() sets the constructor function for this component
‘hal_signal_delete()’ deletes a signal object. Any pins linked to the object are unlinked. ‘name’ is the name of the signal to be deleted. If successful, ‘hal_signal_delete()’ returns 0. On failure, it returns a negative error code.
‘hal_signal_new()’ creates a new signal object. Once a signal has been created, pins can be linked to it with hal_link(). The signal object contains the actual storage for the signal data. Pin objects linked to the signal have pointers that point to the data. ‘name’ is the name of the new signal. It must be no longer than HAL_NAME_LEN. If there is already a signal with the same name the call will fail. ‘type’ is the data type handled by the signal. Pins can only be linked to a signal of the same type. Note that the actual address of the data storage for the signal is not accessible. The data can be accessed only by linking a pin to the signal. Also note that signals, unlike pins, do not have ‘owners’. Once created, a signal remains in place until either it is deleted, or the last HAL component exits. If successful, ’hal_signal_new() returns 0. On failure it returns a negative error code.
hal_start_threads() starts all threads that have been created. This is the point at which realtime functions start being called. On success it returns 0, on failure a negative error code.
hal_stop_threads() stops all threads that were previously started by hal_start_threads(). It should be called before any component that is part of a system exits. On success it returns 0, on failure a negative error code.
attach to an existing stream
create and attach a stream
detach and destroy an open stream
detach from an open stream
stream introspection
hal_thread_delete() deletes a realtime thread. ‘name’ is the name of the thread, which must have been created by ‘hal_create_thread()’. On success, hal_thread_delete() returns 0, on failure it returns a negative error code. Call only from realtime init code, not from user space or realtime code.
‘hal_unlink()’ unlinks any signal from the specified pin. ‘pin_name’ is a string containing the pin name. On success, hal_unlink() returns 0, on failure it returns a negative error code.
hal_unready() indicates that this component is ready. This allows halcmd ‘loadusr -W hal_example’ to wait until the userspace component ‘hal_example’ is ready before continuing.
’rtapi_clock_set_period() sets the basic time interval for realtime tasks. All periodic tasks will run at an integer multiple of this period. The first call to ’rtapi_clock_set_period() with ‘nsecs’ greater than zero will start the clock, using ‘nsecs’ as the clock period in nano-seconds. Due to hardware and RTOS limitations, the actual period may not be exactly what was requested. On success, the function will return the actual clock period if it is available, otherwise it returns the requested period. If the requested period is outside the limits imposed by the hardware or RTOS, it returns -EINVAL and does not start the clock. Once the clock is started, subsequent calls with non-zero ‘nsecs’ return -EINVAL and have no effect. Calling ’rtapi_clock_set_period() with ‘nsecs’ set to zero queries the clock, returning the current clock period, or zero if the clock has not yet been started. Call only from within init/cleanup code, not from realtime tasks. This function is not available from user (non-realtime) code.
rtapi_delay() is a simple delay. It is intended only for short delays, since it simply loops, wasting CPU cycles. ‘nsec’ is the desired delay, in nano-seconds. ’rtapi_delay_max() returns the max delay permitted (usually approximately 1/4 of the clock period). Any call to ‘rtapi_delay()’ requesting a delay longer than the max will delay for the max time only. ‘rtapi_delay_max()’ should be called before using ‘rtapi_delay()’ to make sure the required delays can be achieved. The actual resolution of the delay may be as good as one nano-second, or as bad as a several microseconds. May be called from init/cleanup code, and from within realtime tasks.
‘rtapi_enable_interrupt()’ and ‘rtapi_disable_interrupt()’ are are used to enable and disable interrupts, presumably ones that have handlers assigned to them. Returns a status code. May be called from init/cleanup code, and from within realtime tasks.
‘rtapi_exit()’ shuts down and cleans up the RTAPI. It must be called prior to exit by any module that called rtapi_init. ‘module_id’ is the ID code returned when that module called rtapi_init(). Returns a status code. rtapi_exit() may attempt to clean up any tasks, shared memory, and other resources allocated by the module, but should not be relied on to replace proper cleanup code within the module. Call only from within user or init/cleanup code, not from realtime tasks.
‘rtapi_fifo_delete()’ is the counterpart to ‘rtapi_fifo_new()’. It closes the fifo associated with ‘fifo_ID’. ‘module_id’ is the ID of the calling module. Returns status code. Call only from within user or init/cleanup code, not from realtime tasks.
‘rtapi_fifo_new()’ creates a realtime fifo. ‘key’ identifies the fifo, all modules wishing to access the same fifo must use the same key. ‘module_id’ is the ID of the module making the call (see rtapi_init). ‘size’ is the depth of the fifo. ‘mode’ is either ‘R’ or ‘W’, to request either read or write access to the fifo. On success, it returns a positive integer ID, which is used for subsequent calls dealing with the fifo. On failure, returns a negative error code. Call only from within user or init/cleanup code, not from realtime tasks.
rtapi_get_clocks returns the current time in CPU clocks. It is fast, since it just reads the TSC in the CPU instead of calling a kernel or RTOS function. Of course, times measured in CPU clocks are not as convenient, but for relative measurements this works fine. Its absolute value means nothing, but it is monotonically increasing* and can be used to schedule future events, or to time the duration of some activity. (* on SMP machines, the two TSC’s may get out of sync, so if a task reads the TSC, gets swapped to the other CPU, and reads again, the value may decrease. RTAPI tries to force all RT tasks to run on one CPU.) Returns a 64 bit value. The resolution of the returned value is one CPU clock, which is usually a few nanoseconds to a fraction of a nanosecond. May be called from init/cleanup code, and from within realtime tasks.
Retrieve the message level set by the last call to rtapi_set_msg_level
rtapi_get_time returns the current time in nanoseconds. Depending on the RTOS, this may be time since boot, or time since the clock period was set, or some other time. Its absolute value means nothing, but it is monotonically increasing and can be used to schedule future events, or to time the duration of some activity. Returns a 64 bit value. The resolution of the returned value may be as good as one nano-second, or as poor as several microseconds. May be called from init/cleanup code, and from within realtime tasks.
’rtapi_inb() gets a byte from ‘port’. Returns the byte. May be called from init/cleanup code, and from within realtime tasks. Note: This function always returns zero on the simulated RTOS. Note: Many platforms provide an inline inb() that is faster.
’rtapi_init() sets up the RTAPI. It must be called by any module that intends to use the API, before any other RTAPI calls. ‘modname’ can optionally point to a string that identifies the module. The string will be truncated at RTAPI_NAME_LEN characters. If ‘modname’ is NULL, the system will assign a name. On success, returns a positive integer module ID, which is used for subsequent calls to rtapi_xxx_new, rtapi_xxx_delete, and rtapi_exit. On failure, returns an error code as defined above. Call only from within user or init/cleanup code, not from realtime tasks.
‘rtapi_free_interrupt_handler()’ removes an interrupt handler that was previously installed by rtapi_assign_interrupt_handler(). ‘irq’ is the interrupt number. Removing a realtime module without freeing any handlers it has installed will almost certainly crash the box. Returns 0 or -EINVAL. Call only from within init/cleanup code, not from realtime tasks.
‘rtapi_assign_interrupt_handler()’ is used to set up a handler for a hardware interrupt. ‘irq’ is the interrupt number, and ‘handler’ is a pointer to a function taking no arguments and returning void. ’handler will be called when the interrupt occurs. ‘owner’ is the ID of the calling module (see rtapi_init). Returns a status code. Note: The simulated RTOS does not support interrupts. Call only from within init/cleanup code, not from realtime tasks.
’rtapi_outb() writes ‘byte’ to ‘port’. May be called from init/cleanup code, and from within realtime tasks. Note: This function does nothing on the simulated RTOS. Note: Many platforms provide an inline outb() that is faster.
‘rtapi_print()’ prints a printf style message. Depending on the RTOS and whether the program is being compiled for user space or realtime, the message may be printed to stdout, stderr, or to a kernel message log, etc. The calling syntax and format string is similar to printf except that floating point and longlongs are NOT supported in realtime and may not be supported in user space. For some RTOS’s, a 80 byte buffer is used, so the format line and arguments should not produce a line more than 80 bytes long. (The buffer is protected against overflow.) Does not block, but can take a fairly long time, depending on the format string and OS. May be called from user, init/cleanup, and realtime code.
The ‘rtapi_prio_xxxx()’ functions provide a portable way to set task priority. The mapping of actual priority to priority number depends on the RTOS. Priorities range from ‘rtapi_prio_lowest()’ to ‘rtapi_prio_highest()’, inclusive. To use this API, use one of two methods:
‘rtapi_sem_delete()’ is the counterpart to ‘rtapi_sem_new()’. It discards the semaphore associated with ‘sem_id’. Any tasks blocked on ‘sem’ will resume execution. ‘module_id’ is the ID of the calling module. Returns a status code. Call only from within init/cleanup code, not from realtime tasks.
‘rtapi_sem_give()’ unlocks a semaphore. If a higher priority task is blocked on the semaphore, the calling task will block and the higher priority task will begin to run. Returns a status code. May be called from init/cleanup code, and from within realtime tasks.
‘rtapi_sem_new()’ creates a realtime semaphore. ‘key’ identifies identifies the semaphore, and must be non-zero. All modules wishing to use the same semaphore must specify the same key. ‘module_id’ is the ID of the module making the call (see rtapi_init). On success, it returns a positive integer semaphore ID, which is used for all subsequent calls dealing with the semaphore. On failure it returns a negative error code. Call only from within init/cleanup code, not from realtime tasks.
‘rtapi_sem_take()’ locks a semaphore. Returns 0 or -EINVAL. If the semaphore is unlocked it returns 0 immediately. If the semaphore is locked, the calling task blocks until the semaphore is unlocked, then it returns 0. Call only from within a realtime task.
‘rtapi_sem_try()’ does a non-blocking attempt to lock a semaphore. Returns 0, -EINVAL, or -EBUSY. If the semaphore is unlocked, it returns 0. If the semaphore is locked it does not block, instead it returns -EBUSY, and the caller can decide how to deal with the situation. Call only from within a realtime task.
Set the maximum level of message to print. In userspace code, each component has its own independent message level. In realtime code, all components share a single message level. Returns 0 for success or -EINVAL if the level is out of range.
‘rtapi_shmem_delete()’ frees the shared memory block associated with ‘shmem_id’. ‘module_id’ is the ID of the calling module. Returns a status code. Call only from within user or init/cleanup code, not from realtime tasks.
‘rtapi_shmem_getptr()’ sets ‘*ptr’ to point to shared memory block associated with ‘shmem_id’. Returns a status code. May be called from user code, init/cleanup code, or realtime tasks.
‘rtapi_shmem_new()’ allocates a block of shared memory. ‘key’ identifies the memory block, and must be non-zero. All modules wishing to access the same memory must use the same key. ‘module_id’ is the ID of the module that is making the call (see rtapi_init). The block will be at least ‘size’ bytes, and may be rounded up. Allocating many small blocks may be very wasteful. When a particular block is allocated for the first time, all bytes are zeroed. Subsequent allocations of the same block by other modules or processes will not touch the contents of the block. On success, it returns a positive integer ID, which is used for all subsequent calls dealing with the block. On failure it returns a negative error code. Call only from within user or init/cleanup code, not from realtime tasks.
‘rtapi_snprintf()’ works like ‘snprintf()’ from the normal C library, except that it may not handle long longs. It is provided here because some RTOS kernels don’t provide a realtime safe version of the function, and those that do don’t provide support for printing doubles. On systems with a good kernel snprintf(), or in user space, this function simply calls the normal snprintf(). May be called from user, init/cleanup, and realtime code.
‘rtapi_task_delete()’ deletes a task. ‘task_id’ is a task ID from a previous call to rtapi_task_new(). It frees memory associated with ‘task’, and does any other cleanup needed. If the task has been started, you should pause it before deleting it. Returns a status code. Call only from within init/cleanup code, not from realtime tasks.
’rtapi_task_pause() causes ‘task_id’ to stop execution and change to the “paused” state. ‘task_id’ can be free-running or periodic. Note that rtapi_task_pause() may called from any task, or from init or cleanup code, not just from the task that is to be paused. The task will resume execution when either rtapi_task_resume() or rtapi_task_start() is called. May be called from init/cleanup code, and from within realtime tasks.
’rtapi_task_resume() starts a task in free-running mode. ‘task_id’ is a task ID from a call to rtapi_task_new(). The task must be in the “paused” state, or it will return -EINVAL. A free running task runs continuously until either:
‘rtapi_task_self()’ returns the task ID of the current task or -EINVAL. May be called from init/cleanup code, and from within realtime tasks.
‘rtapi_task_start()’ starts a task in periodic mode. ‘task_id’ is a task ID from a call to rtapi_task_new(). The task must be in the “paused” state, or it will return -EINVAL. ‘period_nsec’ is the task period in nanoseconds, which will be rounded to the nearest multiple of the global clock period. A task period less than the clock period (including zero) will be set equal to the clock period. Call only from within init/cleanup code, not from realtime tasks.
‘rtapi_wait()’ suspends execution of the current task until the next period. The task must be periodic, if not, the result is undefined. The function will return at the beginning of the next period. Call only from within a realtime task.

Type Definitions

HAL ‘constructor’ typedef If it is not NULL, this points to a function which can construct a new instance of its component. Return value is >=0 for success, <0 for error.
HAL parameters also have a direction attribute. For parameters, the attribute determines whether the user can write the value of the parameter, or simply read it. HAL_RO parameters are read-only, and HAL_RW ones are writable with ‘halcmd setp’.
HAL pins have a direction attribute. A pin may be an input to the HAL component, an output, or it may be bidirectional. Any number of HAL_IN or HAL_IO pins may be connected to the same signal, but only one HAL_OUT pin is permitted. This is equivalent to connecting two output pins together in an electronic circuit. (HAL_IO pins can be thought of as tri-state outputs.)
HAL pins and signals are typed, and the HAL only allows pins to be attached to signals of the same type. All HAL types can be read or written atomically. (Read-modify- write operations are not atomic.) Note that when a component reads or writes one of its pins, it is actually reading or writing the signal linked to that pin, by way of the pointer. ‘hal_type_t’ is an enum used to identify the type of a pin, signal, or parameter.
‘rtapi_print_msg()’ prints a printf-style message when the level is less than or equal to the current message level set by rtapi_set_msg_level(). May be called from user, init/cleanup, and realtime code.
‘rtapi_get_msg_handler’ and ‘rtapi_set_msg_handler’ access the function pointer used by rtapi_print and rtapi_print_msg. By default, messages appear in the kernel log, but by replacing the handler a user of the rtapi library can send the messages to another destination. Calling rtapi_set_msg_handler with NULL restores the default handler. Call from real-time init/cleanup code only. When called from rtapi_print(), ‘level’ is RTAPI_MSG_ALL, a level which should not normally be used with rtapi_print_msg().

Unions